diff options
Diffstat (limited to 'third_party/libwebrtc/logging/rtc_event_log')
125 files changed, 25682 insertions, 0 deletions
diff --git a/third_party/libwebrtc/logging/rtc_event_log/DEPS b/third_party/libwebrtc/logging/rtc_event_log/DEPS new file mode 100644 index 0000000000..fe8a9114ed --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/DEPS @@ -0,0 +1,7 @@ +include_rules = [ + "+call", + "+modules/audio_coding/audio_network_adaptor", + "+modules/congestion_controller", + "+modules/rtp_rtcp", + "+system_wrappers", +] diff --git a/third_party/libwebrtc/logging/rtc_event_log/dependency_descriptor_encoder_decoder.cc b/third_party/libwebrtc/logging/rtc_event_log/dependency_descriptor_encoder_decoder.cc new file mode 100644 index 0000000000..c74c8c4b63 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/dependency_descriptor_encoder_decoder.cc @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2023 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 "logging/rtc_event_log/dependency_descriptor_encoder_decoder.h" + +#include <string> +#include <vector> + +#include "logging/rtc_event_log/encoder/delta_encoding.h" +#include "logging/rtc_event_log/encoder/optional_blob_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" +#include "logging/rtc_event_log/rtc_event_log2_proto_include.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +// static +absl::optional<rtclog2::DependencyDescriptorsWireInfo> +RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + const std::vector<rtc::ArrayView<const uint8_t>>& raw_dd_data) { + if (raw_dd_data.empty()) { + return {}; + } + + for (const auto& dd : raw_dd_data) { + if (!dd.empty() && dd.size() < 3) { + RTC_LOG(LS_WARNING) << "DependencyDescriptor size not valid."; + return {}; + } + } + + rtclog2::DependencyDescriptorsWireInfo res; + const rtc::ArrayView<const uint8_t>& base_dd = raw_dd_data[0]; + auto delta_dds = + rtc::MakeArrayView(raw_dd_data.data(), raw_dd_data.size()).subview(1); + + // Start and end bit. + { + absl::optional<uint32_t> start_end_bit; + if (!base_dd.empty()) { + start_end_bit = (base_dd[0] >> 6); + res.set_start_end_bit(*start_end_bit); + } + if (!delta_dds.empty()) { + std::vector<absl::optional<uint64_t>> values(delta_dds.size()); + for (size_t i = 0; i < delta_dds.size(); ++i) { + if (!delta_dds[i].empty()) { + values[i] = delta_dds[i][0] >> 6; + } + } + std::string encoded_deltas = EncodeDeltas(start_end_bit, values); + if (!encoded_deltas.empty()) { + res.set_start_end_bit_deltas(encoded_deltas); + } + } + } + + // Template IDs. + { + absl::optional<uint32_t> template_id; + if (!base_dd.empty()) { + template_id = (base_dd[0] & 0b0011'1111); + res.set_template_id(*template_id); + } + + if (!delta_dds.empty()) { + std::vector<absl::optional<uint64_t>> values(delta_dds.size()); + for (size_t i = 0; i < delta_dds.size(); ++i) { + if (!delta_dds[i].empty()) { + values[i] = delta_dds[i][0] & 0b0011'1111; + } + } + std::string encoded_deltas = EncodeDeltas(template_id, values); + if (!encoded_deltas.empty()) { + res.set_template_id_deltas(encoded_deltas); + } + } + } + + // Frame IDs. + { + absl::optional<uint32_t> frame_id; + if (!base_dd.empty()) { + frame_id = (uint16_t{base_dd[1]} << 8) + base_dd[2]; + res.set_frame_id(*frame_id); + } + + if (!delta_dds.empty()) { + std::vector<absl::optional<uint64_t>> values(delta_dds.size()); + for (size_t i = 0; i < delta_dds.size(); ++i) { + if (!delta_dds[i].empty()) { + values[i] = (uint16_t{delta_dds[i][1]} << 8) + delta_dds[i][2]; + } + } + std::string encoded_deltas = EncodeDeltas(frame_id, values); + if (!encoded_deltas.empty()) { + res.set_frame_id_deltas(encoded_deltas); + } + } + } + + // Extended info + { + std::vector<absl::optional<std::string>> values(raw_dd_data.size()); + for (size_t i = 0; i < raw_dd_data.size(); ++i) { + if (raw_dd_data[i].size() > 3) { + auto extended_info = raw_dd_data[i].subview(3); + values[i] = {reinterpret_cast<const char*>(extended_info.data()), + extended_info.size()}; + } + } + + std::string encoded_blobs = EncodeOptionalBlobs(values); + if (!encoded_blobs.empty()) { + res.set_extended_infos(encoded_blobs); + } + } + + return res; +} + +// static +RtcEventLogParseStatusOr<std::vector<std::vector<uint8_t>>> +RtcEventLogDependencyDescriptorEncoderDecoder::Decode( + const rtclog2::DependencyDescriptorsWireInfo& dd_wire_info, + size_t num_packets) { + if (num_packets == 0) { + return {std::vector<std::vector<uint8_t>>()}; + } + + std::vector<std::vector<uint8_t>> res(num_packets); + + absl::optional<uint64_t> start_end_bit_base; + if (dd_wire_info.has_start_end_bit()) { + start_end_bit_base = dd_wire_info.start_end_bit(); + } + absl::optional<uint64_t> template_id_base; + if (dd_wire_info.has_template_id()) { + template_id_base = dd_wire_info.template_id(); + } + absl::optional<uint64_t> frame_id_base; + if (dd_wire_info.has_frame_id()) { + frame_id_base = dd_wire_info.frame_id(); + } + + std::vector<absl::optional<uint64_t>> start_end_bit_deltas; + if (dd_wire_info.has_start_end_bit_deltas()) { + start_end_bit_deltas = DecodeDeltas(dd_wire_info.start_end_bit_deltas(), + start_end_bit_base, num_packets - 1); + RTC_DCHECK(start_end_bit_deltas.empty() || + start_end_bit_deltas.size() == (num_packets - 1)); + } + std::vector<absl::optional<uint64_t>> template_id_deltas; + if (dd_wire_info.has_template_id_deltas()) { + template_id_deltas = DecodeDeltas(dd_wire_info.template_id_deltas(), + template_id_base, num_packets - 1); + RTC_DCHECK(template_id_deltas.empty() || + template_id_deltas.size() == (num_packets - 1)); + } + std::vector<absl::optional<uint64_t>> frame_id_deltas; + if (dd_wire_info.has_frame_id_deltas()) { + frame_id_deltas = DecodeDeltas(dd_wire_info.frame_id_deltas(), + frame_id_base, num_packets - 1); + RTC_DCHECK(frame_id_deltas.empty() || + frame_id_deltas.size() == (num_packets - 1)); + } + std::vector<absl::optional<std::string>> extended_infos; + if (dd_wire_info.has_extended_infos()) { + extended_infos = + DecodeOptionalBlobs(dd_wire_info.extended_infos(), num_packets); + } + + auto recreate_raw_dd = [&](int i, const absl::optional<uint64_t>& be, + const absl::optional<uint64_t>& tid, + const absl::optional<uint64_t>& fid) { + absl::string_view ext; + if (!extended_infos.empty() && extended_infos[i].has_value()) { + ext = *extended_infos[i]; + } + if (be.has_value() && tid.has_value() && fid.has_value()) { + res[i].reserve(3 + ext.size()); + res[i].push_back((*be << 6) | *tid); + res[i].push_back(*fid >> 8); + res[i].push_back(*fid); + if (!ext.empty()) { + res[i].insert(res[i].end(), ext.begin(), ext.end()); + } + } else if (be.has_value() || tid.has_value() || fid.has_value()) { + RTC_PARSE_RETURN_ERROR("Not all required fields present."); + } else if (!ext.empty()) { + RTC_PARSE_RETURN_ERROR( + "Extended info present without required fields present."); + } + + return RtcEventLogParseStatus::Success(); + }; + + RTC_RETURN_IF_ERROR( + recreate_raw_dd(0, start_end_bit_base, template_id_base, frame_id_base)); + + for (size_t i = 1; i < num_packets; ++i) { + RTC_RETURN_IF_ERROR(recreate_raw_dd( + i, + start_end_bit_deltas.empty() ? start_end_bit_base + : start_end_bit_deltas[i - 1], + template_id_deltas.empty() ? template_id_base + : template_id_deltas[i - 1], + frame_id_deltas.empty() ? frame_id_base : frame_id_deltas[i - 1])); + } + + return res; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/dependency_descriptor_encoder_decoder.h b/third_party/libwebrtc/logging/rtc_event_log/dependency_descriptor_encoder_decoder.h new file mode 100644 index 0000000000..6729a38b6d --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/dependency_descriptor_encoder_decoder.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_DEPENDENCY_DESCRIPTOR_ENCODER_DECODER_H_ +#define LOGGING_RTC_EVENT_LOG_DEPENDENCY_DESCRIPTOR_ENCODER_DECODER_H_ + +#include <vector> + +#include "absl/types/optional.h" +#include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" +#include "logging/rtc_event_log/rtc_event_log2_proto_include.h" +#include "rtc_base/bitstream_reader.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +class RtcEventLogDependencyDescriptorEncoderDecoder { + public: + static absl::optional<rtclog2::DependencyDescriptorsWireInfo> Encode( + const std::vector<rtc::ArrayView<const uint8_t>>& raw_dd_data); + static RtcEventLogParseStatusOr<std::vector<std::vector<uint8_t>>> Decode( + const rtclog2::DependencyDescriptorsWireInfo& dd_wire_info, + size_t num_packets); +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_DEPENDENCY_DESCRIPTOR_ENCODER_DECODER_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/dependency_descriptor_encoder_decoder_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/dependency_descriptor_encoder_decoder_unittest.cc new file mode 100644 index 0000000000..04c7147e28 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/dependency_descriptor_encoder_decoder_unittest.cc @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2023 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 "logging/rtc_event_log/dependency_descriptor_encoder_decoder.h" + +#include <string> +#include <vector> + +#include "logging/rtc_event_log/encoder/delta_encoding.h" +#include "logging/rtc_event_log/encoder/optional_blob_encoding.h" +#include "logging/rtc_event_log/rtc_event_log2_proto_include.h" +#include "test/gmock.h" +#include "test/gtest.h" + +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::IsEmpty; + +namespace webrtc { +namespace { + +constexpr uint64_t kNone = 0; +constexpr uint64_t kEnd = 1; +constexpr uint64_t kBegin = 2; +constexpr uint64_t kBeginEnd = 3; + +class DdBuilder { + public: + DdBuilder() : raw_dd(3) {} + DdBuilder& B() { + raw_dd[0] |= 1 << 7; + return *this; + } + DdBuilder& E() { + raw_dd[0] |= 1 << 6; + return *this; + } + DdBuilder& Tid(uint8_t id) { + RTC_DCHECK_LE(id, 0x3F); + raw_dd[0] |= id; + return *this; + } + DdBuilder& Fid(uint16_t id) { + raw_dd[1] = id >> 8; + raw_dd[2] = id; + return *this; + } + DdBuilder& Ext(const std::string& ext) { + raw_dd.insert(raw_dd.end(), ext.begin(), ext.end()); + return *this; + } + std::vector<uint8_t> Build() { return raw_dd; } + + private: + std::vector<uint8_t> raw_dd; +}; + +TEST(RtcEventLogDependencyDescriptorEncoding, SinglePacket) { + auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + {DdBuilder().B().E().Tid(12).Fid(345).Ext("abc").Build()}); + EXPECT_THAT(encoded->start_end_bit(), Eq(kBeginEnd)); + EXPECT_THAT(encoded->template_id(), Eq(12u)); + EXPECT_THAT(encoded->frame_id(), Eq(345u)); + EXPECT_THAT(encoded->extended_infos(), Eq(EncodeOptionalBlobs({{"abc"}}))); + EXPECT_THAT(encoded->has_start_end_bit_deltas(), Eq(false)); + EXPECT_THAT(encoded->has_template_id_deltas(), Eq(false)); + EXPECT_THAT(encoded->has_frame_id_deltas(), Eq(false)); +} + +TEST(RtcEventLogDependencyDescriptorEncoding, MultiplePackets) { + auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + {DdBuilder().B().E().Tid(1).Fid(1).Ext("abc").Build(), + DdBuilder().B().Tid(2).Fid(3).Ext("def").Build(), + DdBuilder().E().Tid(2).Fid(3).Build()}); + + EXPECT_THAT(encoded->start_end_bit(), Eq(kBeginEnd)); + EXPECT_THAT(encoded->template_id(), Eq(1u)); + EXPECT_THAT(encoded->frame_id(), Eq(1u)); + EXPECT_THAT(encoded->extended_infos(), + Eq(EncodeOptionalBlobs({{"abc"}, {"def"}, {}}))); + EXPECT_THAT(encoded->start_end_bit_deltas(), + Eq(EncodeDeltas({kBeginEnd}, {{kBegin}, {kEnd}}))); + EXPECT_THAT(encoded->template_id_deltas(), Eq(EncodeDeltas({1}, {{2}, {2}}))); + EXPECT_THAT(encoded->frame_id_deltas(), Eq(EncodeDeltas({1}, {{3}, {3}}))); +} + +TEST(RtcEventLogDependencyDescriptorEncoding, MultiplePacketsOneFrame) { + auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + {DdBuilder().B().Tid(1).Fid(1).Ext("abc").Build(), + DdBuilder().Tid(1).Fid(1).Build(), + DdBuilder().E().Tid(1).Fid(1).Build()}); + + EXPECT_THAT(encoded->start_end_bit(), Eq(kBegin)); + EXPECT_THAT(encoded->template_id(), Eq(1u)); + EXPECT_THAT(encoded->frame_id(), Eq(1u)); + EXPECT_THAT(encoded->extended_infos(), + Eq(EncodeOptionalBlobs({{"abc"}, {}, {}}))); + EXPECT_THAT(encoded->start_end_bit_deltas(), + Eq(EncodeDeltas({kBegin}, {{kNone}, {kEnd}}))); + EXPECT_THAT(encoded->has_template_id_deltas(), Eq(false)); + EXPECT_THAT(encoded->has_frame_id_deltas(), Eq(false)); +} + +TEST(RtcEventLogDependencyDescriptorEncoding, FirstPacketMissingDd) { + auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + {{}, + DdBuilder().B().E().Tid(1).Fid(1).Build(), + DdBuilder().E().Tid(2).Fid(3).Ext("three!").Build()}); + + EXPECT_THAT(encoded->has_start_end_bit(), Eq(false)); + EXPECT_THAT(encoded->has_template_id(), Eq(false)); + EXPECT_THAT(encoded->has_frame_id(), Eq(false)); + + EXPECT_THAT(encoded->extended_infos(), + Eq(EncodeOptionalBlobs({{}, {}, {"three!"}}))); + EXPECT_THAT(encoded->start_end_bit_deltas(), + Eq(EncodeDeltas({}, {{kBeginEnd}, {kEnd}}))); + EXPECT_THAT(encoded->template_id_deltas(), Eq(EncodeDeltas({}, {{1}, {2}}))); + EXPECT_THAT(encoded->frame_id_deltas(), Eq(EncodeDeltas({}, {{1}, {3}}))); +} + +TEST(RtcEventLogDependencyDescriptorEncoding, SomePacketMissingDd) { + auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + {DdBuilder().B().E().Tid(1).Fid(1).Build(), + {}, + DdBuilder().E().Tid(2).Fid(3).Build()}); + + EXPECT_THAT(encoded->start_end_bit(), Eq(kBeginEnd)); + EXPECT_THAT(encoded->template_id(), Eq(1u)); + EXPECT_THAT(encoded->frame_id(), Eq(1u)); + EXPECT_THAT(encoded->extended_infos(), Eq(EncodeOptionalBlobs({{}, {}, {}}))); + EXPECT_THAT(encoded->start_end_bit_deltas(), + Eq(EncodeDeltas({kBeginEnd}, {{}, {kEnd}}))); + EXPECT_THAT(encoded->template_id_deltas(), Eq(EncodeDeltas({1}, {{}, {2}}))); + EXPECT_THAT(encoded->frame_id_deltas(), Eq(EncodeDeltas({1}, {{}, {3}}))); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, SinglePacket) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_start_end_bit(kBeginEnd); + encoded.set_template_id(1); + encoded.set_frame_id(2); + encoded.set_extended_infos(EncodeOptionalBlobs({"abc"})); + + auto decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 1); + + auto expected = DdBuilder().B().E().Tid(1).Fid(2).Ext("abc").Build(); + ASSERT_THAT(decoded.ok(), Eq(true)); + EXPECT_THAT(decoded.value(), ElementsAre(expected)); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, MultiplePackets) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_start_end_bit(kBeginEnd); + encoded.set_template_id(1); + encoded.set_frame_id(1); + encoded.set_extended_infos(EncodeOptionalBlobs({{"abc"}, {"def"}, {}})); + encoded.set_start_end_bit_deltas( + EncodeDeltas({kBeginEnd}, {{kBegin}, {kEnd}})); + encoded.set_template_id_deltas(EncodeDeltas({1}, {{2}, {2}})); + encoded.set_frame_id_deltas(EncodeDeltas({1}, {{3}, {3}})); + + auto decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 3); + + auto first_expected = DdBuilder().B().E().Tid(1).Fid(1).Ext("abc").Build(); + auto second_expected = DdBuilder().B().Tid(2).Fid(3).Ext("def").Build(); + auto third_expected = DdBuilder().E().Tid(2).Fid(3).Build(); + ASSERT_THAT(decoded.ok(), Eq(true)); + EXPECT_THAT(decoded.value(), + ElementsAre(first_expected, second_expected, third_expected)); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, MultiplePacketsOneFrame) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_start_end_bit(kBegin); + encoded.set_template_id(1u); + encoded.set_frame_id(1u); + encoded.set_extended_infos(EncodeOptionalBlobs({{"abc"}, {}, {}})); + encoded.set_start_end_bit_deltas(EncodeDeltas({kBegin}, {{kNone}, {kEnd}})); + + auto decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 3); + + auto first_expected = DdBuilder().B().Tid(1).Fid(1).Ext("abc").Build(); + auto second_expected = DdBuilder().Tid(1).Fid(1).Build(); + auto third_expected = DdBuilder().E().Tid(1).Fid(1).Build(); + ASSERT_THAT(decoded.ok(), Eq(true)); + EXPECT_THAT(decoded.value(), + ElementsAre(first_expected, second_expected, third_expected)); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, FirstPacketMissingDd) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_extended_infos(EncodeOptionalBlobs({{}, {}, {"three!"}})); + encoded.set_start_end_bit_deltas(EncodeDeltas({}, {{kBeginEnd}, {kEnd}})); + encoded.set_template_id_deltas(EncodeDeltas({}, {{1}, {2}})); + encoded.set_frame_id_deltas(EncodeDeltas({}, {{1}, {3}})); + + auto decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 3); + + auto second_expected = DdBuilder().B().E().Tid(1).Fid(1).Build(); + auto third_expected = DdBuilder().E().Tid(2).Fid(3).Ext("three!").Build(); + ASSERT_THAT(decoded.ok(), Eq(true)); + EXPECT_THAT(decoded.value(), + ElementsAre(IsEmpty(), second_expected, third_expected)); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, SomePacketMissingDd) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_start_end_bit(kBeginEnd); + encoded.set_template_id(1u); + encoded.set_frame_id(1u); + encoded.set_extended_infos(EncodeOptionalBlobs({{}, {}, {}})); + encoded.set_start_end_bit_deltas(EncodeDeltas({kBeginEnd}, {{}, {kEnd}})); + encoded.set_template_id_deltas(EncodeDeltas({1}, {{}, {2}})); + encoded.set_frame_id_deltas(EncodeDeltas({1}, {{}, {3}})); + + auto decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 3); + + auto first_expected = DdBuilder().B().E().Tid(1).Fid(1).Build(); + auto third_expected = DdBuilder().E().Tid(2).Fid(3).Build(); + ASSERT_THAT(decoded.ok(), Eq(true)); + EXPECT_THAT(decoded.value(), + ElementsAre(first_expected, IsEmpty(), third_expected)); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, ExtendedWithoutRequiredFields) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_start_end_bit_deltas(EncodeDeltas({}, {{kNone}})); + encoded.set_template_id_deltas(EncodeDeltas({}, {{1}})); + encoded.set_frame_id_deltas(EncodeDeltas({}, {{1}})); + encoded.set_extended_infos(EncodeOptionalBlobs({{"Should not be here"}, {}})); + + auto decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 2); + + ASSERT_THAT(decoded.ok(), Eq(false)); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, MissingRequiredField) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_start_end_bit(kBeginEnd); + encoded.set_template_id(1u); + encoded.set_frame_id(1u); + + { + rtclog2::DependencyDescriptorsWireInfo missing = encoded; + missing.clear_start_end_bit(); + EXPECT_THAT( + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(missing, 1).ok(), + Eq(false)); + } + + { + rtclog2::DependencyDescriptorsWireInfo missing = encoded; + missing.clear_template_id(); + EXPECT_THAT( + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(missing, 1).ok(), + Eq(false)); + } + + { + rtclog2::DependencyDescriptorsWireInfo missing = encoded; + missing.clear_frame_id(); + EXPECT_THAT( + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(missing, 1).ok(), + Eq(false)); + } +} + +} // namespace +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/bit_writer.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/bit_writer.cc new file mode 100644 index 0000000000..e8748d3db3 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/bit_writer.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 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 "logging/rtc_event_log/encoder/bit_writer.h" + +namespace webrtc { + +namespace { +size_t BitsToBytes(size_t bits) { + return (bits / 8) + (bits % 8 > 0 ? 1 : 0); +} +} // namespace + +void BitWriter::WriteBits(uint64_t val, size_t bit_count) { + RTC_DCHECK(valid_); + const bool success = bit_writer_.WriteBits(val, bit_count); + RTC_DCHECK(success); + written_bits_ += bit_count; +} + +void BitWriter::WriteBits(absl::string_view input) { + RTC_DCHECK(valid_); + for (char c : input) { + WriteBits(static_cast<unsigned char>(c), CHAR_BIT); + } +} + +// Returns everything that was written so far. +// Nothing more may be written after this is called. +std::string BitWriter::GetString() { + RTC_DCHECK(valid_); + valid_ = false; + + buffer_.resize(BitsToBytes(written_bits_)); + written_bits_ = 0; + + std::string result; + std::swap(buffer_, result); + return result; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/bit_writer.h b/third_party/libwebrtc/logging/rtc_event_log/encoder/bit_writer.h new file mode 100644 index 0000000000..421e7c4370 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/bit_writer.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_ENCODER_BIT_WRITER_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_BIT_WRITER_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <string> +#include <utility> + +#include "absl/strings/string_view.h" +#include "rtc_base/bit_buffer.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Wrap BitBufferWriter and extend its functionality by (1) keeping track of +// the number of bits written and (2) owning its buffer. +class BitWriter final { + public: + explicit BitWriter(size_t byte_count) + : buffer_(byte_count, '\0'), + bit_writer_(reinterpret_cast<uint8_t*>(&buffer_[0]), buffer_.size()), + written_bits_(0), + valid_(true) { + RTC_DCHECK_GT(byte_count, 0); + } + + BitWriter(const BitWriter&) = delete; + BitWriter& operator=(const BitWriter&) = delete; + + void WriteBits(uint64_t val, size_t bit_count); + + void WriteBits(absl::string_view input); + + // Returns everything that was written so far. + // Nothing more may be written after this is called. + std::string GetString(); + + private: + std::string buffer_; + rtc::BitBufferWriter bit_writer_; + // Note: Counting bits instead of bytes wraps around earlier than it has to, + // which means the maximum length is lower than it could be. We don't expect + // to go anywhere near the limit, though, so this is good enough. + size_t written_bits_; + bool valid_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_BIT_WRITER_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/blob_encoding.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/blob_encoding.cc new file mode 100644 index 0000000000..96699dc96a --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/blob_encoding.cc @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2018 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 "logging/rtc_event_log/encoder/blob_encoding.h" + +#include <cstdint> + +#include "logging/rtc_event_log/encoder/var_int.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +std::string EncodeBlobs(const std::vector<std::string>& blobs) { + RTC_DCHECK(!blobs.empty()); + + size_t result_length_bound = kMaxVarIntLengthBytes * blobs.size(); + for (const auto& blob : blobs) { + // Providing an input so long that it would cause a wrap-around is an error. + RTC_DCHECK_GE(result_length_bound + blob.length(), result_length_bound); + result_length_bound += blob.length(); + } + + std::string result; + result.reserve(result_length_bound); + + // First, encode all of the lengths. + for (absl::string_view blob : blobs) { + result += EncodeVarInt(blob.length()); + } + + // Second, encode the actual blobs. + for (absl::string_view blob : blobs) { + result.append(blob.data(), blob.length()); + } + + RTC_DCHECK_LE(result.size(), result_length_bound); + return result; +} + +std::vector<absl::string_view> DecodeBlobs(absl::string_view encoded_blobs, + size_t num_of_blobs) { + if (encoded_blobs.empty()) { + RTC_LOG(LS_WARNING) << "Corrupt input; empty input."; + return std::vector<absl::string_view>(); + } + + if (num_of_blobs == 0u) { + RTC_LOG(LS_WARNING) + << "Corrupt input; number of blobs must be greater than 0."; + return std::vector<absl::string_view>(); + } + + // Read the lengths of all blobs. + std::vector<uint64_t> lengths(num_of_blobs); + for (size_t i = 0; i < num_of_blobs; ++i) { + bool success = false; + std::tie(success, encoded_blobs) = DecodeVarInt(encoded_blobs, &lengths[i]); + if (!success) { + RTC_LOG(LS_WARNING) << "Corrupt input; varint decoding failed."; + return std::vector<absl::string_view>(); + } + } + + // Read the blobs themselves. + std::vector<absl::string_view> blobs(num_of_blobs); + for (size_t i = 0; i < num_of_blobs; ++i) { + if (lengths[i] > encoded_blobs.length()) { + RTC_LOG(LS_WARNING) << "Corrupt input; blob sizes exceed input size."; + return std::vector<absl::string_view>(); + } + + blobs[i] = encoded_blobs.substr(0, lengths[i]); + encoded_blobs = encoded_blobs.substr(lengths[i]); + } + + if (!encoded_blobs.empty()) { + RTC_LOG(LS_WARNING) << "Corrupt input; unrecognized trailer."; + return std::vector<absl::string_view>(); + } + + return blobs; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/blob_encoding.h b/third_party/libwebrtc/logging/rtc_event_log/encoder/blob_encoding.h new file mode 100644 index 0000000000..123fffe8e8 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/blob_encoding.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_ENCODER_BLOB_ENCODING_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_BLOB_ENCODING_H_ + +#include <stddef.h> + +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" + +namespace webrtc { + +// Encode/decode a sequence of strings, whose length is not known to be +// discernable from the blob itself (i.e. without being transmitted OOB), +// in a way that would allow us to separate them again on the decoding side. +// The number of blobs is assumed to be transmitted OOB. For example, if +// multiple sequences of different blobs are sent, but all sequences contain +// the same number of blobs, it is beneficial to not encode the number of blobs. +// +// EncodeBlobs() must be given a non-empty vector. The blobs themselves may +// be equal to "", though. +// EncodeBlobs() may not fail. +// EncodeBlobs() never returns the empty string. +// +// Calling DecodeBlobs() on an empty string, or with `num_of_blobs` set to 0, +// is an error. +// DecodeBlobs() returns an empty vector if it fails, e.g. due to a mismatch +// between `num_of_blobs` and `encoded_blobs`, which can happen if +// `encoded_blobs` is corrupted. +// When successful, DecodeBlobs() returns a vector of string_view objects, +// which refer to the original input (`encoded_blobs`), and therefore may +// not outlive it. +// +// Note that the returned std::string might have been reserved for significantly +// more memory than it ends up using. If the caller to EncodeBlobs() intends +// to store the result long-term, they should consider shrink_to_fit()-ing it. +std::string EncodeBlobs(const std::vector<std::string>& blobs); +std::vector<absl::string_view> DecodeBlobs(absl::string_view encoded_blobs, + size_t num_of_blobs); + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_BLOB_ENCODING_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/blob_encoding_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/blob_encoding_unittest.cc new file mode 100644 index 0000000000..a25923f22d --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/blob_encoding_unittest.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018 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 "logging/rtc_event_log/encoder/blob_encoding.h" + +#include <string> +#include <vector> + +#include "logging/rtc_event_log/encoder/var_int.h" +#include "rtc_base/checks.h" +#include "test/gtest.h" + +using CharT = std::string::value_type; + +namespace webrtc { + +namespace { + +void TestEncodingAndDecoding(const std::vector<std::string>& blobs) { + RTC_DCHECK(!blobs.empty()); + + const std::string encoded = EncodeBlobs(blobs); + ASSERT_FALSE(encoded.empty()); + + const std::vector<absl::string_view> decoded = + DecodeBlobs(encoded, blobs.size()); + + ASSERT_EQ(decoded.size(), blobs.size()); + for (size_t i = 0; i < decoded.size(); ++i) { + ASSERT_EQ(decoded[i], blobs[i]); + } +} + +void TestGracefulErrorHandling(absl::string_view encoded_blobs, + size_t num_of_blobs) { + const std::vector<absl::string_view> decoded = + DecodeBlobs(encoded_blobs, num_of_blobs); + EXPECT_TRUE(decoded.empty()); +} + +} // namespace + +TEST(BlobEncoding, EmptyBlob) { + TestEncodingAndDecoding({""}); +} + +TEST(BlobEncoding, SingleCharacterBlob) { + TestEncodingAndDecoding({"a"}); +} + +TEST(BlobEncoding, LongBlob) { + std::string blob = ""; + for (size_t i = 0; i < 100000; ++i) { + blob += std::to_string(i + 1) + " Mississippi\n"; + } + TestEncodingAndDecoding({blob}); +} + +TEST(BlobEncoding, BlobsOfVariousLengths) { + constexpr size_t kJump = 0xf032d; // Arbitrary. + constexpr size_t kMax = 0xffffff; // Arbitrary. + + std::string blob; + blob.reserve(kMax); + + for (size_t i = 0; i < kMax; i += kJump) { + blob.append(kJump, 'x'); + TestEncodingAndDecoding({blob}); + } +} + +TEST(BlobEncoding, MultipleBlobs) { + std::vector<std::string> blobs; + for (size_t i = 0; i < 100000; ++i) { + blobs.push_back(std::to_string(i + 1) + " Mississippi\n"); + } + TestEncodingAndDecoding(blobs); +} + +TEST(BlobEncoding, DecodeBlobsHandlesErrorsGracefullyEmptyInput) { + TestGracefulErrorHandling("", 1); +} + +TEST(BlobEncoding, DecodeBlobsHandlesErrorsGracefullyZeroBlobs) { + const std::string encoded = EncodeBlobs({"a"}); + ASSERT_FALSE(encoded.empty()); + TestGracefulErrorHandling(encoded, 0); +} + +TEST(BlobEncoding, DecodeBlobsHandlesErrorsGracefullyBlobLengthTooSmall) { + std::string encoded = EncodeBlobs({"ab"}); + ASSERT_FALSE(encoded.empty()); + ASSERT_EQ(encoded[0], 0x02); + encoded[0] = 0x01; + TestGracefulErrorHandling(encoded, 1); +} + +TEST(BlobEncoding, DecodeBlobsHandlesErrorsGracefullyBlobLengthTooLarge) { + std::string encoded = EncodeBlobs({"a"}); + ASSERT_FALSE(encoded.empty()); + ASSERT_EQ(encoded[0], 0x01); + encoded[0] = 0x02; + TestGracefulErrorHandling(encoded, 1); +} + +TEST(BlobEncoding, + DecodeBlobsHandlesErrorsGracefullyNumberOfBlobsIncorrectlyHigh) { + const std::vector<std::string> blobs = {"a", "b"}; + const std::string encoded = EncodeBlobs(blobs); + // Test focus - two empty strings encoded, but DecodeBlobs() told way more + // blobs are in the strings than could be expected. + TestGracefulErrorHandling(encoded, 1000); + + // Test sanity - show that DecodeBlobs() would have worked if it got the + // correct input. + TestEncodingAndDecoding(blobs); +} + +TEST(BlobEncoding, DecodeBlobsHandlesErrorsGracefullyDefectiveVarInt) { + std::string defective_varint; + for (size_t i = 0; i < kMaxVarIntLengthBytes; ++i) { + ASSERT_LE(kMaxVarIntLengthBytes, 0xffu); + defective_varint += static_cast<CharT>(static_cast<size_t>(0x80u) | i); + } + defective_varint += 0x01u; + + const std::string defective_encoded = defective_varint + "whatever"; + + TestGracefulErrorHandling(defective_encoded, 1); +} + +TEST(BlobEncoding, DecodeBlobsHandlesErrorsGracefullyLengthSumWrapAround) { + std::string max_size_varint; + for (size_t i = 0; i < kMaxVarIntLengthBytes - 1; ++i) { + max_size_varint += 0xffu; + } + max_size_varint += 0x7fu; + + const std::string defective_encoded = + max_size_varint + max_size_varint + "whatever"; + + TestGracefulErrorHandling(defective_encoded, 2); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/delta_encoding.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/delta_encoding.cc new file mode 100644 index 0000000000..c80424574c --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/delta_encoding.cc @@ -0,0 +1,839 @@ +/* + * Copyright (c) 2018 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 "logging/rtc_event_log/encoder/delta_encoding.h" + +#include <algorithm> +#include <limits> +#include <memory> +#include <utility> + +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "logging/rtc_event_log/encoder/bit_writer.h" +#include "logging/rtc_event_log/encoder/var_int.h" +#include "rtc_base/bit_buffer.h" +#include "rtc_base/bitstream_reader.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { +namespace { + +// TODO(eladalon): Only build the decoder in tools and unit tests. + +bool g_force_unsigned_for_testing = false; +bool g_force_signed_for_testing = false; + +size_t BitsToBytes(size_t bits) { + return (bits / 8) + (bits % 8 > 0 ? 1 : 0); +} + +// TODO(eladalon): Replace by something more efficient. +uint64_t UnsignedBitWidth(uint64_t input, bool zero_val_as_zero_width = false) { + if (zero_val_as_zero_width && input == 0) { + return 0; + } + + uint64_t width = 0; + do { // input == 0 -> width == 1 + width += 1; + input >>= 1; + } while (input != 0); + return width; +} + +uint64_t SignedBitWidth(uint64_t max_pos_magnitude, + uint64_t max_neg_magnitude) { + const uint64_t bitwidth_pos = UnsignedBitWidth(max_pos_magnitude, true); + const uint64_t bitwidth_neg = + (max_neg_magnitude > 0) ? UnsignedBitWidth(max_neg_magnitude - 1, true) + : 0; + return 1 + std::max(bitwidth_pos, bitwidth_neg); +} + +// Return the maximum integer of a given bit width. +// Examples: +// MaxUnsignedValueOfBitWidth(1) = 0x01 +// MaxUnsignedValueOfBitWidth(6) = 0x3f +// MaxUnsignedValueOfBitWidth(8) = 0xff +// MaxUnsignedValueOfBitWidth(32) = 0xffffffff +uint64_t MaxUnsignedValueOfBitWidth(uint64_t bit_width) { + RTC_DCHECK_GE(bit_width, 1); + RTC_DCHECK_LE(bit_width, 64); + return (bit_width == 64) ? std::numeric_limits<uint64_t>::max() + : ((static_cast<uint64_t>(1) << bit_width) - 1); +} + +// Computes the delta between `previous` and `current`, under the assumption +// that wrap-around occurs after `width` is exceeded. +uint64_t UnsignedDelta(uint64_t previous, uint64_t current, uint64_t bit_mask) { + return (current - previous) & bit_mask; +} + +// Determines the encoding type (e.g. fixed-size encoding). +// Given an encoding type, may also distinguish between some variants of it +// (e.g. which fields of the fixed-size encoding are explicitly mentioned by +// the header, and which are implicitly assumed to hold certain default values). +enum class EncodingType { + kFixedSizeUnsignedDeltasNoEarlyWrapNoOpt = 0, + kFixedSizeSignedDeltasEarlyWrapAndOptSupported = 1, + kReserved1 = 2, + kReserved2 = 3, + kNumberOfEncodingTypes // Keep last +}; + +// The width of each field in the encoding header. Note that this is the +// width in case the field exists; not all fields occur in all encoding types. +constexpr size_t kBitsInHeaderForEncodingType = 2; +constexpr size_t kBitsInHeaderForDeltaWidthBits = 6; +constexpr size_t kBitsInHeaderForSignedDeltas = 1; +constexpr size_t kBitsInHeaderForValuesOptional = 1; +constexpr size_t kBitsInHeaderForValueWidthBits = 6; + +static_assert(static_cast<size_t>(EncodingType::kNumberOfEncodingTypes) <= + 1 << kBitsInHeaderForEncodingType, + "Not all encoding types fit."); + +// Default values for when the encoding header does not specify explicitly. +constexpr bool kDefaultSignedDeltas = false; +constexpr bool kDefaultValuesOptional = false; +constexpr uint64_t kDefaultValueWidthBits = 64; + +// Parameters for fixed-size delta-encoding/decoding. +// These are tailored for the sequence which will be encoded (e.g. widths). +class FixedLengthEncodingParameters final { + public: + static bool ValidParameters(uint64_t delta_width_bits, + bool signed_deltas, + bool values_optional, + uint64_t value_width_bits) { + return (1 <= delta_width_bits && delta_width_bits <= 64 && + 1 <= value_width_bits && value_width_bits <= 64 && + delta_width_bits <= value_width_bits); + } + + FixedLengthEncodingParameters(uint64_t delta_width_bits, + bool signed_deltas, + bool values_optional, + uint64_t value_width_bits) + : delta_width_bits_(delta_width_bits), + signed_deltas_(signed_deltas), + values_optional_(values_optional), + value_width_bits_(value_width_bits), + delta_mask_(MaxUnsignedValueOfBitWidth(delta_width_bits_)), + value_mask_(MaxUnsignedValueOfBitWidth(value_width_bits_)) { + RTC_DCHECK(ValidParameters(delta_width_bits, signed_deltas, values_optional, + value_width_bits)); + } + + // Number of bits necessary to hold the widest(*) of the deltas between the + // values in the sequence. + // (*) - Widest might not be the largest, if signed deltas are used. + uint64_t delta_width_bits() const { return delta_width_bits_; } + + // Whether deltas are signed. + bool signed_deltas() const { return signed_deltas_; } + + // Whether the values of the sequence are optional. That is, it may be + // that some of them do not have a value (not even a sentinel value indicating + // invalidity). + bool values_optional() const { return values_optional_; } + + // Number of bits necessary to hold the largest value in the sequence. + uint64_t value_width_bits() const { return value_width_bits_; } + + // Masks where only the bits relevant to the deltas/values are turned on. + uint64_t delta_mask() const { return delta_mask_; } + uint64_t value_mask() const { return value_mask_; } + + void SetSignedDeltas(bool signed_deltas) { signed_deltas_ = signed_deltas; } + void SetDeltaWidthBits(uint64_t delta_width_bits) { + delta_width_bits_ = delta_width_bits; + delta_mask_ = MaxUnsignedValueOfBitWidth(delta_width_bits); + } + + private: + uint64_t delta_width_bits_; // Normally const, but mutable in tests. + bool signed_deltas_; // Normally const, but mutable in tests. + const bool values_optional_; + const uint64_t value_width_bits_; + + uint64_t delta_mask_; // Normally const, but mutable in tests. + const uint64_t value_mask_; +}; + +// Performs delta-encoding of a single (non-empty) sequence of values, using +// an encoding where all deltas are encoded using the same number of bits. +// (With the exception of optional elements; those are encoded as a bit vector +// with one bit per element, plus a fixed number of bits for every element that +// has a value.) +class FixedLengthDeltaEncoder final { + public: + // See webrtc::EncodeDeltas() for general details. + // This function return a bit pattern that would allow the decoder to + // determine whether it was produced by FixedLengthDeltaEncoder, and can + // therefore be decoded by FixedLengthDeltaDecoder, or whether it was produced + // by a different encoder. + static std::string EncodeDeltas( + absl::optional<uint64_t> base, + const std::vector<absl::optional<uint64_t>>& values); + + FixedLengthDeltaEncoder(const FixedLengthDeltaEncoder&) = delete; + FixedLengthDeltaEncoder& operator=(const FixedLengthDeltaEncoder&) = delete; + + private: + // Calculate min/max values of unsigned/signed deltas, given the bit width + // of all the values in the series. + static void CalculateMinAndMaxDeltas( + absl::optional<uint64_t> base, + const std::vector<absl::optional<uint64_t>>& values, + uint64_t bit_width, + uint64_t* max_unsigned_delta, + uint64_t* max_pos_signed_delta, + uint64_t* min_neg_signed_delta); + + // No effect outside of unit tests. + // In unit tests, may lead to forcing signed/unsigned deltas, etc. + static void ConsiderTestOverrides(FixedLengthEncodingParameters* params, + uint64_t delta_width_bits_signed, + uint64_t delta_width_bits_unsigned); + + // FixedLengthDeltaEncoder objects are to be created by EncodeDeltas() and + // released by it before it returns. They're mostly a convenient way to + // avoid having to pass a lot of state between different functions. + // Therefore, it was deemed acceptable to let them have a reference to + // `values`, whose lifetime must exceed the lifetime of `this`. + FixedLengthDeltaEncoder(const FixedLengthEncodingParameters& params, + absl::optional<uint64_t> base, + const std::vector<absl::optional<uint64_t>>& values, + size_t existent_values_count); + + // Perform delta-encoding using the parameters given to the ctor on the + // sequence of values given to the ctor. + std::string Encode(); + + // Exact lengths. + size_t OutputLengthBytes(size_t existent_values_count) const; + size_t HeaderLengthBits() const; + size_t EncodedDeltasLengthBits(size_t existent_values_count) const; + + // Encode the compression parameters into the stream. + void EncodeHeader(); + + // Encode a given delta into the stream. + void EncodeDelta(uint64_t previous, uint64_t current); + void EncodeUnsignedDelta(uint64_t previous, uint64_t current); + void EncodeSignedDelta(uint64_t previous, uint64_t current); + + // The parameters according to which encoding will be done (width of + // fields, whether signed deltas should be used, etc.) + const FixedLengthEncodingParameters params_; + + // The encoding scheme assumes that at least one value is transmitted OOB, + // so that the first value can be encoded as a delta from that OOB value, + // which is `base_`. + const absl::optional<uint64_t> base_; + + // The values to be encoded. + // Note: This is a non-owning reference. See comment above ctor for details. + const std::vector<absl::optional<uint64_t>>& values_; + + // Buffer into which encoded values will be written. + // This is created dynmically as a way to enforce that the rest of the + // ctor has finished running when this is constructed, so that the lower + // bound on the buffer size would be guaranteed correct. + std::unique_ptr<BitWriter> writer_; +}; + +// TODO(eladalon): Reduce the number of passes. +std::string FixedLengthDeltaEncoder::EncodeDeltas( + absl::optional<uint64_t> base, + const std::vector<absl::optional<uint64_t>>& values) { + RTC_DCHECK(!values.empty()); + + // As a special case, if all of the elements are identical to the base, + // (including, for optional fields, about their existence/non-existence), + // the empty string is used to signal that. + if (std::all_of( + values.cbegin(), values.cend(), + [base](absl::optional<uint64_t> val) { return val == base; })) { + return std::string(); + } + + bool non_decreasing = true; + uint64_t max_value_including_base = base.value_or(0u); + size_t existent_values_count = 0; + { + uint64_t previous = base.value_or(0u); + for (size_t i = 0; i < values.size(); ++i) { + if (!values[i].has_value()) { + continue; + } + ++existent_values_count; + non_decreasing &= (previous <= values[i].value()); + max_value_including_base = + std::max(max_value_including_base, values[i].value()); + previous = values[i].value(); + } + } + + // If the sequence is non-decreasing, it may be assumed to have width = 64; + // there's no reason to encode the actual max width in the encoding header. + const uint64_t value_width_bits = + non_decreasing ? 64 : UnsignedBitWidth(max_value_including_base); + + uint64_t max_unsigned_delta; + uint64_t max_pos_signed_delta; + uint64_t min_neg_signed_delta; + CalculateMinAndMaxDeltas(base, values, value_width_bits, &max_unsigned_delta, + &max_pos_signed_delta, &min_neg_signed_delta); + + const uint64_t delta_width_bits_unsigned = + UnsignedBitWidth(max_unsigned_delta); + const uint64_t delta_width_bits_signed = + SignedBitWidth(max_pos_signed_delta, min_neg_signed_delta); + + // Note: Preference for unsigned if the two have the same width (efficiency). + const bool signed_deltas = + delta_width_bits_signed < delta_width_bits_unsigned; + const uint64_t delta_width_bits = + signed_deltas ? delta_width_bits_signed : delta_width_bits_unsigned; + + const bool values_optional = + !base.has_value() || (existent_values_count < values.size()); + + FixedLengthEncodingParameters params(delta_width_bits, signed_deltas, + values_optional, value_width_bits); + + // No effect in production. + ConsiderTestOverrides(¶ms, delta_width_bits_signed, + delta_width_bits_unsigned); + + FixedLengthDeltaEncoder encoder(params, base, values, existent_values_count); + return encoder.Encode(); +} + +void FixedLengthDeltaEncoder::CalculateMinAndMaxDeltas( + absl::optional<uint64_t> base, + const std::vector<absl::optional<uint64_t>>& values, + uint64_t bit_width, + uint64_t* max_unsigned_delta_out, + uint64_t* max_pos_signed_delta_out, + uint64_t* min_neg_signed_delta_out) { + RTC_DCHECK(!values.empty()); + RTC_DCHECK(max_unsigned_delta_out); + RTC_DCHECK(max_pos_signed_delta_out); + RTC_DCHECK(min_neg_signed_delta_out); + + const uint64_t bit_mask = MaxUnsignedValueOfBitWidth(bit_width); + + uint64_t max_unsigned_delta = 0; + uint64_t max_pos_signed_delta = 0; + uint64_t min_neg_signed_delta = 0; + + absl::optional<uint64_t> prev = base; + for (size_t i = 0; i < values.size(); ++i) { + if (!values[i].has_value()) { + continue; + } + + if (!prev.has_value()) { + // If the base is non-existent, the first existent value is encoded as + // a varint, rather than as a delta. + RTC_DCHECK(!base.has_value()); + prev = values[i]; + continue; + } + + const uint64_t current = values[i].value(); + + const uint64_t forward_delta = UnsignedDelta(*prev, current, bit_mask); + const uint64_t backward_delta = UnsignedDelta(current, *prev, bit_mask); + + max_unsigned_delta = std::max(max_unsigned_delta, forward_delta); + + if (forward_delta < backward_delta) { + max_pos_signed_delta = std::max(max_pos_signed_delta, forward_delta); + } else { + min_neg_signed_delta = std::max(min_neg_signed_delta, backward_delta); + } + + prev = current; + } + + *max_unsigned_delta_out = max_unsigned_delta; + *max_pos_signed_delta_out = max_pos_signed_delta; + *min_neg_signed_delta_out = min_neg_signed_delta; +} + +void FixedLengthDeltaEncoder::ConsiderTestOverrides( + FixedLengthEncodingParameters* params, + uint64_t delta_width_bits_signed, + uint64_t delta_width_bits_unsigned) { + if (g_force_unsigned_for_testing) { + params->SetDeltaWidthBits(delta_width_bits_unsigned); + params->SetSignedDeltas(false); + } else if (g_force_signed_for_testing) { + params->SetDeltaWidthBits(delta_width_bits_signed); + params->SetSignedDeltas(true); + } else { + // Unchanged. + } +} + +FixedLengthDeltaEncoder::FixedLengthDeltaEncoder( + const FixedLengthEncodingParameters& params, + absl::optional<uint64_t> base, + const std::vector<absl::optional<uint64_t>>& values, + size_t existent_values_count) + : params_(params), base_(base), values_(values) { + RTC_DCHECK(!values_.empty()); + writer_ = + std::make_unique<BitWriter>(OutputLengthBytes(existent_values_count)); +} + +std::string FixedLengthDeltaEncoder::Encode() { + EncodeHeader(); + + if (params_.values_optional()) { + // Encode which values exist and which don't. + for (absl::optional<uint64_t> value : values_) { + writer_->WriteBits(value.has_value() ? 1u : 0u, 1); + } + } + + absl::optional<uint64_t> previous = base_; + for (absl::optional<uint64_t> value : values_) { + if (!value.has_value()) { + RTC_DCHECK(params_.values_optional()); + continue; + } + + if (!previous.has_value()) { + // If the base is non-existent, the first existent value is encoded as + // a varint, rather than as a delta. + RTC_DCHECK(!base_.has_value()); + writer_->WriteBits(EncodeVarInt(value.value())); + } else { + EncodeDelta(previous.value(), value.value()); + } + + previous = value; + } + + return writer_->GetString(); +} + +size_t FixedLengthDeltaEncoder::OutputLengthBytes( + size_t existent_values_count) const { + return BitsToBytes(HeaderLengthBits() + + EncodedDeltasLengthBits(existent_values_count)); +} + +size_t FixedLengthDeltaEncoder::HeaderLengthBits() const { + if (params_.signed_deltas() == kDefaultSignedDeltas && + params_.values_optional() == kDefaultValuesOptional && + params_.value_width_bits() == kDefaultValueWidthBits) { + return kBitsInHeaderForEncodingType + kBitsInHeaderForDeltaWidthBits; + } else { + return kBitsInHeaderForEncodingType + kBitsInHeaderForDeltaWidthBits + + kBitsInHeaderForSignedDeltas + kBitsInHeaderForValuesOptional + + kBitsInHeaderForValueWidthBits; + } +} + +size_t FixedLengthDeltaEncoder::EncodedDeltasLengthBits( + size_t existent_values_count) const { + if (!params_.values_optional()) { + return values_.size() * params_.delta_width_bits(); + } else { + RTC_DCHECK_EQ(std::count_if(values_.begin(), values_.end(), + [](absl::optional<uint64_t> val) { + return val.has_value(); + }), + existent_values_count); + // One bit for each delta, to indicate if the value exists, and delta_width + // for each existent value, to indicate the delta itself. + // If base_ is non-existent, the first value (if any) is encoded as a varint + // rather than as a delta. + const size_t existence_bitmap_size_bits = 1 * values_.size(); + const bool first_value_is_varint = + !base_.has_value() && existent_values_count >= 1; + const size_t first_value_varint_size_bits = 8 * kMaxVarIntLengthBytes; + const size_t deltas_count = existent_values_count - first_value_is_varint; + const size_t deltas_size_bits = deltas_count * params_.delta_width_bits(); + return existence_bitmap_size_bits + first_value_varint_size_bits + + deltas_size_bits; + } +} + +void FixedLengthDeltaEncoder::EncodeHeader() { + RTC_DCHECK(writer_); + + const EncodingType encoding_type = + (params_.value_width_bits() == kDefaultValueWidthBits && + params_.signed_deltas() == kDefaultSignedDeltas && + params_.values_optional() == kDefaultValuesOptional) + ? EncodingType::kFixedSizeUnsignedDeltasNoEarlyWrapNoOpt + : EncodingType::kFixedSizeSignedDeltasEarlyWrapAndOptSupported; + + writer_->WriteBits(static_cast<uint64_t>(encoding_type), + kBitsInHeaderForEncodingType); + + // Note: Since it's meaningless for a field to be of width 0, when it comes + // to fields that relate widths, we encode width 1 as 0, width 2 as 1, + + writer_->WriteBits(params_.delta_width_bits() - 1, + kBitsInHeaderForDeltaWidthBits); + + if (encoding_type == EncodingType::kFixedSizeUnsignedDeltasNoEarlyWrapNoOpt) { + return; + } + + writer_->WriteBits(static_cast<uint64_t>(params_.signed_deltas()), + kBitsInHeaderForSignedDeltas); + writer_->WriteBits(static_cast<uint64_t>(params_.values_optional()), + kBitsInHeaderForValuesOptional); + writer_->WriteBits(params_.value_width_bits() - 1, + kBitsInHeaderForValueWidthBits); +} + +void FixedLengthDeltaEncoder::EncodeDelta(uint64_t previous, uint64_t current) { + if (params_.signed_deltas()) { + EncodeSignedDelta(previous, current); + } else { + EncodeUnsignedDelta(previous, current); + } +} + +void FixedLengthDeltaEncoder::EncodeUnsignedDelta(uint64_t previous, + uint64_t current) { + RTC_DCHECK(writer_); + const uint64_t delta = UnsignedDelta(previous, current, params_.value_mask()); + writer_->WriteBits(delta, params_.delta_width_bits()); +} + +void FixedLengthDeltaEncoder::EncodeSignedDelta(uint64_t previous, + uint64_t current) { + RTC_DCHECK(writer_); + + const uint64_t forward_delta = + UnsignedDelta(previous, current, params_.value_mask()); + const uint64_t backward_delta = + UnsignedDelta(current, previous, params_.value_mask()); + + uint64_t delta; + if (forward_delta <= backward_delta) { + delta = forward_delta; + } else { + // Compute the unsigned representation of a negative delta. + // This is the two's complement representation of this negative value, + // when deltas are of width params_.delta_mask(). + RTC_DCHECK_GE(params_.delta_mask(), backward_delta); + RTC_DCHECK_LT(params_.delta_mask() - backward_delta, params_.delta_mask()); + delta = params_.delta_mask() - backward_delta + 1; + RTC_DCHECK_LE(delta, params_.delta_mask()); + } + + writer_->WriteBits(delta, params_.delta_width_bits()); +} + +// Perform decoding of a a delta-encoded stream, extracting the original +// sequence of values. +class FixedLengthDeltaDecoder final { + public: + // Checks whether FixedLengthDeltaDecoder is a suitable decoder for this + // bitstream. Note that this does NOT imply that stream is valid, and will + // be decoded successfully. It DOES imply that all other decoder classes + // will fail to decode this input, though. + static bool IsSuitableDecoderFor(absl::string_view input); + + // Assuming that `input` is the result of fixed-size delta-encoding + // that took place with the same value to `base` and over `num_of_deltas` + // original values, this will return the sequence of original values. + // If an error occurs (can happen if `input` is corrupt), an empty + // vector will be returned. + static std::vector<absl::optional<uint64_t>> DecodeDeltas( + absl::string_view input, + absl::optional<uint64_t> base, + size_t num_of_deltas); + + FixedLengthDeltaDecoder(const FixedLengthDeltaDecoder&) = delete; + FixedLengthDeltaDecoder& operator=(const FixedLengthDeltaDecoder&) = delete; + + private: + // Reads the encoding header in `input` and returns a FixedLengthDeltaDecoder + // with the corresponding configuration, that can be used to decode the + // values in `input`. + // If the encoding header is corrupt (contains an illegal configuration), + // nullptr will be returned. + // When a valid FixedLengthDeltaDecoder is returned, this does not mean that + // the entire stream is free of error. Rather, only the encoding header is + // examined and guaranteed. + static std::unique_ptr<FixedLengthDeltaDecoder> Create( + absl::string_view input, + absl::optional<uint64_t> base, + size_t num_of_deltas); + + // FixedLengthDeltaDecoder objects are to be created by DecodeDeltas() and + // released by it before it returns. They're mostly a convenient way to + // avoid having to pass a lot of state between different functions. + // Therefore, it was deemed acceptable that `reader` does not own the buffer + // it reads, meaning the lifetime of `this` must not exceed the lifetime + // of `reader`'s underlying buffer. + FixedLengthDeltaDecoder(BitstreamReader reader, + const FixedLengthEncodingParameters& params, + absl::optional<uint64_t> base, + size_t num_of_deltas); + + // Perform the decoding using the parameters given to the ctor. + std::vector<absl::optional<uint64_t>> Decode(); + + // Add `delta` to `base` to produce the next value in a sequence. + // The delta is applied as signed/unsigned depending on the parameters + // given to the ctor. Wrap-around is taken into account according to the + // values' width, as specified by the aforementioned encoding parameters. + uint64_t ApplyDelta(uint64_t base, uint64_t delta) const; + + // Helpers for ApplyDelta(). + uint64_t ApplyUnsignedDelta(uint64_t base, uint64_t delta) const; + uint64_t ApplySignedDelta(uint64_t base, uint64_t delta) const; + + // Reader of the input stream to be decoded. Does not own that buffer. + // See comment above ctor for details. + BitstreamReader reader_; + + // The parameters according to which encoding will be done (width of + // fields, whether signed deltas should be used, etc.) + const FixedLengthEncodingParameters params_; + + // The encoding scheme assumes that at least one value is transmitted OOB, + // so that the first value can be encoded as a delta from that OOB value, + // which is `base_`. + const absl::optional<uint64_t> base_; + + // The number of values to be known to be decoded. + const size_t num_of_deltas_; +}; + +bool FixedLengthDeltaDecoder::IsSuitableDecoderFor(absl::string_view input) { + BitstreamReader reader(input); + uint64_t encoding_type_bits = reader.ReadBits(kBitsInHeaderForEncodingType); + if (!reader.Ok()) { + return false; + } + + const auto encoding_type = static_cast<EncodingType>(encoding_type_bits); + return encoding_type == + EncodingType::kFixedSizeUnsignedDeltasNoEarlyWrapNoOpt || + encoding_type == + EncodingType::kFixedSizeSignedDeltasEarlyWrapAndOptSupported; +} + +std::vector<absl::optional<uint64_t>> FixedLengthDeltaDecoder::DecodeDeltas( + absl::string_view input, + absl::optional<uint64_t> base, + size_t num_of_deltas) { + auto decoder = FixedLengthDeltaDecoder::Create(input, base, num_of_deltas); + if (!decoder) { + return std::vector<absl::optional<uint64_t>>(); + } + + return decoder->Decode(); +} + +std::unique_ptr<FixedLengthDeltaDecoder> FixedLengthDeltaDecoder::Create( + absl::string_view input, + absl::optional<uint64_t> base, + size_t num_of_deltas) { + BitstreamReader reader(input); + // Encoding type + uint32_t encoding_type_bits = reader.ReadBits(kBitsInHeaderForEncodingType); + if (!reader.Ok()) { + return nullptr; + } + + const EncodingType encoding = static_cast<EncodingType>(encoding_type_bits); + if (encoding != EncodingType::kFixedSizeUnsignedDeltasNoEarlyWrapNoOpt && + encoding != + EncodingType::kFixedSizeSignedDeltasEarlyWrapAndOptSupported) { + RTC_LOG(LS_WARNING) << "Unrecognized encoding type."; + return nullptr; + } + + // See encoding for +1's rationale. + const uint64_t delta_width_bits = + reader.ReadBits(kBitsInHeaderForDeltaWidthBits) + 1; + RTC_DCHECK_LE(delta_width_bits, 64); + + // signed_deltas, values_optional, value_width_bits + bool signed_deltas; + bool values_optional; + uint64_t value_width_bits; + if (encoding == EncodingType::kFixedSizeUnsignedDeltasNoEarlyWrapNoOpt) { + signed_deltas = kDefaultSignedDeltas; + values_optional = kDefaultValuesOptional; + value_width_bits = kDefaultValueWidthBits; + } else { + signed_deltas = reader.Read<bool>(); + values_optional = reader.Read<bool>(); + // See encoding for +1's rationale. + value_width_bits = reader.ReadBits(kBitsInHeaderForValueWidthBits) + 1; + RTC_DCHECK_LE(value_width_bits, 64); + } + + if (!reader.Ok()) { + return nullptr; + } + + // Note: Because of the way the parameters are read, it is not possible + // for illegal values to be read. We check nevertheless, in case the code + // changes in the future in a way that breaks this promise. + if (!FixedLengthEncodingParameters::ValidParameters( + delta_width_bits, signed_deltas, values_optional, value_width_bits)) { + RTC_LOG(LS_WARNING) << "Corrupt log; illegal encoding parameters."; + return nullptr; + } + + FixedLengthEncodingParameters params(delta_width_bits, signed_deltas, + values_optional, value_width_bits); + return absl::WrapUnique( + new FixedLengthDeltaDecoder(reader, params, base, num_of_deltas)); +} + +FixedLengthDeltaDecoder::FixedLengthDeltaDecoder( + BitstreamReader reader, + const FixedLengthEncodingParameters& params, + absl::optional<uint64_t> base, + size_t num_of_deltas) + : reader_(reader), + params_(params), + base_(base), + num_of_deltas_(num_of_deltas) { + RTC_DCHECK(reader_.Ok()); +} + +std::vector<absl::optional<uint64_t>> FixedLengthDeltaDecoder::Decode() { + RTC_DCHECK(reader_.Ok()); + std::vector<bool> existing_values(num_of_deltas_); + if (params_.values_optional()) { + for (size_t i = 0; i < num_of_deltas_; ++i) { + existing_values[i] = reader_.Read<bool>(); + } + } else { + std::fill(existing_values.begin(), existing_values.end(), true); + } + + absl::optional<uint64_t> previous = base_; + std::vector<absl::optional<uint64_t>> values(num_of_deltas_); + + for (size_t i = 0; i < num_of_deltas_; ++i) { + if (!existing_values[i]) { + RTC_DCHECK(params_.values_optional()); + continue; + } + + if (!previous) { + // If the base is non-existent, the first existent value is encoded as + // a varint, rather than as a delta. + RTC_DCHECK(!base_.has_value()); + values[i] = DecodeVarInt(reader_); + } else { + uint64_t delta = reader_.ReadBits(params_.delta_width_bits()); + values[i] = ApplyDelta(*previous, delta); + } + + previous = values[i]; + } + + if (!reader_.Ok()) { + values = {}; + } + + return values; +} + +uint64_t FixedLengthDeltaDecoder::ApplyDelta(uint64_t base, + uint64_t delta) const { + RTC_DCHECK_LE(base, MaxUnsignedValueOfBitWidth(params_.value_width_bits())); + RTC_DCHECK_LE(delta, MaxUnsignedValueOfBitWidth(params_.delta_width_bits())); + return params_.signed_deltas() ? ApplySignedDelta(base, delta) + : ApplyUnsignedDelta(base, delta); +} + +uint64_t FixedLengthDeltaDecoder::ApplyUnsignedDelta(uint64_t base, + uint64_t delta) const { + // Note: May still be used if signed deltas used. + RTC_DCHECK_LE(base, MaxUnsignedValueOfBitWidth(params_.value_width_bits())); + RTC_DCHECK_LE(delta, MaxUnsignedValueOfBitWidth(params_.delta_width_bits())); + return (base + delta) & params_.value_mask(); +} + +uint64_t FixedLengthDeltaDecoder::ApplySignedDelta(uint64_t base, + uint64_t delta) const { + RTC_DCHECK(params_.signed_deltas()); + RTC_DCHECK_LE(base, MaxUnsignedValueOfBitWidth(params_.value_width_bits())); + RTC_DCHECK_LE(delta, MaxUnsignedValueOfBitWidth(params_.delta_width_bits())); + + const uint64_t top_bit = static_cast<uint64_t>(1) + << (params_.delta_width_bits() - 1); + + const bool positive_delta = ((delta & top_bit) == 0); + if (positive_delta) { + return ApplyUnsignedDelta(base, delta); + } + + const uint64_t delta_abs = (~delta & params_.delta_mask()) + 1; + return (base - delta_abs) & params_.value_mask(); +} + +} // namespace + +std::string EncodeDeltas(absl::optional<uint64_t> base, + const std::vector<absl::optional<uint64_t>>& values) { + // TODO(eladalon): Support additional encodings. + return FixedLengthDeltaEncoder::EncodeDeltas(base, values); +} + +std::vector<absl::optional<uint64_t>> DecodeDeltas( + absl::string_view input, + absl::optional<uint64_t> base, + size_t num_of_deltas) { + RTC_DCHECK_GT(num_of_deltas, 0); // Allows empty vector to indicate error. + + // The empty string is a special case indicating that all values were equal + // to the base. + if (input.empty()) { + std::vector<absl::optional<uint64_t>> result(num_of_deltas); + std::fill(result.begin(), result.end(), base); + return result; + } + + if (FixedLengthDeltaDecoder::IsSuitableDecoderFor(input)) { + return FixedLengthDeltaDecoder::DecodeDeltas(input, base, num_of_deltas); + } + + RTC_LOG(LS_WARNING) << "Could not decode delta-encoded stream."; + return std::vector<absl::optional<uint64_t>>(); +} + +void SetFixedLengthEncoderDeltaSignednessForTesting(bool signedness) { + g_force_unsigned_for_testing = !signedness; + g_force_signed_for_testing = signedness; +} + +void UnsetFixedLengthEncoderDeltaSignednessForTesting() { + g_force_unsigned_for_testing = false; + g_force_signed_for_testing = false; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/delta_encoding.h b/third_party/libwebrtc/logging/rtc_event_log/encoder/delta_encoding.h new file mode 100644 index 0000000000..779cdc6b2f --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/delta_encoding.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_ENCODER_DELTA_ENCODING_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_DELTA_ENCODING_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" + +namespace webrtc { + +// Encode `values` as a sequence of deltas following on `base` and return it. +// If all of the values were equal to the base, an empty string will be +// returned; this is a valid encoding of that edge case. +// `base` is not guaranteed to be written into `output`, and must therefore +// be provided separately to the decoder. +// This function never fails. +// TODO(eladalon): Split into optional and non-optional variants (efficiency). +std::string EncodeDeltas(absl::optional<uint64_t> base, + const std::vector<absl::optional<uint64_t>>& values); + +// EncodeDeltas() and DecodeDeltas() are inverse operations; +// invoking DecodeDeltas() over the output of EncodeDeltas(), will return +// the input originally given to EncodeDeltas(). +// `num_of_deltas` must be greater than zero. If input is not a valid encoding +// of `num_of_deltas` elements based on `base`, the function returns an empty +// vector, which signals an error. +// TODO(eladalon): Split into optional and non-optional variants (efficiency). +std::vector<absl::optional<uint64_t>> DecodeDeltas( + absl::string_view input, + absl::optional<uint64_t> base, + size_t num_of_deltas); + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_DELTA_ENCODING_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/delta_encoding_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/delta_encoding_unittest.cc new file mode 100644 index 0000000000..d0f7fb93db --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/delta_encoding_unittest.cc @@ -0,0 +1,693 @@ +/* + * Copyright (c) 2018 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 "logging/rtc_event_log/encoder/delta_encoding.h" + +#include <algorithm> +#include <limits> +#include <numeric> +#include <string> +#include <tuple> +#include <vector> + +#include "absl/types/optional.h" +#include "rtc_base/arraysize.h" +#include "rtc_base/checks.h" +#include "rtc_base/random.h" +#include "test/gtest.h" + +namespace webrtc { + +void SetFixedLengthEncoderDeltaSignednessForTesting(bool signedness); +void UnsetFixedLengthEncoderDeltaSignednessForTesting(); + +namespace { + +enum class DeltaSignedness { kNoOverride, kForceUnsigned, kForceSigned }; + +void MaybeSetSignedness(DeltaSignedness signedness) { + switch (signedness) { + case DeltaSignedness::kNoOverride: + UnsetFixedLengthEncoderDeltaSignednessForTesting(); + return; + case DeltaSignedness::kForceUnsigned: + SetFixedLengthEncoderDeltaSignednessForTesting(false); + return; + case DeltaSignedness::kForceSigned: + SetFixedLengthEncoderDeltaSignednessForTesting(true); + return; + } + RTC_DCHECK_NOTREACHED(); +} + +uint64_t RandomWithMaxBitWidth(Random* prng, uint64_t max_width) { + RTC_DCHECK_GE(max_width, 1u); + RTC_DCHECK_LE(max_width, 64u); + + const uint64_t low = prng->Rand(std::numeric_limits<uint32_t>::max()); + const uint64_t high = + max_width > 32u ? prng->Rand(std::numeric_limits<uint32_t>::max()) : 0u; + + const uint64_t random_before_mask = (high << 32) | low; + + if (max_width < 64) { + return random_before_mask & ((static_cast<uint64_t>(1) << max_width) - 1); + } else { + return random_before_mask; + } +} + +// Encodes `values` based on `base`, then decodes the result and makes sure +// that it is equal to the original input. +// If `encoded_string` is non-null, the encoded result will also be written +// into it. +void TestEncodingAndDecoding( + absl::optional<uint64_t> base, + const std::vector<absl::optional<uint64_t>>& values, + std::string* encoded_string = nullptr) { + const std::string encoded = EncodeDeltas(base, values); + if (encoded_string) { + *encoded_string = encoded; + } + + const std::vector<absl::optional<uint64_t>> decoded = + DecodeDeltas(encoded, base, values.size()); + + EXPECT_EQ(decoded, values); +} + +std::vector<absl::optional<uint64_t>> CreateSequenceByFirstValue( + uint64_t first, + size_t sequence_length) { + std::vector<absl::optional<uint64_t>> sequence(sequence_length); + std::iota(sequence.begin(), sequence.end(), first); + return sequence; +} + +std::vector<absl::optional<uint64_t>> CreateSequenceByLastValue( + uint64_t last, + size_t num_values) { + const uint64_t first = last - num_values + 1; + std::vector<absl::optional<uint64_t>> result(num_values); + std::iota(result.begin(), result.end(), first); + return result; +} + +// If `sequence_length` is greater than the number of deltas, the sequence of +// deltas will wrap around. +std::vector<absl::optional<uint64_t>> CreateSequenceByOptionalDeltas( + uint64_t first, + const std::vector<absl::optional<uint64_t>>& deltas, + size_t sequence_length) { + RTC_DCHECK_GE(sequence_length, 1); + + std::vector<absl::optional<uint64_t>> sequence(sequence_length); + + uint64_t previous = first; + for (size_t i = 0, next_delta_index = 0; i < sequence.size(); ++i) { + if (deltas[next_delta_index].has_value()) { + sequence[i] = + absl::optional<uint64_t>(previous + deltas[next_delta_index].value()); + previous = sequence[i].value(); + } + next_delta_index = (next_delta_index + 1) % deltas.size(); + } + + return sequence; +} + +size_t EncodingLengthUpperBound(size_t delta_max_bit_width, + size_t num_of_deltas, + DeltaSignedness signedness_override) { + absl::optional<size_t> smallest_header_size_bytes; + switch (signedness_override) { + case DeltaSignedness::kNoOverride: + case DeltaSignedness::kForceUnsigned: + smallest_header_size_bytes = 1; + break; + case DeltaSignedness::kForceSigned: + smallest_header_size_bytes = 2; + break; + } + RTC_DCHECK(smallest_header_size_bytes); + + return delta_max_bit_width * num_of_deltas + *smallest_header_size_bytes; +} + +// If `sequence_length` is greater than the number of deltas, the sequence of +// deltas will wrap around. +std::vector<absl::optional<uint64_t>> CreateSequenceByDeltas( + uint64_t first, + const std::vector<uint64_t>& deltas, + size_t sequence_length) { + RTC_DCHECK(!deltas.empty()); + std::vector<absl::optional<uint64_t>> optional_deltas(deltas.size()); + for (size_t i = 0; i < deltas.size(); ++i) { + optional_deltas[i] = absl::optional<uint64_t>(deltas[i]); + } + return CreateSequenceByOptionalDeltas(first, optional_deltas, + sequence_length); +} + +// Tests of the delta encoding, parameterized by the number of values +// in the sequence created by the test. +class DeltaEncodingTest + : public ::testing::TestWithParam< + std::tuple<DeltaSignedness, size_t, bool, uint64_t>> { + public: + DeltaEncodingTest() + : signedness_(std::get<0>(GetParam())), + num_of_values_(std::get<1>(GetParam())), + optional_values_(std::get<2>(GetParam())), + partial_random_seed_(std::get<3>(GetParam())) { + MaybeSetSignedness(signedness_); + } + + ~DeltaEncodingTest() override = default; + + // Running with the same seed for all variants would make all tests start + // with the same sequence; avoid this by making the seed different. + uint64_t Seed() const { + // Multiply everything but by different primes to produce unique results. + return 2 * static_cast<uint64_t>(signedness_) + 3 * num_of_values_ + + 5 * optional_values_ + 7 * partial_random_seed_; + } + + const DeltaSignedness signedness_; + const uint64_t num_of_values_; + const bool optional_values_; + const uint64_t partial_random_seed_; // Explained where it's used. +}; + +TEST_P(DeltaEncodingTest, AllValuesEqualToExistentBaseValue) { + const absl::optional<uint64_t> base(3432); + std::vector<absl::optional<uint64_t>> values(num_of_values_); + std::fill(values.begin(), values.end(), base); + std::string encoded; + TestEncodingAndDecoding(base, values, &encoded); + + // Additional requirement - the encoding should be efficient in this + // case - the empty string will be used. + EXPECT_TRUE(encoded.empty()); +} + +TEST_P(DeltaEncodingTest, AllValuesEqualToNonExistentBaseValue) { + if (!optional_values_) { + return; // Test irrelevant for this case. + } + + const absl::optional<uint64_t> base; + std::vector<absl::optional<uint64_t>> values(num_of_values_); + std::fill(values.begin(), values.end(), base); + std::string encoded; + TestEncodingAndDecoding(base, values, &encoded); + + // Additional requirement - the encoding should be efficient in this + // case - the empty string will be used. + EXPECT_TRUE(encoded.empty()); +} + +TEST_P(DeltaEncodingTest, BaseNonExistentButSomeOtherValuesExist) { + if (!optional_values_) { + return; // Test irrelevant for this case. + } + + const absl::optional<uint64_t> base; + std::vector<absl::optional<uint64_t>> values(num_of_values_); + + Random prng(Seed()); + + const uint64_t max_bit_width = 1 + prng.Rand(63); // [1, 64] + + for (size_t i = 0; i < values.size();) { + // Leave a random number of values as non-existent. + const size_t non_existent_count = prng.Rand(values.size() - i - 1); + i += non_existent_count; + + // Assign random values to a random number of values. (At least one, to + // prevent this iteration of the outer loop from being a no-op.) + const size_t existent_count = + std::max<size_t>(prng.Rand(values.size() - i - 1), 1); + for (size_t j = 0; j < existent_count; ++j) { + values[i + j] = RandomWithMaxBitWidth(&prng, max_bit_width); + } + i += existent_count; + } + + TestEncodingAndDecoding(base, values); +} + +TEST_P(DeltaEncodingTest, MinDeltaNoWrapAround) { + const absl::optional<uint64_t> base(3432); + + auto values = CreateSequenceByFirstValue(base.value() + 1, num_of_values_); + ASSERT_GT(values[values.size() - 1], base) << "Sanity; must not wrap around"; + + if (optional_values_) { + // Arbitrarily make one of the values non-existent, to force + // optional-supporting encoding. + values[0] = absl::optional<uint64_t>(); + } + + TestEncodingAndDecoding(base, values); +} + +TEST_P(DeltaEncodingTest, BigDeltaNoWrapAround) { + const uint64_t kBigDelta = 132828; + const absl::optional<uint64_t> base(3432); + + auto values = + CreateSequenceByFirstValue(base.value() + kBigDelta, num_of_values_); + ASSERT_GT(values[values.size() - 1], base) << "Sanity; must not wrap around"; + + if (optional_values_) { + // Arbitrarily make one of the values non-existent, to force + // optional-supporting encoding. + values[0] = absl::optional<uint64_t>(); + } + + TestEncodingAndDecoding(base, values); +} + +TEST_P(DeltaEncodingTest, MaxDeltaNoWrapAround) { + const absl::optional<uint64_t> base(3432); + + auto values = CreateSequenceByLastValue(std::numeric_limits<uint64_t>::max(), + num_of_values_); + ASSERT_GT(values[values.size() - 1], base) << "Sanity; must not wrap around"; + + if (optional_values_) { + // Arbitrarily make one of the values non-existent, to force + // optional-supporting encoding. + values[0] = absl::optional<uint64_t>(); + } + + TestEncodingAndDecoding(base, values); +} + +TEST_P(DeltaEncodingTest, SmallDeltaWithWrapAroundComparedToBase) { + if (optional_values_ && num_of_values_ == 1) { + return; // Inapplicable + } + + const absl::optional<uint64_t> base(std::numeric_limits<uint64_t>::max()); + + auto values = CreateSequenceByDeltas(*base, {1, 10, 3}, num_of_values_); + ASSERT_LT(values[0], base) << "Sanity; must wrap around"; + + if (optional_values_) { + // Arbitrarily make one of the values non-existent, to force + // optional-supporting encoding. + values[1] = absl::optional<uint64_t>(); + } + + TestEncodingAndDecoding(base, values); +} + +TEST_P(DeltaEncodingTest, SmallDeltaWithWrapAroundInValueSequence) { + if (num_of_values_ == 1 || (optional_values_ && num_of_values_ < 3)) { + return; // Inapplicable. + } + + const absl::optional<uint64_t> base(std::numeric_limits<uint64_t>::max() - 2); + + auto values = CreateSequenceByDeltas(*base, {1, 10, 3}, num_of_values_); + ASSERT_LT(values[values.size() - 1], values[0]) << "Sanity; must wrap around"; + + if (optional_values_) { + // Arbitrarily make one of the values non-existent, to force + // optional-supporting encoding. + RTC_DCHECK_GT(values.size() - 1, 1u); // Wrap around not cancelled. + values[1] = absl::optional<uint64_t>(); + } + + TestEncodingAndDecoding(base, values); +} + +// Suppress "integral constant overflow" warning; this is the test's focus. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4307) +#endif +TEST_P(DeltaEncodingTest, BigDeltaWithWrapAroundComparedToBase) { + if (optional_values_ && num_of_values_ == 1) { + return; // Inapplicable + } + + const uint64_t kBigDelta = 132828; + const absl::optional<uint64_t> base(std::numeric_limits<uint64_t>::max() - + kBigDelta + 3); + + auto values = + CreateSequenceByFirstValue(base.value() + kBigDelta, num_of_values_); + ASSERT_LT(values[0], base.value()) << "Sanity; must wrap around"; + + if (optional_values_) { + // Arbitrarily make one of the values non-existent, to force + // optional-supporting encoding. + values[1] = absl::optional<uint64_t>(); + } + + TestEncodingAndDecoding(base, values); +} + +TEST_P(DeltaEncodingTest, BigDeltaWithWrapAroundInValueSequence) { + if (num_of_values_ == 1 || (optional_values_ && num_of_values_ < 3)) { + return; // Inapplicable. + } + + const uint64_t kBigDelta = 132828; + const absl::optional<uint64_t> base(std::numeric_limits<uint64_t>::max() - + kBigDelta + 3); + + auto values = CreateSequenceByFirstValue(std::numeric_limits<uint64_t>::max(), + num_of_values_); + ASSERT_LT(values[values.size() - 1], values[0]) << "Sanity; must wrap around"; + + if (optional_values_) { + // Arbitrarily make one of the values non-existent, to force + // optional-supporting encoding. + RTC_DCHECK_GT(values.size() - 1, 1u); // Wrap around not cancelled. + values[1] = absl::optional<uint64_t>(); + } + + TestEncodingAndDecoding(base, values); +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +TEST_P(DeltaEncodingTest, MaxDeltaWithWrapAroundComparedToBase) { + if (optional_values_ && num_of_values_ == 1) { + return; // Inapplicable + } + + const absl::optional<uint64_t> base(3432); + auto values = CreateSequenceByFirstValue(*base - 1, num_of_values_); + + if (optional_values_) { + // Arbitrarily make one of the values non-existent, to force + // optional-supporting encoding. + values[1] = absl::optional<uint64_t>(); + } + + TestEncodingAndDecoding(base, values); +} + +TEST_P(DeltaEncodingTest, MaxDeltaWithWrapAroundInValueSequence) { + if (num_of_values_ == 1 || (optional_values_ && num_of_values_ < 3)) { + return; // Inapplicable. + } + + const absl::optional<uint64_t> base(3432); + + auto values = CreateSequenceByDeltas( + *base, {0, std::numeric_limits<uint64_t>::max(), 3}, num_of_values_); + // Wraps around continuously by virtue of being max(); will not ASSERT. + + if (optional_values_) { + // Arbitrarily make one of the values non-existent, to force + // optional-supporting encoding. + RTC_DCHECK_GT(values.size() - 1, 1u); // Wrap around not cancelled. + values[1] = absl::optional<uint64_t>(); + } + + TestEncodingAndDecoding(base, values); +} + +// If num_of_values_ == 1, a zero delta will yield an empty string; that's +// already covered by AllValuesEqualToExistentBaseValue, but it doesn't hurt to +// test again. For all other cases, we have a new test. +TEST_P(DeltaEncodingTest, ZeroDelta) { + const absl::optional<uint64_t> base(3432); + + // Arbitrary sequence of deltas with intentional zero deltas, as well as + // consecutive zeros. + const std::vector<uint64_t> deltas = {0, 312, 11, 1, 1, 0, 0, 12, + 400321, 3, 3, 12, 5, 0, 6}; + auto values = CreateSequenceByDeltas(base.value(), deltas, num_of_values_); + + if (optional_values_) { + // Arbitrarily make one of the values non-existent, to force + // optional-supporting encoding. + values[0] = absl::optional<uint64_t>(); + } + + TestEncodingAndDecoding(base, values); +} + +INSTANTIATE_TEST_SUITE_P( + SignednessOverrideAndNumberOfValuesInSequence, + DeltaEncodingTest, + ::testing::Combine(::testing::Values(DeltaSignedness::kNoOverride, + DeltaSignedness::kForceUnsigned, + DeltaSignedness::kForceSigned), + ::testing::Values(1, 2, 100, 10000), + ::testing::Bool(), + ::testing::Values(10, 20, 30))); + +// Tests over the quality of the compression (as opposed to its correctness). +// Not to be confused with tests of runtime efficiency. +class DeltaEncodingCompressionQualityTest + : public ::testing::TestWithParam< + std::tuple<DeltaSignedness, uint64_t, uint64_t, uint64_t>> { + public: + DeltaEncodingCompressionQualityTest() + : signedness_(std::get<0>(GetParam())), + delta_max_bit_width_(std::get<1>(GetParam())), + num_of_values_(std::get<2>(GetParam())), + partial_random_seed_(std::get<3>(GetParam())) { + MaybeSetSignedness(signedness_); + } + + ~DeltaEncodingCompressionQualityTest() override = default; + + // Running with the same seed for all variants would make all tests start + // with the same sequence; avoid this by making the seed different. + uint64_t Seed() const { + // Multiply everything but by different primes to produce unique results. + return 2 * static_cast<uint64_t>(signedness_) + 3 * delta_max_bit_width_ + + 5 * delta_max_bit_width_ + 7 * num_of_values_ + + 11 * partial_random_seed_; + } + + const DeltaSignedness signedness_; + const uint64_t delta_max_bit_width_; + const uint64_t num_of_values_; + const uint64_t partial_random_seed_; // Explained where it's used. +}; + +// If no wrap-around occurs in the stream, the width of the values does not +// matter to compression performance; only the deltas matter. +TEST_P(DeltaEncodingCompressionQualityTest, + BaseDoesNotAffectEfficiencyIfNoWrapAround) { + // 1. Bases which will not produce a wrap-around. + // 2. The last base - 0xffffffffffffffff - does cause a wrap-around, but + // that still works, because the width is 64 anyway, and does not + // need to be conveyed explicitly in the encoding header. + const uint64_t bases[] = {0, 0x55, 0xffffffff, + std::numeric_limits<uint64_t>::max()}; + const size_t kIntendedWrapAroundBaseIndex = arraysize(bases); + + std::vector<uint64_t> deltas(num_of_values_); + + // Allows us to make sure that the deltas do not produce a wrap-around. + uint64_t last_element[arraysize(bases)]; + memcpy(last_element, bases, sizeof(bases)); + + // Avoid empty `deltas` due to first element causing wrap-around. + deltas[0] = 1; + for (size_t i = 0; i < arraysize(last_element); ++i) { + last_element[i] += 1; + } + + Random prng(Seed()); + + for (size_t i = 1; i < deltas.size(); ++i) { + const uint64_t delta = RandomWithMaxBitWidth(&prng, delta_max_bit_width_); + + bool wrap_around = false; + for (size_t j = 0; j < arraysize(last_element); ++j) { + if (j == kIntendedWrapAroundBaseIndex) { + continue; + } + + last_element[j] += delta; + if (last_element[j] < bases[j]) { + wrap_around = true; + break; + } + } + + if (wrap_around) { + deltas.resize(i); + break; + } + + deltas[i] = delta; + } + + std::string encodings[arraysize(bases)]; + + for (size_t i = 0; i < arraysize(bases); ++i) { + const auto values = + CreateSequenceByDeltas(bases[i], deltas, num_of_values_); + // Produce the encoding and write it to encodings[i]. + // By using TestEncodingAndDecoding() to do this, we also sanity-test + // the encoding/decoding, though that is not the test's focus. + TestEncodingAndDecoding(bases[i], values, &encodings[i]); + EXPECT_LE(encodings[i].length(), + EncodingLengthUpperBound(delta_max_bit_width_, num_of_values_, + signedness_)); + } + + // Test focus - all of the encodings should be the same, as they are based + // on the same delta sequence, and do not contain a wrap-around. + for (size_t i = 1; i < arraysize(encodings); ++i) { + EXPECT_EQ(encodings[i], encodings[0]); + } +} + +INSTANTIATE_TEST_SUITE_P( + SignednessOverrideAndDeltaMaxBitWidthAndNumberOfValuesInSequence, + DeltaEncodingCompressionQualityTest, + ::testing::Combine( + ::testing::Values(DeltaSignedness::kNoOverride, + DeltaSignedness::kForceUnsigned, + DeltaSignedness::kForceSigned), + ::testing::Values(1, 4, 8, 15, 16, 17, 31, 32, 33, 63, 64), + ::testing::Values(1, 2, 100, 10000), + ::testing::Values(11, 12, 13))); + +// Similar to DeltaEncodingTest, but instead of semi-surgically producing +// specific cases, produce large amount of semi-realistic inputs. +class DeltaEncodingFuzzerLikeTest + : public ::testing::TestWithParam< + std::tuple<DeltaSignedness, uint64_t, uint64_t, bool, uint64_t>> { + public: + DeltaEncodingFuzzerLikeTest() + : signedness_(std::get<0>(GetParam())), + delta_max_bit_width_(std::get<1>(GetParam())), + num_of_values_(std::get<2>(GetParam())), + optional_values_(std::get<3>(GetParam())), + partial_random_seed_(std::get<4>(GetParam())) { + MaybeSetSignedness(signedness_); + } + + ~DeltaEncodingFuzzerLikeTest() override = default; + + // Running with the same seed for all variants would make all tests start + // with the same sequence; avoid this by making the seed different. + uint64_t Seed() const { + // Multiply everything but by different primes to produce unique results. + return 2 * static_cast<uint64_t>(signedness_) + 3 * delta_max_bit_width_ + + 5 * delta_max_bit_width_ + 7 * num_of_values_ + + 11 * static_cast<uint64_t>(optional_values_) + + 13 * partial_random_seed_; + } + + const DeltaSignedness signedness_; + const uint64_t delta_max_bit_width_; + const uint64_t num_of_values_; + const bool optional_values_; + const uint64_t partial_random_seed_; // Explained where it's used. +}; + +TEST_P(DeltaEncodingFuzzerLikeTest, Test) { + const absl::optional<uint64_t> base(3432); + + Random prng(Seed()); + std::vector<absl::optional<uint64_t>> deltas(num_of_values_); + for (size_t i = 0; i < deltas.size(); ++i) { + if (!optional_values_ || prng.Rand<bool>()) { + deltas[i] = RandomWithMaxBitWidth(&prng, delta_max_bit_width_); + } + } + const auto values = + CreateSequenceByOptionalDeltas(base.value(), deltas, num_of_values_); + + TestEncodingAndDecoding(base, values); +} + +INSTANTIATE_TEST_SUITE_P( + SignednessOverrideAndDeltaMaxBitWidthAndNumberOfValuesInSequence, + DeltaEncodingFuzzerLikeTest, + ::testing::Combine( + ::testing::Values(DeltaSignedness::kNoOverride, + DeltaSignedness::kForceUnsigned, + DeltaSignedness::kForceSigned), + ::testing::Values(1, 4, 8, 15, 16, 17, 31, 32, 33, 63, 64), + ::testing::Values(1, 2, 100, 10000), + ::testing::Bool(), + ::testing::Values(21, 22, 23))); + +class DeltaEncodingSpecificEdgeCasesTest + : public ::testing::TestWithParam< + std::tuple<DeltaSignedness, uint64_t, bool>> { + public: + DeltaEncodingSpecificEdgeCasesTest() { + UnsetFixedLengthEncoderDeltaSignednessForTesting(); + } + + ~DeltaEncodingSpecificEdgeCasesTest() override = default; +}; + +// This case is special because it produces identical forward/backward deltas. +TEST_F(DeltaEncodingSpecificEdgeCasesTest, SignedDeltaWithOnlyTopBitOn) { + MaybeSetSignedness(DeltaSignedness::kForceSigned); + + const absl::optional<uint64_t> base(3432); + + const uint64_t delta = static_cast<uint64_t>(1) << 63; + const std::vector<absl::optional<uint64_t>> values = {base.value() + delta}; + + TestEncodingAndDecoding(base, values); +} + +TEST_F(DeltaEncodingSpecificEdgeCasesTest, MaximumUnsignedDelta) { + MaybeSetSignedness(DeltaSignedness::kForceUnsigned); + + const absl::optional<uint64_t> base((static_cast<uint64_t>(1) << 63) + 0x123); + + const std::vector<absl::optional<uint64_t>> values = {base.value() - 1}; + + TestEncodingAndDecoding(base, values); +} + +// Check that, if all deltas are set to -1, things still work. +TEST_P(DeltaEncodingSpecificEdgeCasesTest, ReverseSequence) { + MaybeSetSignedness(std::get<0>(GetParam())); + const uint64_t width = std::get<1>(GetParam()); + const bool wrap_around = std::get<2>(GetParam()); + + const uint64_t value_mask = (width == 64) + ? std::numeric_limits<uint64_t>::max() + : ((static_cast<uint64_t>(1) << width) - 1); + + const uint64_t base = wrap_around ? 1u : (0xf82d3 & value_mask); + const std::vector<absl::optional<uint64_t>> values = { + (base - 1u) & value_mask, (base - 2u) & value_mask, + (base - 3u) & value_mask}; + + TestEncodingAndDecoding(base, values); +} + +INSTANTIATE_TEST_SUITE_P( + _, + DeltaEncodingSpecificEdgeCasesTest, + ::testing::Combine( + ::testing::Values(DeltaSignedness::kNoOverride, + DeltaSignedness::kForceUnsigned, + DeltaSignedness::kForceSigned), + ::testing::Values(1, 4, 8, 15, 16, 17, 31, 32, 33, 63, 64), + ::testing::Bool())); + +} // namespace +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/optional_blob_encoding.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/optional_blob_encoding.cc new file mode 100644 index 0000000000..81d2c0625b --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/optional_blob_encoding.cc @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2023 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 "logging/rtc_event_log/encoder/optional_blob_encoding.h" + +#include <cstdint> + +#include "rtc_base/bit_buffer.h" +#include "rtc_base/bitstream_reader.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +std::string EncodeOptionalBlobs( + const std::vector<absl::optional<std::string>>& blobs) { + if (blobs.empty()) { + return {}; + } + + size_t reserve_size_bits = 1; + size_t num_blobs_present = 0; + for (const auto& blob : blobs) { + if (blob.has_value()) { + ++num_blobs_present; + reserve_size_bits += + (rtc::BitBufferWriter::kMaxLeb128Length.bytes() + blob->size()) * 8; + } + } + + if (num_blobs_present == 0) { + return {}; + } + + const bool all_blobs_present = num_blobs_present == blobs.size(); + if (!all_blobs_present) { + reserve_size_bits += blobs.size(); + } + + std::vector<uint8_t> buffer((reserve_size_bits + 7) / 8); + rtc::BitBufferWriter writer(buffer.data(), buffer.size()); + + // Write present bits if all blobs are not present. + writer.WriteBits(all_blobs_present, 1); + if (!all_blobs_present) { + for (const auto& blob : blobs) { + writer.WriteBits(blob.has_value(), 1); + } + } + + // Byte align the writer. + writer.ConsumeBits(writer.RemainingBitCount() % 8); + + // Write blobs. + for (const auto& blob : blobs) { + if (blob.has_value()) { + writer.WriteLeb128(blob->length()); + writer.WriteString(*blob); + } + } + + size_t bytes_written; + size_t bits_written; + writer.GetCurrentOffset(&bytes_written, &bits_written); + RTC_CHECK_EQ(bits_written, 0); + RTC_CHECK_LE(bytes_written, buffer.size()); + + return std::string(buffer.data(), buffer.data() + bytes_written); +} + +std::vector<absl::optional<std::string>> DecodeOptionalBlobs( + absl::string_view encoded_blobs, + size_t num_of_blobs) { + std::vector<absl::optional<std::string>> res(num_of_blobs); + if (encoded_blobs.empty() || num_of_blobs == 0) { + return res; + } + + BitstreamReader reader(encoded_blobs); + const bool all_blobs_present = reader.ReadBit(); + + // Read present bits if all blobs are not present. + std::vector<uint8_t> present; + if (!all_blobs_present) { + present.resize(num_of_blobs); + for (size_t i = 0; i < num_of_blobs; ++i) { + present[i] = reader.ReadBit(); + } + } + + // Byte align the reader. + reader.ConsumeBits(reader.RemainingBitCount() % 8); + + // Read the blobs. + for (size_t i = 0; i < num_of_blobs; ++i) { + if (!all_blobs_present && !present[i]) { + continue; + } + res[i] = reader.ReadString(reader.ReadLeb128()); + } + + // The result is only valid if exactly all bits was consumed during decoding. + if (!reader.Ok() || reader.RemainingBitCount() > 0) { + return {}; + } + + return res; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/optional_blob_encoding.h b/third_party/libwebrtc/logging/rtc_event_log/encoder/optional_blob_encoding.h new file mode 100644 index 0000000000..32f52785c6 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/optional_blob_encoding.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_ENCODER_OPTIONAL_BLOB_ENCODING_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_OPTIONAL_BLOB_ENCODING_H_ + +#include <stddef.h> + +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" + +namespace webrtc { + +// Encode a sequence of optional strings, whose length is not known to be +// discernable from the blob itself (i.e. without being transmitted OOB), +// in a way that would allow us to separate them again on the decoding side. +// EncodeOptionalBlobs() may not fail but may return an empty string +std::string EncodeOptionalBlobs( + const std::vector<absl::optional<std::string>>& blobs); + +// Calling DecodeOptionalBlobs() on an empty string, or with `num_of_blobs` set +// to 0, is an error. DecodeOptionalBlobs() returns an empty vector if it fails, +// which can happen if `encoded_blobs` is corrupted. +std::vector<absl::optional<std::string>> DecodeOptionalBlobs( + absl::string_view encoded_blobs, + size_t num_of_blobs); + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_OPTIONAL_BLOB_ENCODING_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/optional_blob_encoding_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/optional_blob_encoding_unittest.cc new file mode 100644 index 0000000000..bdb876f707 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/optional_blob_encoding_unittest.cc @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2023 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 "logging/rtc_event_log/encoder/optional_blob_encoding.h" + +#include <string> +#include <vector> + +#include "test/gmock.h" +#include "test/gtest.h" + +using ::testing::ElementsAre; +using ::testing::IsEmpty; + +namespace webrtc { +namespace { + +class BitBuilder { + public: + BitBuilder& Bit(uint8_t bit) { + if (total_bits_ % 8 == 0) { + bits_.push_back(0); + } + bits_[total_bits_ / 8] |= bit << (7 - (total_bits_ % 8)); + ++total_bits_; + return *this; + } + + BitBuilder& Bytes(const std::vector<uint8_t>& bytes) { + for (uint8_t byte : bytes) { + for (int i = 1; i <= 8; ++i) { + uint8_t bit = (byte >> (8 - i)) & 1; + Bit(bit); + } + } + return *this; + } + + BitBuilder& ByteAlign() { + while (total_bits_ % 8 > 0) { + Bit(0); + } + return *this; + } + + std::string AsString() { return std::string(bits_.begin(), bits_.end()); } + + private: + std::vector<uint8_t> bits_; + uint64_t total_bits_ = 0; +}; + +TEST(OptionalBlobEncoding, AllBlobsPresent) { + std::string encoded = EncodeOptionalBlobs({"a", "b", "c"}); + std::string expected = BitBuilder() + .Bit(1) + .ByteAlign() + .Bytes({0x01, 'a'}) + .Bytes({0x01, 'b'}) + .Bytes({0x01, 'c'}) + .AsString(); + EXPECT_EQ(encoded, expected); +} + +TEST(OptionalBlobEncoding, SomeBlobsPresent) { + std::string encoded = EncodeOptionalBlobs({"a", absl::nullopt, "c"}); + std::string expected = BitBuilder() + .Bit(0) + .Bit(1) + .Bit(0) + .Bit(1) + .ByteAlign() + .Bytes({0x01, 'a'}) + .Bytes({0x01, 'c'}) + .AsString(); + EXPECT_EQ(encoded, expected); +} + +TEST(OptionalBlobEncoding, NoBlobsPresent) { + std::string encoded = + EncodeOptionalBlobs({absl::nullopt, absl::nullopt, absl::nullopt}); + EXPECT_THAT(encoded, IsEmpty()); +} + +TEST(OptionalBlobEncoding, EmptyBlobsPresent) { + std::string encoded = EncodeOptionalBlobs({absl::nullopt, "", absl::nullopt}); + std::string expected = BitBuilder() + .Bit(0) + .Bit(0) + .Bit(1) + .Bit(0) + .ByteAlign() + .Bytes({0x0}) + .AsString(); + EXPECT_EQ(encoded, expected); +} + +TEST(OptionalBlobEncoding, ZeroBlobs) { + std::string encoded = EncodeOptionalBlobs({}); + EXPECT_EQ(encoded, std::string()); +} + +TEST(OptionalBlobEncoding, LongBlobs) { + std::string medium_string(100, 'a'); + std::string long_string(200, 'b'); + std::string encoded = EncodeOptionalBlobs({medium_string, long_string}); + std::string expected = + BitBuilder() + .Bit(1) + .ByteAlign() + .Bytes({0x64}) + .Bytes({medium_string.begin(), medium_string.end()}) + .Bytes({0xC8, 0x01}) + .Bytes({long_string.begin(), long_string.end()}) + .AsString(); + EXPECT_EQ(encoded, expected); +} + +TEST(OptionalBlobDecoding, AllBlobsPresent) { + std::string encoded = BitBuilder() + .Bit(1) + .ByteAlign() + .Bytes({0x01, 'a'}) + .Bytes({0x01, 'b'}) + .Bytes({0x01, 'c'}) + .AsString(); + auto decoded = DecodeOptionalBlobs(encoded, 3); + EXPECT_THAT(decoded, ElementsAre("a", "b", "c")); +} + +TEST(OptionalBlobDecoding, SomeBlobsPresent) { + std::string encoded = BitBuilder() + .Bit(0) + .Bit(1) + .Bit(0) + .Bit(1) + .ByteAlign() + .Bytes({0x01, 'a'}) + .Bytes({0x01, 'c'}) + .AsString(); + auto decoded = DecodeOptionalBlobs(encoded, 3); + EXPECT_THAT(decoded, ElementsAre("a", absl::nullopt, "c")); +} + +TEST(OptionalBlobDecoding, NoBlobsPresent) { + auto decoded = DecodeOptionalBlobs("", 3); + EXPECT_THAT(decoded, + ElementsAre(absl::nullopt, absl::nullopt, absl::nullopt)); +} + +TEST(OptionalBlobDecoding, EmptyBlobsPresent) { + std::string encoded = BitBuilder() + .Bit(0) + .Bit(0) + .Bit(1) + .Bit(0) + .ByteAlign() + .Bytes({0x0}) + .AsString(); + auto decoded = DecodeOptionalBlobs(encoded, 3); + EXPECT_THAT(decoded, ElementsAre(absl::nullopt, "", absl::nullopt)); +} + +TEST(OptionalBlobDecoding, ZeroBlobs) { + std::string encoded; + auto decoded = DecodeOptionalBlobs(encoded, 0); + EXPECT_THAT(decoded, IsEmpty()); +} + +TEST(OptionalBlobDecoding, LongBlobs) { + std::string medium_string(100, 'a'); + std::string long_string(200, 'b'); + std::string encoded = BitBuilder() + .Bit(1) + .ByteAlign() + .Bytes({0x64}) + .Bytes({medium_string.begin(), medium_string.end()}) + .Bytes({0xC8, 0x01}) + .Bytes({long_string.begin(), long_string.end()}) + .AsString(); + auto decoded = DecodeOptionalBlobs(encoded, 2); + EXPECT_THAT(decoded, ElementsAre(medium_string, long_string)); +} + +TEST(OptionalBlobDecoding, TooShortEncodedBlobLength) { + std::string encoded = + BitBuilder().Bit(1).ByteAlign().Bytes({0x01, 'a', 'b'}).AsString(); + auto decoded = DecodeOptionalBlobs(encoded, 1); + EXPECT_THAT(decoded, IsEmpty()); +} + +TEST(OptionalBlobDecoding, TooLongEncodedBlobLength) { + std::string encoded = + BitBuilder().Bit(1).ByteAlign().Bytes({0x03, 'a', 'b'}).AsString(); + auto decoded = DecodeOptionalBlobs(encoded, 1); + EXPECT_THAT(decoded, IsEmpty()); +} + +TEST(OptionalBlobDecoding, TooLongEncodedBufferLength) { + std::string encoded = BitBuilder().Bytes({0x00, 0x00, 0x00}).AsString(); + auto decoded = DecodeOptionalBlobs(encoded, 8); + EXPECT_THAT(decoded, IsEmpty()); +} + +} // namespace +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder.h b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder.h new file mode 100644 index 0000000000..3c3dc78990 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_H_ + +#include <deque> +#include <memory> +#include <string> + +#include "api/rtc_event_log/rtc_event.h" + +namespace webrtc { +class RtcEventLogEncoder { + public: + virtual ~RtcEventLogEncoder() = default; + + virtual std::string EncodeLogStart(int64_t timestamp_us, + int64_t utc_time_us) = 0; + virtual std::string EncodeLogEnd(int64_t timestamp_us) = 0; + + virtual std::string EncodeBatch( + std::deque<std::unique_ptr<RtcEvent>>::const_iterator begin, + std::deque<std::unique_ptr<RtcEvent>>::const_iterator end) = 0; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_common.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_common.cc new file mode 100644 index 0000000000..7aea47611d --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_common.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 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 "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { +// We use 0x3fff because that gives decent precision (compared to the underlying +// measurement producing the packet loss fraction) on the one hand, while +// allowing us to use no more than 2 bytes in varint form on the other hand. +// (We might also fixed-size encode using at most 14 bits.) +constexpr uint32_t kPacketLossFractionRange = (1 << 14) - 1; // 0x3fff +constexpr float kPacketLossFractionRangeFloat = + static_cast<float>(kPacketLossFractionRange); +} // namespace + +uint32_t ConvertPacketLossFractionToProtoFormat(float packet_loss_fraction) { + RTC_DCHECK_GE(packet_loss_fraction, 0); + RTC_DCHECK_LE(packet_loss_fraction, 1); + return static_cast<uint32_t>(packet_loss_fraction * kPacketLossFractionRange); +} + +bool ParsePacketLossFractionFromProtoFormat(uint32_t proto_packet_loss_fraction, + float* output) { + if (proto_packet_loss_fraction >= kPacketLossFractionRange) { + return false; + } + *output = proto_packet_loss_fraction / kPacketLossFractionRangeFloat; + return true; +} +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h new file mode 100644 index 0000000000..c167a8eb8f --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_COMMON_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_COMMON_H_ + +#include <stdint.h> + +#include <limits> +#include <type_traits> + +namespace webrtc { + +// Convert between the packet fraction loss (a floating point number in +// the range [0.0, 1.0]), and a uint32_t with up to a fixed number of bits. +// The latter can be more efficiently stored in a protobuf and/or delta-encoded. +uint32_t ConvertPacketLossFractionToProtoFormat(float packet_loss_fraction); +bool ParsePacketLossFractionFromProtoFormat(uint32_t proto_packet_loss_fraction, + float* output); + +} // namespace webrtc + +namespace webrtc_event_logging { + +// Produce an unsigned representation of a signed integer. On two's complement +// machines, this is equivalent to: +// static_cast<uint64_t>(static_cast<std::make_unsigned<T>>(y)) +template <typename T> +uint64_t ToUnsigned(T y) { + static_assert(std::is_integral<T>::value, ""); + static_assert(std::is_signed<T>::value, ""); + + // Note that a signed integer whose width is N bits, has N-1 digits. + static_assert(std::numeric_limits<T>::digits < 64, ""); + + constexpr T MIN_T = std::numeric_limits<T>::min(); + constexpr T MAX_T = std::numeric_limits<T>::max(); + + static_assert(MAX_T + MIN_T + 1 >= 0, "MAX_T >= abs(MIN_T) - 1"); + + if (y >= 0) { + return static_cast<uint64_t>(y); + } else { + // y is in the range [MIN_T, -1], so (y - MIN_T) is in the + // range [0, abs(MIN_T) - 1]. This is representable in a T + // because MAX_T >= abs(MIN_T) - 1, as per the static_assert above. + return static_cast<uint64_t>(MAX_T) + 1 + static_cast<uint64_t>(y - MIN_T); + } +} + +// Assuming x = ToUnsigned(y), return `y`. +// Note: static_cast<T>(x) would work on most platforms and compilers, but +// involves undefined behavior. This function is well-defined, and can be +// optimized to a noop for 64 bit types, or a few arithmetic +// instructions and a single conditional jump for narrower types. +template <typename T> +bool ToSigned(uint64_t x, T* y) { + static_assert(std::is_integral<T>::value, ""); + static_assert(std::is_signed<T>::value, ""); + + // Note that a signed integer whose width is N bits, has N-1 digits. + static_assert(std::numeric_limits<T>::digits < 64, ""); + + constexpr T MIN_T = std::numeric_limits<T>::min(); + constexpr T MAX_T = std::numeric_limits<T>::max(); + + using UNSIGNED_T = typename std::make_unsigned<T>::type; + constexpr auto MAX_UNSIGNED_T = std::numeric_limits<UNSIGNED_T>::max(); + if (x > static_cast<uint64_t>(MAX_UNSIGNED_T)) { + return false; // `x` cannot be represented using a T. + } + + if (x <= static_cast<uint64_t>(MAX_T)) { + // The original value was positive, so it is safe to just static_cast. + *y = static_cast<T>(x); + } else { // x > static_cast<uint64_t>(MAX_T) + const uint64_t neg_x = x - static_cast<uint64_t>(MAX_T) - 1; + *y = static_cast<T>(neg_x) + MIN_T; + } + + return true; +} + +} // namespace webrtc_event_logging + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_COMMON_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_common_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_common_unittest.cc new file mode 100644 index 0000000000..0afa5368d1 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_common_unittest.cc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018 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 "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" + +#include <cstdint> +#include <limits> +#include <type_traits> +#include <vector> + +#include "test/gtest.h" + +namespace webrtc_event_logging { +namespace { + +template <typename T> +class SignednessConversionTest : public ::testing::Test { + public: + static_assert(std::is_integral<T>::value, ""); + static_assert(std::is_signed<T>::value, ""); +}; + +TYPED_TEST_SUITE_P(SignednessConversionTest); + +TYPED_TEST_P(SignednessConversionTest, CorrectlyConvertsLegalValues) { + using T = TypeParam; + std::vector<T> legal_values = {std::numeric_limits<T>::min(), + std::numeric_limits<T>::min() + 1, + -1, + 0, + 1, + std::numeric_limits<T>::max() - 1, + std::numeric_limits<T>::max()}; + for (T val : legal_values) { + const auto unsigned_val = ToUnsigned(val); + T signed_val; + ASSERT_TRUE(ToSigned<T>(unsigned_val, &signed_val)) + << "Failed on " << static_cast<uint64_t>(unsigned_val) << "."; + EXPECT_EQ(val, signed_val) + << "Failed on " << static_cast<uint64_t>(unsigned_val) << "."; + } +} + +TYPED_TEST_P(SignednessConversionTest, FailsOnConvertingIllegalValues) { + using T = TypeParam; + + // Note that a signed integer whose width is N bits, has N-1 digits. + constexpr bool width_is_64 = std::numeric_limits<T>::digits == 63; + + if (width_is_64) { + return; // Test irrelevant; illegal values do not exist. + } + + const uint64_t max_legal_value = ToUnsigned(static_cast<T>(-1)); + + const std::vector<uint64_t> illegal_values = { + max_legal_value + 1u, max_legal_value + 2u, + std::numeric_limits<uint64_t>::max() - 1u, + std::numeric_limits<uint64_t>::max()}; + + for (uint64_t unsigned_val : illegal_values) { + T signed_val; + EXPECT_FALSE(ToSigned<T>(unsigned_val, &signed_val)) + << "Failed on " << static_cast<uint64_t>(unsigned_val) << "."; + } +} + +REGISTER_TYPED_TEST_SUITE_P(SignednessConversionTest, + CorrectlyConvertsLegalValues, + FailsOnConvertingIllegalValues); + +using Types = ::testing::Types<int8_t, int16_t, int32_t, int64_t>; + +INSTANTIATE_TYPED_TEST_SUITE_P(_, SignednessConversionTest, Types); + +} // namespace +} // namespace webrtc_event_logging diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc new file mode 100644 index 0000000000..5619827246 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc @@ -0,0 +1,814 @@ +/* + * 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 "logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h" + +#include <string.h> + +#include <vector> + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/network_state_predictor.h" +#include "api/rtp_headers.h" +#include "api/rtp_parameters.h" +#include "api/transport/network_types.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_ice_candidate_pair.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.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_remote_estimate.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_stream_config.h" +#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.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/common_header.h" +#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h" +#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h" +#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sdes.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" +#include "modules/rtp_rtcp/source/rtp_packet.h" +#include "rtc_base/checks.h" +#include "rtc_base/ignore_wundef.h" +#include "rtc_base/logging.h" + +// *.pb.h files are generated at build-time by the protobuf compiler. +RTC_PUSH_IGNORING_WUNDEF() +#ifdef WEBRTC_ANDROID_PLATFORM_BUILD +#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log.pb.h" +#else +#include "logging/rtc_event_log/rtc_event_log.pb.h" +#endif +RTC_POP_IGNORING_WUNDEF() + +namespace webrtc { + +namespace { +rtclog::DelayBasedBweUpdate::DetectorState ConvertDetectorState( + BandwidthUsage state) { + switch (state) { + case BandwidthUsage::kBwNormal: + return rtclog::DelayBasedBweUpdate::BWE_NORMAL; + case BandwidthUsage::kBwUnderusing: + return rtclog::DelayBasedBweUpdate::BWE_UNDERUSING; + case BandwidthUsage::kBwOverusing: + return rtclog::DelayBasedBweUpdate::BWE_OVERUSING; + case BandwidthUsage::kLast: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog::DelayBasedBweUpdate::BWE_NORMAL; +} + +rtclog::BweProbeResult::ResultType ConvertProbeResultType( + ProbeFailureReason failure_reason) { + switch (failure_reason) { + case ProbeFailureReason::kInvalidSendReceiveInterval: + return rtclog::BweProbeResult::INVALID_SEND_RECEIVE_INTERVAL; + case ProbeFailureReason::kInvalidSendReceiveRatio: + return rtclog::BweProbeResult::INVALID_SEND_RECEIVE_RATIO; + case ProbeFailureReason::kTimeout: + return rtclog::BweProbeResult::TIMEOUT; + case ProbeFailureReason::kLast: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog::BweProbeResult::SUCCESS; +} + +rtclog::VideoReceiveConfig_RtcpMode ConvertRtcpMode(RtcpMode rtcp_mode) { + switch (rtcp_mode) { + case RtcpMode::kCompound: + return rtclog::VideoReceiveConfig::RTCP_COMPOUND; + case RtcpMode::kReducedSize: + return rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE; + case RtcpMode::kOff: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog::VideoReceiveConfig::RTCP_COMPOUND; +} + +rtclog::IceCandidatePairConfig::IceCandidatePairConfigType +ConvertIceCandidatePairConfigType(IceCandidatePairConfigType type) { + switch (type) { + case IceCandidatePairConfigType::kAdded: + return rtclog::IceCandidatePairConfig::ADDED; + case IceCandidatePairConfigType::kUpdated: + return rtclog::IceCandidatePairConfig::UPDATED; + case IceCandidatePairConfigType::kDestroyed: + return rtclog::IceCandidatePairConfig::DESTROYED; + case IceCandidatePairConfigType::kSelected: + return rtclog::IceCandidatePairConfig::SELECTED; + case IceCandidatePairConfigType::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog::IceCandidatePairConfig::ADDED; +} + +rtclog::IceCandidatePairConfig::IceCandidateType ConvertIceCandidateType( + IceCandidateType type) { + switch (type) { + case IceCandidateType::kUnknown: + return rtclog::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE; + case IceCandidateType::kLocal: + return rtclog::IceCandidatePairConfig::LOCAL; + case IceCandidateType::kStun: + return rtclog::IceCandidatePairConfig::STUN; + case IceCandidateType::kPrflx: + return rtclog::IceCandidatePairConfig::PRFLX; + case IceCandidateType::kRelay: + return rtclog::IceCandidatePairConfig::RELAY; + case IceCandidateType::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE; +} + +rtclog::IceCandidatePairConfig::Protocol ConvertIceCandidatePairProtocol( + IceCandidatePairProtocol protocol) { + switch (protocol) { + case IceCandidatePairProtocol::kUnknown: + return rtclog::IceCandidatePairConfig::UNKNOWN_PROTOCOL; + case IceCandidatePairProtocol::kUdp: + return rtclog::IceCandidatePairConfig::UDP; + case IceCandidatePairProtocol::kTcp: + return rtclog::IceCandidatePairConfig::TCP; + case IceCandidatePairProtocol::kSsltcp: + return rtclog::IceCandidatePairConfig::SSLTCP; + case IceCandidatePairProtocol::kTls: + return rtclog::IceCandidatePairConfig::TLS; + case IceCandidatePairProtocol::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog::IceCandidatePairConfig::UNKNOWN_PROTOCOL; +} + +rtclog::IceCandidatePairConfig::AddressFamily +ConvertIceCandidatePairAddressFamily( + IceCandidatePairAddressFamily address_family) { + switch (address_family) { + case IceCandidatePairAddressFamily::kUnknown: + return rtclog::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY; + case IceCandidatePairAddressFamily::kIpv4: + return rtclog::IceCandidatePairConfig::IPV4; + case IceCandidatePairAddressFamily::kIpv6: + return rtclog::IceCandidatePairConfig::IPV6; + case IceCandidatePairAddressFamily::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY; +} + +rtclog::IceCandidatePairConfig::NetworkType ConvertIceCandidateNetworkType( + IceCandidateNetworkType network_type) { + switch (network_type) { + case IceCandidateNetworkType::kUnknown: + return rtclog::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE; + case IceCandidateNetworkType::kEthernet: + return rtclog::IceCandidatePairConfig::ETHERNET; + case IceCandidateNetworkType::kLoopback: + return rtclog::IceCandidatePairConfig::LOOPBACK; + case IceCandidateNetworkType::kWifi: + return rtclog::IceCandidatePairConfig::WIFI; + case IceCandidateNetworkType::kVpn: + return rtclog::IceCandidatePairConfig::VPN; + case IceCandidateNetworkType::kCellular: + return rtclog::IceCandidatePairConfig::CELLULAR; + case IceCandidateNetworkType::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE; +} + +rtclog::IceCandidatePairEvent::IceCandidatePairEventType +ConvertIceCandidatePairEventType(IceCandidatePairEventType type) { + switch (type) { + case IceCandidatePairEventType::kCheckSent: + return rtclog::IceCandidatePairEvent::CHECK_SENT; + case IceCandidatePairEventType::kCheckReceived: + return rtclog::IceCandidatePairEvent::CHECK_RECEIVED; + case IceCandidatePairEventType::kCheckResponseSent: + return rtclog::IceCandidatePairEvent::CHECK_RESPONSE_SENT; + case IceCandidatePairEventType::kCheckResponseReceived: + return rtclog::IceCandidatePairEvent::CHECK_RESPONSE_RECEIVED; + case IceCandidatePairEventType::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog::IceCandidatePairEvent::CHECK_SENT; +} + +} // namespace + +std::string RtcEventLogEncoderLegacy::EncodeLogStart(int64_t timestamp_us, + int64_t utc_time_us) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(timestamp_us); + rtclog_event.set_type(rtclog::Event::LOG_START); + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeLogEnd(int64_t timestamp_us) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(timestamp_us); + rtclog_event.set_type(rtclog::Event::LOG_END); + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeBatch( + std::deque<std::unique_ptr<RtcEvent>>::const_iterator begin, + std::deque<std::unique_ptr<RtcEvent>>::const_iterator end) { + std::string encoded_output; + for (auto it = begin; it != end; ++it) { + // TODO(terelius): Can we avoid the slight inefficiency of reallocating the + // string? + RTC_CHECK(it->get() != nullptr); + encoded_output += Encode(**it); + } + return encoded_output; +} + +std::string RtcEventLogEncoderLegacy::Encode(const RtcEvent& event) { + switch (event.GetType()) { + case RtcEvent::Type::AudioNetworkAdaptation: { + auto& rtc_event = + static_cast<const RtcEventAudioNetworkAdaptation&>(event); + return EncodeAudioNetworkAdaptation(rtc_event); + } + + case RtcEvent::Type::AlrStateEvent: { + auto& rtc_event = static_cast<const RtcEventAlrState&>(event); + return EncodeAlrState(rtc_event); + } + + case RtcEvent::Type::AudioPlayout: { + auto& rtc_event = static_cast<const RtcEventAudioPlayout&>(event); + return EncodeAudioPlayout(rtc_event); + } + + case RtcEvent::Type::AudioReceiveStreamConfig: { + auto& rtc_event = + static_cast<const RtcEventAudioReceiveStreamConfig&>(event); + return EncodeAudioReceiveStreamConfig(rtc_event); + } + + case RtcEvent::Type::AudioSendStreamConfig: { + auto& rtc_event = + static_cast<const RtcEventAudioSendStreamConfig&>(event); + return EncodeAudioSendStreamConfig(rtc_event); + } + + case RtcEvent::Type::BweUpdateDelayBased: { + auto& rtc_event = static_cast<const RtcEventBweUpdateDelayBased&>(event); + return EncodeBweUpdateDelayBased(rtc_event); + } + + case RtcEvent::Type::BweUpdateLossBased: { + auto& rtc_event = static_cast<const RtcEventBweUpdateLossBased&>(event); + return EncodeBweUpdateLossBased(rtc_event); + } + + case RtcEvent::Type::DtlsTransportState: { + return ""; + } + + case RtcEvent::Type::DtlsWritableState: { + return ""; + } + + case RtcEvent::Type::IceCandidatePairConfig: { + auto& rtc_event = + static_cast<const RtcEventIceCandidatePairConfig&>(event); + return EncodeIceCandidatePairConfig(rtc_event); + } + + case RtcEvent::Type::IceCandidatePairEvent: { + auto& rtc_event = static_cast<const RtcEventIceCandidatePair&>(event); + return EncodeIceCandidatePairEvent(rtc_event); + } + + case RtcEvent::Type::ProbeClusterCreated: { + auto& rtc_event = static_cast<const RtcEventProbeClusterCreated&>(event); + return EncodeProbeClusterCreated(rtc_event); + } + + case RtcEvent::Type::ProbeResultFailure: { + auto& rtc_event = static_cast<const RtcEventProbeResultFailure&>(event); + return EncodeProbeResultFailure(rtc_event); + } + + case RtcEvent::Type::ProbeResultSuccess: { + auto& rtc_event = static_cast<const RtcEventProbeResultSuccess&>(event); + return EncodeProbeResultSuccess(rtc_event); + } + + case RtcEvent::Type::RemoteEstimateEvent: { + auto& rtc_event = static_cast<const RtcEventRemoteEstimate&>(event); + return EncodeRemoteEstimate(rtc_event); + } + + case RtcEvent::Type::RtcpPacketIncoming: { + auto& rtc_event = static_cast<const RtcEventRtcpPacketIncoming&>(event); + return EncodeRtcpPacketIncoming(rtc_event); + } + + case RtcEvent::Type::RtcpPacketOutgoing: { + auto& rtc_event = static_cast<const RtcEventRtcpPacketOutgoing&>(event); + return EncodeRtcpPacketOutgoing(rtc_event); + } + + case RtcEvent::Type::RtpPacketIncoming: { + auto& rtc_event = static_cast<const RtcEventRtpPacketIncoming&>(event); + return EncodeRtpPacketIncoming(rtc_event); + } + + case RtcEvent::Type::RtpPacketOutgoing: { + auto& rtc_event = static_cast<const RtcEventRtpPacketOutgoing&>(event); + return EncodeRtpPacketOutgoing(rtc_event); + } + + case RtcEvent::Type::VideoReceiveStreamConfig: { + auto& rtc_event = + static_cast<const RtcEventVideoReceiveStreamConfig&>(event); + return EncodeVideoReceiveStreamConfig(rtc_event); + } + + case RtcEvent::Type::VideoSendStreamConfig: { + auto& rtc_event = + static_cast<const RtcEventVideoSendStreamConfig&>(event); + return EncodeVideoSendStreamConfig(rtc_event); + } + case RtcEvent::Type::BeginV3Log: + case RtcEvent::Type::EndV3Log: + // These special events are written as part of starting + // and stopping the log, and only as part of version 3 of the format. + RTC_DCHECK_NOTREACHED(); + break; + case RtcEvent::Type::FakeEvent: + // Fake event used for unit test. + RTC_DCHECK_NOTREACHED(); + break; + case RtcEvent::Type::RouteChangeEvent: + case RtcEvent::Type::GenericPacketReceived: + case RtcEvent::Type::GenericPacketSent: + case RtcEvent::Type::GenericAckReceived: + case RtcEvent::Type::FrameDecoded: + case RtcEvent::Type::NetEqSetMinimumDelay: + // These are unsupported in the old format, but shouldn't crash. + return ""; + } + + int event_type = static_cast<int>(event.GetType()); + RTC_DCHECK_NOTREACHED() << "Unknown event type (" << event_type << ")"; + return ""; +} + +std::string RtcEventLogEncoderLegacy::EncodeAlrState( + const RtcEventAlrState& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::ALR_STATE_EVENT); + + auto* alr_state = rtclog_event.mutable_alr_state(); + alr_state->set_in_alr(event.in_alr()); + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeAudioNetworkAdaptation( + const RtcEventAudioNetworkAdaptation& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT); + + auto* audio_network_adaptation = + rtclog_event.mutable_audio_network_adaptation(); + if (event.config().bitrate_bps) + audio_network_adaptation->set_bitrate_bps(*event.config().bitrate_bps); + if (event.config().frame_length_ms) + audio_network_adaptation->set_frame_length_ms( + *event.config().frame_length_ms); + if (event.config().uplink_packet_loss_fraction) { + audio_network_adaptation->set_uplink_packet_loss_fraction( + *event.config().uplink_packet_loss_fraction); + } + if (event.config().enable_fec) + audio_network_adaptation->set_enable_fec(*event.config().enable_fec); + if (event.config().enable_dtx) + audio_network_adaptation->set_enable_dtx(*event.config().enable_dtx); + if (event.config().num_channels) + audio_network_adaptation->set_num_channels(*event.config().num_channels); + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeAudioPlayout( + const RtcEventAudioPlayout& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::AUDIO_PLAYOUT_EVENT); + + auto* playout_event = rtclog_event.mutable_audio_playout_event(); + playout_event->set_local_ssrc(event.ssrc()); + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeAudioReceiveStreamConfig( + const RtcEventAudioReceiveStreamConfig& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT); + + rtclog::AudioReceiveConfig* receiver_config = + rtclog_event.mutable_audio_receiver_config(); + receiver_config->set_remote_ssrc(event.config().remote_ssrc); + receiver_config->set_local_ssrc(event.config().local_ssrc); + + for (const auto& e : event.config().rtp_extensions) { + rtclog::RtpHeaderExtension* extension = + receiver_config->add_header_extensions(); + extension->set_name(e.uri); + extension->set_id(e.id); + } + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeAudioSendStreamConfig( + const RtcEventAudioSendStreamConfig& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::AUDIO_SENDER_CONFIG_EVENT); + + rtclog::AudioSendConfig* sender_config = + rtclog_event.mutable_audio_sender_config(); + + sender_config->set_ssrc(event.config().local_ssrc); + + for (const auto& e : event.config().rtp_extensions) { + rtclog::RtpHeaderExtension* extension = + sender_config->add_header_extensions(); + extension->set_name(e.uri); + extension->set_id(e.id); + } + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeBweUpdateDelayBased( + const RtcEventBweUpdateDelayBased& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::DELAY_BASED_BWE_UPDATE); + + auto* bwe_event = rtclog_event.mutable_delay_based_bwe_update(); + bwe_event->set_bitrate_bps(event.bitrate_bps()); + bwe_event->set_detector_state(ConvertDetectorState(event.detector_state())); + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeBweUpdateLossBased( + const RtcEventBweUpdateLossBased& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::LOSS_BASED_BWE_UPDATE); + + auto* bwe_event = rtclog_event.mutable_loss_based_bwe_update(); + bwe_event->set_bitrate_bps(event.bitrate_bps()); + bwe_event->set_fraction_loss(event.fraction_loss()); + bwe_event->set_total_packets(event.total_packets()); + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeIceCandidatePairConfig( + const RtcEventIceCandidatePairConfig& event) { + rtclog::Event encoded_rtc_event; + encoded_rtc_event.set_timestamp_us(event.timestamp_us()); + encoded_rtc_event.set_type(rtclog::Event::ICE_CANDIDATE_PAIR_CONFIG); + + auto* encoded_ice_event = + encoded_rtc_event.mutable_ice_candidate_pair_config(); + encoded_ice_event->set_config_type( + ConvertIceCandidatePairConfigType(event.type())); + encoded_ice_event->set_candidate_pair_id(event.candidate_pair_id()); + const auto& desc = event.candidate_pair_desc(); + encoded_ice_event->set_local_candidate_type( + ConvertIceCandidateType(desc.local_candidate_type)); + encoded_ice_event->set_local_relay_protocol( + ConvertIceCandidatePairProtocol(desc.local_relay_protocol)); + encoded_ice_event->set_local_network_type( + ConvertIceCandidateNetworkType(desc.local_network_type)); + encoded_ice_event->set_local_address_family( + ConvertIceCandidatePairAddressFamily(desc.local_address_family)); + encoded_ice_event->set_remote_candidate_type( + ConvertIceCandidateType(desc.remote_candidate_type)); + encoded_ice_event->set_remote_address_family( + ConvertIceCandidatePairAddressFamily(desc.remote_address_family)); + encoded_ice_event->set_candidate_pair_protocol( + ConvertIceCandidatePairProtocol(desc.candidate_pair_protocol)); + return Serialize(&encoded_rtc_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeIceCandidatePairEvent( + const RtcEventIceCandidatePair& event) { + rtclog::Event encoded_rtc_event; + encoded_rtc_event.set_timestamp_us(event.timestamp_us()); + encoded_rtc_event.set_type(rtclog::Event::ICE_CANDIDATE_PAIR_EVENT); + + auto* encoded_ice_event = + encoded_rtc_event.mutable_ice_candidate_pair_event(); + encoded_ice_event->set_event_type( + ConvertIceCandidatePairEventType(event.type())); + encoded_ice_event->set_candidate_pair_id(event.candidate_pair_id()); + return Serialize(&encoded_rtc_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeProbeClusterCreated( + const RtcEventProbeClusterCreated& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT); + + auto* probe_cluster = rtclog_event.mutable_probe_cluster(); + probe_cluster->set_id(event.id()); + probe_cluster->set_bitrate_bps(event.bitrate_bps()); + probe_cluster->set_min_packets(event.min_probes()); + probe_cluster->set_min_bytes(event.min_bytes()); + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeProbeResultFailure( + const RtcEventProbeResultFailure& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::BWE_PROBE_RESULT_EVENT); + + auto* probe_result = rtclog_event.mutable_probe_result(); + probe_result->set_id(event.id()); + probe_result->set_result(ConvertProbeResultType(event.failure_reason())); + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeProbeResultSuccess( + const RtcEventProbeResultSuccess& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::BWE_PROBE_RESULT_EVENT); + + auto* probe_result = rtclog_event.mutable_probe_result(); + probe_result->set_id(event.id()); + probe_result->set_result(rtclog::BweProbeResult::SUCCESS); + probe_result->set_bitrate_bps(event.bitrate_bps()); + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeRemoteEstimate( + const RtcEventRemoteEstimate& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::REMOTE_ESTIMATE); + + auto* remote_estimate = rtclog_event.mutable_remote_estimate(); + if (event.link_capacity_lower_.IsFinite()) + remote_estimate->set_link_capacity_lower_kbps( + event.link_capacity_lower_.kbps<uint32_t>()); + if (event.link_capacity_upper_.IsFinite()) + remote_estimate->set_link_capacity_upper_kbps( + event.link_capacity_upper_.kbps<uint32_t>()); + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeRtcpPacketIncoming( + const RtcEventRtcpPacketIncoming& event) { + return EncodeRtcpPacket(event.timestamp_us(), event.packet(), true); +} + +std::string RtcEventLogEncoderLegacy::EncodeRtcpPacketOutgoing( + const RtcEventRtcpPacketOutgoing& event) { + return EncodeRtcpPacket(event.timestamp_us(), event.packet(), false); +} + +std::string RtcEventLogEncoderLegacy::EncodeRtpPacketIncoming( + const RtcEventRtpPacketIncoming& event) { + return EncodeRtpPacket(event.timestamp_us(), event.RawHeader(), + event.packet_length(), PacedPacketInfo::kNotAProbe, + true); +} + +std::string RtcEventLogEncoderLegacy::EncodeRtpPacketOutgoing( + const RtcEventRtpPacketOutgoing& event) { + return EncodeRtpPacket(event.timestamp_us(), event.RawHeader(), + event.packet_length(), event.probe_cluster_id(), + false); +} + +std::string RtcEventLogEncoderLegacy::EncodeVideoReceiveStreamConfig( + const RtcEventVideoReceiveStreamConfig& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT); + + rtclog::VideoReceiveConfig* receiver_config = + rtclog_event.mutable_video_receiver_config(); + receiver_config->set_remote_ssrc(event.config().remote_ssrc); + receiver_config->set_local_ssrc(event.config().local_ssrc); + + // TODO(perkj): Add field for rsid. + receiver_config->set_rtcp_mode(ConvertRtcpMode(event.config().rtcp_mode)); + receiver_config->set_remb(event.config().remb); + + for (const auto& e : event.config().rtp_extensions) { + rtclog::RtpHeaderExtension* extension = + receiver_config->add_header_extensions(); + extension->set_name(e.uri); + extension->set_id(e.id); + } + + for (const auto& d : event.config().codecs) { + rtclog::DecoderConfig* decoder = receiver_config->add_decoders(); + decoder->set_name(d.payload_name); + decoder->set_payload_type(d.payload_type); + if (d.rtx_payload_type != 0) { + rtclog::RtxMap* rtx = receiver_config->add_rtx_map(); + rtx->set_payload_type(d.payload_type); + rtx->mutable_config()->set_rtx_ssrc(event.config().rtx_ssrc); + rtx->mutable_config()->set_rtx_payload_type(d.rtx_payload_type); + } + } + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeVideoSendStreamConfig( + const RtcEventVideoSendStreamConfig& event) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(event.timestamp_us()); + rtclog_event.set_type(rtclog::Event::VIDEO_SENDER_CONFIG_EVENT); + + rtclog::VideoSendConfig* sender_config = + rtclog_event.mutable_video_sender_config(); + + // TODO(perkj): rtclog::VideoSendConfig should only contain one SSRC. + sender_config->add_ssrcs(event.config().local_ssrc); + if (event.config().rtx_ssrc != 0) { + sender_config->add_rtx_ssrcs(event.config().rtx_ssrc); + } + + for (const auto& e : event.config().rtp_extensions) { + rtclog::RtpHeaderExtension* extension = + sender_config->add_header_extensions(); + extension->set_name(e.uri); + extension->set_id(e.id); + } + + // TODO(perkj): rtclog::VideoSendConfig should contain many possible codec + // configurations. + for (const auto& codec : event.config().codecs) { + sender_config->set_rtx_payload_type(codec.rtx_payload_type); + rtclog::EncoderConfig* encoder = sender_config->mutable_encoder(); + encoder->set_name(codec.payload_name); + encoder->set_payload_type(codec.payload_type); + + if (event.config().codecs.size() > 1) { + RTC_LOG(LS_WARNING) + << "LogVideoSendStreamConfig currently only supports one " + "codec. Logging codec :" + << codec.payload_name; + break; + } + } + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeRtcpPacket( + int64_t timestamp_us, + const rtc::Buffer& packet, + bool is_incoming) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(timestamp_us); + rtclog_event.set_type(rtclog::Event::RTCP_EVENT); + rtclog_event.mutable_rtcp_packet()->set_incoming(is_incoming); + + rtcp::CommonHeader header; + const uint8_t* block_begin = packet.data(); + const uint8_t* packet_end = packet.data() + packet.size(); + std::vector<uint8_t> buffer(packet.size()); + uint32_t buffer_length = 0; + while (block_begin < packet_end) { + if (!header.Parse(block_begin, packet_end - block_begin)) { + break; // Incorrect message header. + } + const uint8_t* next_block = header.NextPacket(); + uint32_t block_size = next_block - block_begin; + switch (header.type()) { + case rtcp::Bye::kPacketType: + case rtcp::ExtendedReports::kPacketType: + case rtcp::Psfb::kPacketType: + case rtcp::ReceiverReport::kPacketType: + case rtcp::Rtpfb::kPacketType: + case rtcp::SenderReport::kPacketType: + // We log sender reports, receiver reports, bye messages, third-party + // loss reports, payload-specific feedback and extended reports. + memcpy(buffer.data() + buffer_length, block_begin, block_size); + buffer_length += block_size; + break; + case rtcp::App::kPacketType: + case rtcp::Sdes::kPacketType: + default: + // We don't log sender descriptions, application defined messages + // or message blocks of unknown type. + break; + } + + block_begin += block_size; + } + rtclog_event.mutable_rtcp_packet()->set_packet_data(buffer.data(), + buffer_length); + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::EncodeRtpPacket( + int64_t timestamp_us, + rtc::ArrayView<const uint8_t> header, + size_t packet_length, + int probe_cluster_id, + bool is_incoming) { + rtclog::Event rtclog_event; + rtclog_event.set_timestamp_us(timestamp_us); + rtclog_event.set_type(rtclog::Event::RTP_EVENT); + + rtclog_event.mutable_rtp_packet()->set_incoming(is_incoming); + rtclog_event.mutable_rtp_packet()->set_packet_length(packet_length); + rtclog_event.mutable_rtp_packet()->set_header(header.data(), header.size()); + if (probe_cluster_id != PacedPacketInfo::kNotAProbe) { + RTC_DCHECK(!is_incoming); + rtclog_event.mutable_rtp_packet()->set_probe_cluster_id(probe_cluster_id); + } + + return Serialize(&rtclog_event); +} + +std::string RtcEventLogEncoderLegacy::Serialize(rtclog::Event* event) { + // Even though we're only serializing a single event during this call, what + // we intend to get is a list of events, with a tag and length preceding + // each actual event. To produce that, we serialize a list of a single event. + // If we later concatenate several results from this function, the result will + // be a proper concatenation of all those events. + + rtclog::EventStream event_stream; + event_stream.add_stream(); + + // As a tweak, we swap the new event into the event-stream, write that to + // file, then swap back. This saves on some copying, while making sure that + // the caller wouldn't be surprised by Serialize() modifying the object. + rtclog::Event* output_event = event_stream.mutable_stream(0); + output_event->Swap(event); + + std::string output_string = event_stream.SerializeAsString(); + RTC_DCHECK(!output_string.empty()); + + // When the function returns, the original Event will be unchanged. + output_event->Swap(event); + + return output_string; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h new file mode 100644 index 0000000000..33c530789b --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h @@ -0,0 +1,110 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_LEGACY_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_LEGACY_H_ + +#include <deque> +#include <memory> +#include <string> + +#include "api/array_view.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder.h" +#include "rtc_base/buffer.h" + +namespace webrtc { + +namespace rtclog { +class Event; // Auto-generated from protobuf. +} // namespace rtclog + +class RtcEventAlrState; +class RtcEventAudioNetworkAdaptation; +class RtcEventAudioPlayout; +class RtcEventAudioReceiveStreamConfig; +class RtcEventAudioSendStreamConfig; +class RtcEventBweUpdateDelayBased; +class RtcEventBweUpdateLossBased; +class RtcEventIceCandidatePairConfig; +class RtcEventIceCandidatePair; +class RtcEventLoggingStarted; +class RtcEventLoggingStopped; +class RtcEventProbeClusterCreated; +class RtcEventProbeResultFailure; +class RtcEventProbeResultSuccess; +class RtcEventRemoteEstimate; +class RtcEventRtcpPacketIncoming; +class RtcEventRtcpPacketOutgoing; +class RtcEventRtpPacketIncoming; +class RtcEventRtpPacketOutgoing; +class RtcEventVideoReceiveStreamConfig; +class RtcEventVideoSendStreamConfig; +class RtpPacket; + +class RtcEventLogEncoderLegacy final : public RtcEventLogEncoder { + public: + ~RtcEventLogEncoderLegacy() override = default; + + std::string EncodeLogStart(int64_t timestamp_us, + int64_t utc_time_us) override; + std::string EncodeLogEnd(int64_t timestamp_us) override; + + std::string EncodeBatch( + std::deque<std::unique_ptr<RtcEvent>>::const_iterator begin, + std::deque<std::unique_ptr<RtcEvent>>::const_iterator end) override; + + private: + std::string Encode(const RtcEvent& event); + // Encoding entry-point for the various RtcEvent subclasses. + std::string EncodeAlrState(const RtcEventAlrState& event); + std::string EncodeAudioNetworkAdaptation( + const RtcEventAudioNetworkAdaptation& event); + std::string EncodeAudioPlayout(const RtcEventAudioPlayout& event); + std::string EncodeAudioReceiveStreamConfig( + const RtcEventAudioReceiveStreamConfig& event); + std::string EncodeAudioSendStreamConfig( + const RtcEventAudioSendStreamConfig& event); + std::string EncodeBweUpdateDelayBased( + const RtcEventBweUpdateDelayBased& event); + std::string EncodeBweUpdateLossBased(const RtcEventBweUpdateLossBased& event); + std::string EncodeIceCandidatePairConfig( + const RtcEventIceCandidatePairConfig& event); + std::string EncodeIceCandidatePairEvent( + const RtcEventIceCandidatePair& event); + std::string EncodeProbeClusterCreated( + const RtcEventProbeClusterCreated& event); + std::string EncodeProbeResultFailure(const RtcEventProbeResultFailure& event); + std::string EncodeProbeResultSuccess(const RtcEventProbeResultSuccess&); + std::string EncodeRemoteEstimate(const RtcEventRemoteEstimate& event); + std::string EncodeRtcpPacketIncoming(const RtcEventRtcpPacketIncoming& event); + std::string EncodeRtcpPacketOutgoing(const RtcEventRtcpPacketOutgoing& event); + std::string EncodeRtpPacketIncoming(const RtcEventRtpPacketIncoming& event); + std::string EncodeRtpPacketOutgoing(const RtcEventRtpPacketOutgoing& event); + std::string EncodeVideoReceiveStreamConfig( + const RtcEventVideoReceiveStreamConfig& event); + std::string EncodeVideoSendStreamConfig( + const RtcEventVideoSendStreamConfig& event); + + // RTCP/RTP are handled similarly for incoming/outgoing. + std::string EncodeRtcpPacket(int64_t timestamp_us, + const rtc::Buffer& packet, + bool is_incoming); + std::string EncodeRtpPacket(int64_t timestamp_us, + rtc::ArrayView<const uint8_t> header, + size_t packet_length, + int probe_cluster_id, + bool is_incoming); + + std::string Serialize(rtclog::Event* event); +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_LEGACY_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc new file mode 100644 index 0000000000..2c9e42e064 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc @@ -0,0 +1,1970 @@ +/* + * 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 "logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h" + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/network_state_predictor.h" +#include "logging/rtc_event_log/dependency_descriptor_encoder_decoder.h" +#include "logging/rtc_event_log/encoder/blob_encoding.h" +#include "logging/rtc_event_log/encoder/delta_encoding.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.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_dtls_transport_state.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" +#include "logging/rtc_event_log/events/rtc_event_frame_decoded.h" +#include "logging/rtc_event_log/events/rtc_event_generic_ack_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_sent.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" +#include "logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.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_remote_estimate.h" +#include "logging/rtc_event_log/events/rtc_event_route_change.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_stream_config.h" +#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" +#include "modules/rtp_rtcp/include/rtp_cvo.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.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/common_header.h" +#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h" +#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h" +#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sdes.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" +#include "modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet.h" +#include "rtc_base/checks.h" +#include "rtc_base/ignore_wundef.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/field_trial.h" + +// *.pb.h files are generated at build-time by the protobuf compiler. +RTC_PUSH_IGNORING_WUNDEF() +#ifdef WEBRTC_ANDROID_PLATFORM_BUILD +#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log2.pb.h" +#else +#include "logging/rtc_event_log/rtc_event_log2.pb.h" +#endif +RTC_POP_IGNORING_WUNDEF() + +using webrtc_event_logging::ToUnsigned; + +namespace webrtc { + +namespace { +rtclog2::DelayBasedBweUpdates::DetectorState ConvertToProtoFormat( + BandwidthUsage state) { + switch (state) { + case BandwidthUsage::kBwNormal: + return rtclog2::DelayBasedBweUpdates::BWE_NORMAL; + case BandwidthUsage::kBwUnderusing: + return rtclog2::DelayBasedBweUpdates::BWE_UNDERUSING; + case BandwidthUsage::kBwOverusing: + return rtclog2::DelayBasedBweUpdates::BWE_OVERUSING; + case BandwidthUsage::kLast: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::DelayBasedBweUpdates::BWE_UNKNOWN_STATE; +} + +rtclog2::FrameDecodedEvents::Codec ConvertToProtoFormat(VideoCodecType codec) { + switch (codec) { + case VideoCodecType::kVideoCodecGeneric: + return rtclog2::FrameDecodedEvents::CODEC_GENERIC; + case VideoCodecType::kVideoCodecVP8: + return rtclog2::FrameDecodedEvents::CODEC_VP8; + case VideoCodecType::kVideoCodecVP9: + return rtclog2::FrameDecodedEvents::CODEC_VP9; + case VideoCodecType::kVideoCodecAV1: + return rtclog2::FrameDecodedEvents::CODEC_AV1; + case VideoCodecType::kVideoCodecH264: + return rtclog2::FrameDecodedEvents::CODEC_H264; + case VideoCodecType::kVideoCodecMultiplex: + // This codec type is afaik not used. + return rtclog2::FrameDecodedEvents::CODEC_UNKNOWN; + case VideoCodecType::kVideoCodecH265: + return rtclog2::FrameDecodedEvents::CODEC_H265; + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::FrameDecodedEvents::CODEC_UNKNOWN; +} + +rtclog2::BweProbeResultFailure::FailureReason ConvertToProtoFormat( + ProbeFailureReason failure_reason) { + switch (failure_reason) { + case ProbeFailureReason::kInvalidSendReceiveInterval: + return rtclog2::BweProbeResultFailure::INVALID_SEND_RECEIVE_INTERVAL; + case ProbeFailureReason::kInvalidSendReceiveRatio: + return rtclog2::BweProbeResultFailure::INVALID_SEND_RECEIVE_RATIO; + case ProbeFailureReason::kTimeout: + return rtclog2::BweProbeResultFailure::TIMEOUT; + case ProbeFailureReason::kLast: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::BweProbeResultFailure::UNKNOWN; +} + +// Returns true if there are recognized extensions that we should log +// and false if there are no extensions or all extensions are types we don't +// log. The protobuf representation of the header configs is written to +// `proto_config`. +bool ConvertToProtoFormat(const std::vector<RtpExtension>& extensions, + rtclog2::RtpHeaderExtensionConfig* proto_config) { + size_t unknown_extensions = 0; + for (auto& extension : extensions) { + if (extension.uri == RtpExtension::kAudioLevelUri) { + proto_config->set_audio_level_id(extension.id); + } else if (extension.uri == RtpExtension::kTimestampOffsetUri) { + proto_config->set_transmission_time_offset_id(extension.id); + } else if (extension.uri == RtpExtension::kAbsSendTimeUri) { + proto_config->set_absolute_send_time_id(extension.id); + } else if (extension.uri == RtpExtension::kTransportSequenceNumberUri) { + proto_config->set_transport_sequence_number_id(extension.id); + } else if (extension.uri == RtpExtension::kVideoRotationUri) { + proto_config->set_video_rotation_id(extension.id); + } else if (extension.uri == RtpExtension::kDependencyDescriptorUri) { + proto_config->set_dependency_descriptor_id(extension.id); + } else { + ++unknown_extensions; + } + } + return unknown_extensions < extensions.size(); +} + +rtclog2::DtlsTransportStateEvent::DtlsTransportState ConvertToProtoFormat( + webrtc::DtlsTransportState state) { + switch (state) { + case webrtc::DtlsTransportState::kNew: + return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_NEW; + case webrtc::DtlsTransportState::kConnecting: + return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CONNECTING; + case webrtc::DtlsTransportState::kConnected: + return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CONNECTED; + case webrtc::DtlsTransportState::kClosed: + return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CLOSED; + case webrtc::DtlsTransportState::kFailed: + return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_FAILED; + case webrtc::DtlsTransportState::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::DtlsTransportStateEvent::UNKNOWN_DTLS_TRANSPORT_STATE; +} + +rtclog2::IceCandidatePairConfig::IceCandidatePairConfigType +ConvertToProtoFormat(IceCandidatePairConfigType type) { + switch (type) { + case IceCandidatePairConfigType::kAdded: + return rtclog2::IceCandidatePairConfig::ADDED; + case IceCandidatePairConfigType::kUpdated: + return rtclog2::IceCandidatePairConfig::UPDATED; + case IceCandidatePairConfigType::kDestroyed: + return rtclog2::IceCandidatePairConfig::DESTROYED; + case IceCandidatePairConfigType::kSelected: + return rtclog2::IceCandidatePairConfig::SELECTED; + case IceCandidatePairConfigType::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::IceCandidatePairConfig::UNKNOWN_CONFIG_TYPE; +} + +rtclog2::IceCandidatePairConfig::IceCandidateType ConvertToProtoFormat( + IceCandidateType type) { + switch (type) { + case IceCandidateType::kUnknown: + return rtclog2::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE; + case IceCandidateType::kLocal: + return rtclog2::IceCandidatePairConfig::LOCAL; + case IceCandidateType::kStun: + return rtclog2::IceCandidatePairConfig::STUN; + case IceCandidateType::kPrflx: + return rtclog2::IceCandidatePairConfig::PRFLX; + case IceCandidateType::kRelay: + return rtclog2::IceCandidatePairConfig::RELAY; + case IceCandidateType::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE; +} + +rtclog2::IceCandidatePairConfig::Protocol ConvertToProtoFormat( + IceCandidatePairProtocol protocol) { + switch (protocol) { + case IceCandidatePairProtocol::kUnknown: + return rtclog2::IceCandidatePairConfig::UNKNOWN_PROTOCOL; + case IceCandidatePairProtocol::kUdp: + return rtclog2::IceCandidatePairConfig::UDP; + case IceCandidatePairProtocol::kTcp: + return rtclog2::IceCandidatePairConfig::TCP; + case IceCandidatePairProtocol::kSsltcp: + return rtclog2::IceCandidatePairConfig::SSLTCP; + case IceCandidatePairProtocol::kTls: + return rtclog2::IceCandidatePairConfig::TLS; + case IceCandidatePairProtocol::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::IceCandidatePairConfig::UNKNOWN_PROTOCOL; +} + +rtclog2::IceCandidatePairConfig::AddressFamily ConvertToProtoFormat( + IceCandidatePairAddressFamily address_family) { + switch (address_family) { + case IceCandidatePairAddressFamily::kUnknown: + return rtclog2::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY; + case IceCandidatePairAddressFamily::kIpv4: + return rtclog2::IceCandidatePairConfig::IPV4; + case IceCandidatePairAddressFamily::kIpv6: + return rtclog2::IceCandidatePairConfig::IPV6; + case IceCandidatePairAddressFamily::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY; +} + +rtclog2::IceCandidatePairConfig::NetworkType ConvertToProtoFormat( + IceCandidateNetworkType network_type) { + switch (network_type) { + case IceCandidateNetworkType::kUnknown: + return rtclog2::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE; + case IceCandidateNetworkType::kEthernet: + return rtclog2::IceCandidatePairConfig::ETHERNET; + case IceCandidateNetworkType::kLoopback: + return rtclog2::IceCandidatePairConfig::LOOPBACK; + case IceCandidateNetworkType::kWifi: + return rtclog2::IceCandidatePairConfig::WIFI; + case IceCandidateNetworkType::kVpn: + return rtclog2::IceCandidatePairConfig::VPN; + case IceCandidateNetworkType::kCellular: + return rtclog2::IceCandidatePairConfig::CELLULAR; + case IceCandidateNetworkType::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE; +} + +rtclog2::IceCandidatePairEvent::IceCandidatePairEventType ConvertToProtoFormat( + IceCandidatePairEventType type) { + switch (type) { + case IceCandidatePairEventType::kCheckSent: + return rtclog2::IceCandidatePairEvent::CHECK_SENT; + case IceCandidatePairEventType::kCheckReceived: + return rtclog2::IceCandidatePairEvent::CHECK_RECEIVED; + case IceCandidatePairEventType::kCheckResponseSent: + return rtclog2::IceCandidatePairEvent::CHECK_RESPONSE_SENT; + case IceCandidatePairEventType::kCheckResponseReceived: + return rtclog2::IceCandidatePairEvent::CHECK_RESPONSE_RECEIVED; + case IceCandidatePairEventType::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::IceCandidatePairEvent::UNKNOWN_CHECK_TYPE; +} + +// Copies all RTCP blocks except APP, SDES and unknown from `packet` to +// `buffer`. `buffer` must have space for at least `packet.size()` bytes. +size_t RemoveNonAllowlistedRtcpBlocks(const rtc::Buffer& packet, + uint8_t* buffer) { + RTC_DCHECK(buffer != nullptr); + rtcp::CommonHeader header; + const uint8_t* block_begin = packet.data(); + const uint8_t* packet_end = packet.data() + packet.size(); + size_t buffer_length = 0; + while (block_begin < packet_end) { + if (!header.Parse(block_begin, packet_end - block_begin)) { + break; // Incorrect message header. + } + const uint8_t* next_block = header.NextPacket(); + RTC_DCHECK_GT(next_block, block_begin); + RTC_DCHECK_LE(next_block, packet_end); + size_t block_size = next_block - block_begin; + switch (header.type()) { + case rtcp::Bye::kPacketType: + case rtcp::ExtendedReports::kPacketType: + case rtcp::Psfb::kPacketType: + case rtcp::ReceiverReport::kPacketType: + case rtcp::Rtpfb::kPacketType: + case rtcp::SenderReport::kPacketType: + // We log sender reports, receiver reports, bye messages, third-party + // loss reports, payload-specific feedback and extended reports. + // TODO(terelius): As an optimization, don't copy anything if all blocks + // in the packet are allowlisted types. + memcpy(buffer + buffer_length, block_begin, block_size); + buffer_length += block_size; + break; + case rtcp::App::kPacketType: + case rtcp::Sdes::kPacketType: + default: + // We don't log sender descriptions, application defined messages + // or message blocks of unknown type. + break; + } + + block_begin += block_size; + } + return buffer_length; +} + +template <typename EventType, typename ProtoType> +void EncodeRtcpPacket(rtc::ArrayView<const EventType*> batch, + ProtoType* proto_batch) { + if (batch.empty()) { + return; + } + + // Base event + const EventType* const base_event = batch[0]; + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + { + std::vector<uint8_t> buffer(base_event->packet().size()); + size_t buffer_length = + RemoveNonAllowlistedRtcpBlocks(base_event->packet(), buffer.data()); + proto_batch->set_raw_packet(buffer.data(), buffer_length); + } + + if (batch.size() == 1) { + return; + } + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // raw_packet + std::vector<std::string> scrubed_packets(batch.size() - 1); + for (size_t i = 0; i < scrubed_packets.size(); ++i) { + const EventType* event = batch[i + 1]; + scrubed_packets[i].resize(event->packet().size()); + static_assert(sizeof(std::string::value_type) == sizeof(uint8_t), ""); + const size_t buffer_length = RemoveNonAllowlistedRtcpBlocks( + event->packet(), reinterpret_cast<uint8_t*>(&scrubed_packets[i][0])); + if (buffer_length < event->packet().size()) { + scrubed_packets[i].resize(buffer_length); + } + } + proto_batch->set_raw_packet_blobs(EncodeBlobs(scrubed_packets)); +} + +template <typename EventType, typename ProtoType> +void EncodeRtpPacket(const std::vector<const EventType*>& batch, + ProtoType* proto_batch) { + if (batch.empty()) { + return; + } + + // Base event + const EventType* const base_event = batch[0]; + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_marker(base_event->Marker()); + // TODO(terelius): Is payload type needed? + proto_batch->set_payload_type(base_event->PayloadType()); + proto_batch->set_sequence_number(base_event->SequenceNumber()); + proto_batch->set_rtp_timestamp(base_event->Timestamp()); + proto_batch->set_ssrc(base_event->Ssrc()); + proto_batch->set_payload_size(base_event->payload_length()); + proto_batch->set_header_size(base_event->header_length()); + proto_batch->set_padding_size(base_event->padding_length()); + + // Add header extensions (base event). + absl::optional<uint64_t> base_transport_sequence_number; + { + uint16_t seqnum; + if (base_event->template GetExtension<TransportSequenceNumber>(&seqnum)) { + proto_batch->set_transport_sequence_number(seqnum); + base_transport_sequence_number = seqnum; + } + } + + absl::optional<uint64_t> unsigned_base_transmission_time_offset; + { + int32_t offset; + if (base_event->template GetExtension<TransmissionOffset>(&offset)) { + proto_batch->set_transmission_time_offset(offset); + unsigned_base_transmission_time_offset = ToUnsigned(offset); + } + } + + absl::optional<uint64_t> base_absolute_send_time; + { + uint32_t sendtime; + if (base_event->template GetExtension<AbsoluteSendTime>(&sendtime)) { + proto_batch->set_absolute_send_time(sendtime); + base_absolute_send_time = sendtime; + } + } + + absl::optional<uint64_t> base_video_rotation; + { + VideoRotation video_rotation; + if (base_event->template GetExtension<VideoOrientation>(&video_rotation)) { + proto_batch->set_video_rotation( + ConvertVideoRotationToCVOByte(video_rotation)); + base_video_rotation = ConvertVideoRotationToCVOByte(video_rotation); + } + } + + absl::optional<uint64_t> base_audio_level; + absl::optional<uint64_t> base_voice_activity; + { + bool voice_activity; + uint8_t audio_level; + if (base_event->template GetExtension<AudioLevel>(&voice_activity, + &audio_level)) { + RTC_DCHECK_LE(audio_level, 0x7Fu); + base_audio_level = audio_level; + proto_batch->set_audio_level(audio_level); + + base_voice_activity = voice_activity; + proto_batch->set_voice_activity(voice_activity); + } + } + + { + // TODO(webrtc:14975) Remove this kill switch after DD in RTC event log has + // been rolled out. + if (!webrtc::field_trial::IsDisabled( + "WebRTC-RtcEventLogEncodeDependencyDescriptor")) { + std::vector<rtc::ArrayView<const uint8_t>> raw_dds(batch.size()); + bool has_dd = false; + for (size_t i = 0; i < batch.size(); ++i) { + raw_dds[i] = + batch[i] + ->template GetRawExtension<RtpDependencyDescriptorExtension>(); + has_dd |= !raw_dds[i].empty(); + } + if (has_dd) { + if (auto dd_encoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + raw_dds)) { + *proto_batch->mutable_dependency_descriptor() = *dd_encoded; + } + } + } + } + + if (batch.size() == 1) { + return; + } + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms (event) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // marker (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->Marker(); + } + encoded_deltas = EncodeDeltas(base_event->Marker(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_marker_deltas(encoded_deltas); + } + + // payload_type (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->PayloadType(); + } + encoded_deltas = EncodeDeltas(base_event->PayloadType(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_payload_type_deltas(encoded_deltas); + } + + // sequence_number (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->SequenceNumber(); + } + encoded_deltas = EncodeDeltas(base_event->SequenceNumber(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_sequence_number_deltas(encoded_deltas); + } + + // rtp_timestamp (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->Timestamp(); + } + encoded_deltas = EncodeDeltas(base_event->Timestamp(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_rtp_timestamp_deltas(encoded_deltas); + } + + // ssrc (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->Ssrc(); + } + encoded_deltas = EncodeDeltas(base_event->Ssrc(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_ssrc_deltas(encoded_deltas); + } + + // payload_size (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->payload_length(); + } + encoded_deltas = EncodeDeltas(base_event->payload_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_payload_size_deltas(encoded_deltas); + } + + // header_size (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->header_length(); + } + encoded_deltas = EncodeDeltas(base_event->header_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_header_size_deltas(encoded_deltas); + } + + // padding_size (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->padding_length(); + } + encoded_deltas = EncodeDeltas(base_event->padding_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_padding_size_deltas(encoded_deltas); + } + + // transport_sequence_number (RTP extension) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + uint16_t seqnum; + if (event->template GetExtension<TransportSequenceNumber>(&seqnum)) { + values[i] = seqnum; + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_transport_sequence_number, values); + if (!encoded_deltas.empty()) { + proto_batch->set_transport_sequence_number_deltas(encoded_deltas); + } + + // transmission_time_offset (RTP extension) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + int32_t offset; + if (event->template GetExtension<TransmissionOffset>(&offset)) { + values[i] = ToUnsigned(offset); + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(unsigned_base_transmission_time_offset, values); + if (!encoded_deltas.empty()) { + proto_batch->set_transmission_time_offset_deltas(encoded_deltas); + } + + // absolute_send_time (RTP extension) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + uint32_t sendtime; + if (event->template GetExtension<AbsoluteSendTime>(&sendtime)) { + values[i] = sendtime; + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_absolute_send_time, values); + if (!encoded_deltas.empty()) { + proto_batch->set_absolute_send_time_deltas(encoded_deltas); + } + + // video_rotation (RTP extension) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + VideoRotation video_rotation; + if (event->template GetExtension<VideoOrientation>(&video_rotation)) { + values[i] = ConvertVideoRotationToCVOByte(video_rotation); + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_video_rotation, values); + if (!encoded_deltas.empty()) { + proto_batch->set_video_rotation_deltas(encoded_deltas); + } + + // audio_level (RTP extension) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + bool voice_activity; + uint8_t audio_level; + if (event->template GetExtension<AudioLevel>(&voice_activity, + &audio_level)) { + RTC_DCHECK_LE(audio_level, 0x7Fu); + values[i] = audio_level; + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_audio_level, values); + if (!encoded_deltas.empty()) { + proto_batch->set_audio_level_deltas(encoded_deltas); + } + + // voice_activity (RTP extension) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + bool voice_activity; + uint8_t audio_level; + if (event->template GetExtension<AudioLevel>(&voice_activity, + &audio_level)) { + RTC_DCHECK_LE(audio_level, 0x7Fu); + values[i] = voice_activity; + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_voice_activity, values); + if (!encoded_deltas.empty()) { + proto_batch->set_voice_activity_deltas(encoded_deltas); + } +} +} // namespace + +RtcEventLogEncoderNewFormat::RtcEventLogEncoderNewFormat() { + encode_neteq_set_minimum_delay_kill_switch_ = false; + if (webrtc::field_trial::IsEnabled( + "WebRTC-RtcEventLogEncodeNetEqSetMinimumDelayKillSwitch")) { + encode_neteq_set_minimum_delay_kill_switch_ = true; + } +} + +std::string RtcEventLogEncoderNewFormat::EncodeLogStart(int64_t timestamp_us, + int64_t utc_time_us) { + rtclog2::EventStream event_stream; + rtclog2::BeginLogEvent* proto_batch = event_stream.add_begin_log_events(); + proto_batch->set_timestamp_ms(timestamp_us / 1000); + proto_batch->set_version(2); + proto_batch->set_utc_time_ms(utc_time_us / 1000); + return event_stream.SerializeAsString(); +} + +std::string RtcEventLogEncoderNewFormat::EncodeLogEnd(int64_t timestamp_us) { + rtclog2::EventStream event_stream; + rtclog2::EndLogEvent* proto_batch = event_stream.add_end_log_events(); + proto_batch->set_timestamp_ms(timestamp_us / 1000); + return event_stream.SerializeAsString(); +} + +std::string RtcEventLogEncoderNewFormat::EncodeBatch( + std::deque<std::unique_ptr<RtcEvent>>::const_iterator begin, + std::deque<std::unique_ptr<RtcEvent>>::const_iterator end) { + rtclog2::EventStream event_stream; + std::string encoded_output; + + { + std::vector<const RtcEventAlrState*> alr_state_events; + std::vector<const RtcEventAudioNetworkAdaptation*> + audio_network_adaptation_events; + std::vector<const RtcEventAudioPlayout*> audio_playout_events; + std::vector<const RtcEventNetEqSetMinimumDelay*> + neteq_set_minimum_delay_events; + std::vector<const RtcEventAudioReceiveStreamConfig*> + audio_recv_stream_configs; + std::vector<const RtcEventAudioSendStreamConfig*> audio_send_stream_configs; + std::vector<const RtcEventBweUpdateDelayBased*> bwe_delay_based_updates; + std::vector<const RtcEventBweUpdateLossBased*> bwe_loss_based_updates; + std::vector<const RtcEventDtlsTransportState*> dtls_transport_states; + std::vector<const RtcEventDtlsWritableState*> dtls_writable_states; + std::map<uint32_t /* SSRC */, std::vector<const RtcEventFrameDecoded*>> + frames_decoded; + std::vector<const RtcEventGenericAckReceived*> generic_acks_received; + std::vector<const RtcEventGenericPacketReceived*> generic_packets_received; + std::vector<const RtcEventGenericPacketSent*> generic_packets_sent; + std::vector<const RtcEventIceCandidatePair*> ice_candidate_events; + std::vector<const RtcEventIceCandidatePairConfig*> ice_candidate_configs; + std::vector<const RtcEventProbeClusterCreated*> + probe_cluster_created_events; + std::vector<const RtcEventProbeResultFailure*> probe_result_failure_events; + std::vector<const RtcEventProbeResultSuccess*> probe_result_success_events; + std::vector<const RtcEventRouteChange*> route_change_events; + std::vector<const RtcEventRemoteEstimate*> remote_estimate_events; + std::vector<const RtcEventRtcpPacketIncoming*> incoming_rtcp_packets; + std::vector<const RtcEventRtcpPacketOutgoing*> outgoing_rtcp_packets; + std::map<uint32_t /* SSRC */, std::vector<const RtcEventRtpPacketIncoming*>> + incoming_rtp_packets; + std::map<uint32_t /* SSRC */, std::vector<const RtcEventRtpPacketOutgoing*>> + outgoing_rtp_packets; + std::vector<const RtcEventVideoReceiveStreamConfig*> + video_recv_stream_configs; + std::vector<const RtcEventVideoSendStreamConfig*> video_send_stream_configs; + + for (auto it = begin; it != end; ++it) { + switch ((*it)->GetType()) { + case RtcEvent::Type::AlrStateEvent: { + auto* rtc_event = + static_cast<const RtcEventAlrState* const>(it->get()); + alr_state_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::AudioNetworkAdaptation: { + auto* rtc_event = + static_cast<const RtcEventAudioNetworkAdaptation* const>( + it->get()); + audio_network_adaptation_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::AudioPlayout: { + auto* rtc_event = + static_cast<const RtcEventAudioPlayout* const>(it->get()); + audio_playout_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::AudioReceiveStreamConfig: { + auto* rtc_event = + static_cast<const RtcEventAudioReceiveStreamConfig* const>( + it->get()); + audio_recv_stream_configs.push_back(rtc_event); + break; + } + case RtcEvent::Type::AudioSendStreamConfig: { + auto* rtc_event = + static_cast<const RtcEventAudioSendStreamConfig* const>( + it->get()); + audio_send_stream_configs.push_back(rtc_event); + break; + } + case RtcEvent::Type::BweUpdateDelayBased: { + auto* rtc_event = + static_cast<const RtcEventBweUpdateDelayBased* const>(it->get()); + bwe_delay_based_updates.push_back(rtc_event); + break; + } + case RtcEvent::Type::BweUpdateLossBased: { + auto* rtc_event = + static_cast<const RtcEventBweUpdateLossBased* const>(it->get()); + bwe_loss_based_updates.push_back(rtc_event); + break; + } + case RtcEvent::Type::DtlsTransportState: { + auto* rtc_event = + static_cast<const RtcEventDtlsTransportState* const>(it->get()); + dtls_transport_states.push_back(rtc_event); + break; + } + case RtcEvent::Type::DtlsWritableState: { + auto* rtc_event = + static_cast<const RtcEventDtlsWritableState* const>(it->get()); + dtls_writable_states.push_back(rtc_event); + break; + } + case RtcEvent::Type::ProbeClusterCreated: { + auto* rtc_event = + static_cast<const RtcEventProbeClusterCreated* const>(it->get()); + probe_cluster_created_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::ProbeResultFailure: { + auto* rtc_event = + static_cast<const RtcEventProbeResultFailure* const>(it->get()); + probe_result_failure_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::ProbeResultSuccess: { + auto* rtc_event = + static_cast<const RtcEventProbeResultSuccess* const>(it->get()); + probe_result_success_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::RouteChangeEvent: { + auto* rtc_event = + static_cast<const RtcEventRouteChange* const>(it->get()); + route_change_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::RemoteEstimateEvent: { + auto* rtc_event = + static_cast<const RtcEventRemoteEstimate* const>(it->get()); + remote_estimate_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::RtcpPacketIncoming: { + auto* rtc_event = + static_cast<const RtcEventRtcpPacketIncoming* const>(it->get()); + incoming_rtcp_packets.push_back(rtc_event); + break; + } + case RtcEvent::Type::RtcpPacketOutgoing: { + auto* rtc_event = + static_cast<const RtcEventRtcpPacketOutgoing* const>(it->get()); + outgoing_rtcp_packets.push_back(rtc_event); + break; + } + case RtcEvent::Type::RtpPacketIncoming: { + auto* rtc_event = + static_cast<const RtcEventRtpPacketIncoming* const>(it->get()); + auto& v = incoming_rtp_packets[rtc_event->Ssrc()]; + v.emplace_back(rtc_event); + break; + } + case RtcEvent::Type::RtpPacketOutgoing: { + auto* rtc_event = + static_cast<const RtcEventRtpPacketOutgoing* const>(it->get()); + auto& v = outgoing_rtp_packets[rtc_event->Ssrc()]; + v.emplace_back(rtc_event); + break; + } + case RtcEvent::Type::VideoReceiveStreamConfig: { + auto* rtc_event = + static_cast<const RtcEventVideoReceiveStreamConfig* const>( + it->get()); + video_recv_stream_configs.push_back(rtc_event); + break; + } + case RtcEvent::Type::VideoSendStreamConfig: { + auto* rtc_event = + static_cast<const RtcEventVideoSendStreamConfig* const>( + it->get()); + video_send_stream_configs.push_back(rtc_event); + break; + } + case RtcEvent::Type::IceCandidatePairConfig: { + auto* rtc_event = + static_cast<const RtcEventIceCandidatePairConfig* const>( + it->get()); + ice_candidate_configs.push_back(rtc_event); + break; + } + case RtcEvent::Type::IceCandidatePairEvent: { + auto* rtc_event = + static_cast<const RtcEventIceCandidatePair* const>(it->get()); + ice_candidate_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::GenericPacketReceived: { + auto* rtc_event = + static_cast<const RtcEventGenericPacketReceived* const>( + it->get()); + generic_packets_received.push_back(rtc_event); + break; + } + case RtcEvent::Type::GenericPacketSent: { + auto* rtc_event = + static_cast<const RtcEventGenericPacketSent* const>(it->get()); + generic_packets_sent.push_back(rtc_event); + break; + } + case RtcEvent::Type::GenericAckReceived: { + auto* rtc_event = + static_cast<const RtcEventGenericAckReceived* const>(it->get()); + generic_acks_received.push_back(rtc_event); + break; + } + case RtcEvent::Type::FrameDecoded: { + auto* rtc_event = + static_cast<const RtcEventFrameDecoded* const>(it->get()); + frames_decoded[rtc_event->ssrc()].emplace_back(rtc_event); + break; + } + case RtcEvent::Type::NetEqSetMinimumDelay: { + auto* rtc_event = + static_cast<const RtcEventNetEqSetMinimumDelay* const>(it->get()); + neteq_set_minimum_delay_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::BeginV3Log: + case RtcEvent::Type::EndV3Log: + // These special events are written as part of starting + // and stopping the log, and only as part of version 3 of the format. + RTC_DCHECK_NOTREACHED(); + break; + case RtcEvent::Type::FakeEvent: + // Fake event used for unit test. + RTC_DCHECK_NOTREACHED(); + break; + } + } + + EncodeAlrState(alr_state_events, &event_stream); + EncodeAudioNetworkAdaptation(audio_network_adaptation_events, + &event_stream); + EncodeAudioPlayout(audio_playout_events, &event_stream); + EncodeAudioRecvStreamConfig(audio_recv_stream_configs, &event_stream); + EncodeAudioSendStreamConfig(audio_send_stream_configs, &event_stream); + EncodeNetEqSetMinimumDelay(neteq_set_minimum_delay_events, &event_stream); + EncodeBweUpdateDelayBased(bwe_delay_based_updates, &event_stream); + EncodeBweUpdateLossBased(bwe_loss_based_updates, &event_stream); + EncodeDtlsTransportState(dtls_transport_states, &event_stream); + EncodeDtlsWritableState(dtls_writable_states, &event_stream); + for (const auto& kv : frames_decoded) { + EncodeFramesDecoded(kv.second, &event_stream); + } + EncodeGenericAcksReceived(generic_acks_received, &event_stream); + EncodeGenericPacketsReceived(generic_packets_received, &event_stream); + EncodeGenericPacketsSent(generic_packets_sent, &event_stream); + EncodeIceCandidatePairConfig(ice_candidate_configs, &event_stream); + EncodeIceCandidatePairEvent(ice_candidate_events, &event_stream); + EncodeProbeClusterCreated(probe_cluster_created_events, &event_stream); + EncodeProbeResultFailure(probe_result_failure_events, &event_stream); + EncodeProbeResultSuccess(probe_result_success_events, &event_stream); + EncodeRouteChange(route_change_events, &event_stream); + EncodeRemoteEstimate(remote_estimate_events, &event_stream); + EncodeRtcpPacketIncoming(incoming_rtcp_packets, &event_stream); + EncodeRtcpPacketOutgoing(outgoing_rtcp_packets, &event_stream); + EncodeRtpPacketIncoming(incoming_rtp_packets, &event_stream); + EncodeRtpPacketOutgoing(outgoing_rtp_packets, &event_stream); + EncodeVideoRecvStreamConfig(video_recv_stream_configs, &event_stream); + EncodeVideoSendStreamConfig(video_send_stream_configs, &event_stream); + } // Deallocate the temporary vectors. + + return event_stream.SerializeAsString(); +} + +void RtcEventLogEncoderNewFormat::EncodeAlrState( + rtc::ArrayView<const RtcEventAlrState*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventAlrState* base_event : batch) { + rtclog2::AlrState* proto_batch = event_stream->add_alr_states(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_in_alr(base_event->in_alr()); + } + // TODO(terelius): Should we delta-compress this event type? +} + +void RtcEventLogEncoderNewFormat::EncodeAudioNetworkAdaptation( + rtc::ArrayView<const RtcEventAudioNetworkAdaptation*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) + return; + + // Base event + const RtcEventAudioNetworkAdaptation* const base_event = batch[0]; + rtclog2::AudioNetworkAdaptations* proto_batch = + event_stream->add_audio_network_adaptations(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + if (base_event->config().bitrate_bps.has_value()) + proto_batch->set_bitrate_bps(base_event->config().bitrate_bps.value()); + if (base_event->config().frame_length_ms.has_value()) { + proto_batch->set_frame_length_ms( + base_event->config().frame_length_ms.value()); + } + absl::optional<uint64_t> base_uplink_packet_loss_fraction; + if (base_event->config().uplink_packet_loss_fraction.has_value()) { + base_uplink_packet_loss_fraction = ConvertPacketLossFractionToProtoFormat( + base_event->config().uplink_packet_loss_fraction.value()); + proto_batch->set_uplink_packet_loss_fraction( + base_uplink_packet_loss_fraction.value()); + } + if (base_event->config().enable_fec.has_value()) + proto_batch->set_enable_fec(base_event->config().enable_fec.value()); + if (base_event->config().enable_dtx.has_value()) + proto_batch->set_enable_dtx(base_event->config().enable_dtx.value()); + // Note that `num_channels_deltas` encodes N as N-1, to keep deltas smaller, + // but there's no reason to do the same for the base event's value, since + // no bits will be spared. + if (base_event->config().num_channels.has_value()) + proto_batch->set_num_channels(base_event->config().num_channels.value()); + + if (batch.size() == 1) + return; + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // bitrate_bps + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + if (event->config().bitrate_bps.has_value()) { + values[i] = ToUnsigned(event->config().bitrate_bps.value()); + } else { + values[i].reset(); + } + } + const absl::optional<uint64_t> unsigned_base_bitrate_bps = + base_event->config().bitrate_bps.has_value() + ? ToUnsigned(base_event->config().bitrate_bps.value()) + : absl::optional<uint64_t>(); + encoded_deltas = EncodeDeltas(unsigned_base_bitrate_bps, values); + if (!encoded_deltas.empty()) { + proto_batch->set_bitrate_bps_deltas(encoded_deltas); + } + + // frame_length_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + if (event->config().frame_length_ms.has_value()) { + values[i] = ToUnsigned(event->config().frame_length_ms.value()); + } else { + values[i].reset(); + } + } + const absl::optional<uint64_t> unsigned_base_frame_length_ms = + base_event->config().frame_length_ms.has_value() + ? ToUnsigned(base_event->config().frame_length_ms.value()) + : absl::optional<uint64_t>(); + encoded_deltas = EncodeDeltas(unsigned_base_frame_length_ms, values); + if (!encoded_deltas.empty()) { + proto_batch->set_frame_length_ms_deltas(encoded_deltas); + } + + // uplink_packet_loss_fraction + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + if (event->config().uplink_packet_loss_fraction.has_value()) { + values[i] = ConvertPacketLossFractionToProtoFormat( + event->config().uplink_packet_loss_fraction.value()); + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_uplink_packet_loss_fraction, values); + if (!encoded_deltas.empty()) { + proto_batch->set_uplink_packet_loss_fraction_deltas(encoded_deltas); + } + + // enable_fec + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + values[i] = event->config().enable_fec; + } + encoded_deltas = EncodeDeltas(base_event->config().enable_fec, values); + if (!encoded_deltas.empty()) { + proto_batch->set_enable_fec_deltas(encoded_deltas); + } + + // enable_dtx + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + values[i] = event->config().enable_dtx; + } + encoded_deltas = EncodeDeltas(base_event->config().enable_dtx, values); + if (!encoded_deltas.empty()) { + proto_batch->set_enable_dtx_deltas(encoded_deltas); + } + + // num_channels + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + const absl::optional<size_t> num_channels = event->config().num_channels; + if (num_channels.has_value()) { + // Since the number of channels is always greater than 0, we can encode + // N channels as N-1, thereby making sure that we get smaller deltas. + // That is, a toggle of 1->2->1 can be encoded as deltas vector (1, 1), + // rather than as (1, 3) or (1, -1), either of which would require two + // bits per delta. + RTC_DCHECK_GT(num_channels.value(), 0u); + values[i] = num_channels.value() - 1; + } else { + values[i].reset(); + } + } + // In the base event, N channels encoded as N channels, but for delta + // compression purposes, also shifted down by 1. + absl::optional<size_t> shifted_base_num_channels; + if (base_event->config().num_channels.has_value()) { + RTC_DCHECK_GT(base_event->config().num_channels.value(), 0u); + shifted_base_num_channels = base_event->config().num_channels.value() - 1; + } + encoded_deltas = EncodeDeltas(shifted_base_num_channels, values); + if (!encoded_deltas.empty()) { + proto_batch->set_num_channels_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeAudioPlayout( + rtc::ArrayView<const RtcEventAudioPlayout*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) + return; + + // Base event + const RtcEventAudioPlayout* const base_event = batch[0]; + rtclog2::AudioPlayoutEvents* proto_batch = + event_stream->add_audio_playout_events(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_local_ssrc(base_event->ssrc()); + + if (batch.size() == 1) + return; + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioPlayout* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // local_ssrc + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioPlayout* event = batch[i + 1]; + values[i] = event->ssrc(); + } + encoded_deltas = EncodeDeltas(base_event->ssrc(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_local_ssrc_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeNetEqSetMinimumDelay( + rtc::ArrayView<const RtcEventNetEqSetMinimumDelay*> batch, + rtclog2::EventStream* event_stream) { + if (encode_neteq_set_minimum_delay_kill_switch_) { + return; + } + if (batch.empty()) { + return; + } + + const RtcEventNetEqSetMinimumDelay* base_event = batch[0]; + + rtclog2::NetEqSetMinimumDelay* proto_batch = + event_stream->add_neteq_set_minimum_delay(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_remote_ssrc(base_event->remote_ssrc()); + proto_batch->set_minimum_delay_ms(base_event->minimum_delay_ms()); + + if (batch.size() == 1) + return; + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventNetEqSetMinimumDelay* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // remote_ssrc + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventNetEqSetMinimumDelay* event = batch[i + 1]; + values[i] = event->remote_ssrc(); + } + encoded_deltas = EncodeDeltas(base_event->remote_ssrc(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_remote_ssrc_deltas(encoded_deltas); + } + + // minimum_delay_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventNetEqSetMinimumDelay* event = batch[i + 1]; + values[i] = ToUnsigned(event->minimum_delay_ms()); + } + encoded_deltas = + EncodeDeltas(ToUnsigned(base_event->minimum_delay_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_minimum_delay_ms_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeAudioRecvStreamConfig( + rtc::ArrayView<const RtcEventAudioReceiveStreamConfig*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventAudioReceiveStreamConfig* base_event : batch) { + rtclog2::AudioRecvStreamConfig* proto_batch = + event_stream->add_audio_recv_stream_configs(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_remote_ssrc(base_event->config().remote_ssrc); + proto_batch->set_local_ssrc(base_event->config().local_ssrc); + + rtclog2::RtpHeaderExtensionConfig* proto_config = + proto_batch->mutable_header_extensions(); + bool has_recognized_extensions = + ConvertToProtoFormat(base_event->config().rtp_extensions, proto_config); + if (!has_recognized_extensions) + proto_batch->clear_header_extensions(); + } +} + +void RtcEventLogEncoderNewFormat::EncodeAudioSendStreamConfig( + rtc::ArrayView<const RtcEventAudioSendStreamConfig*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventAudioSendStreamConfig* base_event : batch) { + rtclog2::AudioSendStreamConfig* proto_batch = + event_stream->add_audio_send_stream_configs(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_ssrc(base_event->config().local_ssrc); + + rtclog2::RtpHeaderExtensionConfig* proto_config = + proto_batch->mutable_header_extensions(); + bool has_recognized_extensions = + ConvertToProtoFormat(base_event->config().rtp_extensions, proto_config); + if (!has_recognized_extensions) + proto_batch->clear_header_extensions(); + } +} + +void RtcEventLogEncoderNewFormat::EncodeBweUpdateDelayBased( + rtc::ArrayView<const RtcEventBweUpdateDelayBased*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) + return; + + // Base event + const RtcEventBweUpdateDelayBased* const base_event = batch[0]; + rtclog2::DelayBasedBweUpdates* proto_batch = + event_stream->add_delay_based_bwe_updates(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_bitrate_bps(base_event->bitrate_bps()); + proto_batch->set_detector_state( + ConvertToProtoFormat(base_event->detector_state())); + + if (batch.size() == 1) + return; + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateDelayBased* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // bitrate_bps + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateDelayBased* event = batch[i + 1]; + values[i] = event->bitrate_bps(); + } + encoded_deltas = EncodeDeltas(base_event->bitrate_bps(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_bitrate_bps_deltas(encoded_deltas); + } + + // detector_state + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateDelayBased* event = batch[i + 1]; + values[i] = + static_cast<uint64_t>(ConvertToProtoFormat(event->detector_state())); + } + encoded_deltas = EncodeDeltas( + static_cast<uint64_t>(ConvertToProtoFormat(base_event->detector_state())), + values); + if (!encoded_deltas.empty()) { + proto_batch->set_detector_state_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeBweUpdateLossBased( + rtc::ArrayView<const RtcEventBweUpdateLossBased*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) + return; + + // Base event + const RtcEventBweUpdateLossBased* const base_event = batch[0]; + rtclog2::LossBasedBweUpdates* proto_batch = + event_stream->add_loss_based_bwe_updates(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_bitrate_bps(base_event->bitrate_bps()); + proto_batch->set_fraction_loss(base_event->fraction_loss()); + proto_batch->set_total_packets(base_event->total_packets()); + + if (batch.size() == 1) + return; + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateLossBased* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // bitrate_bps + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateLossBased* event = batch[i + 1]; + values[i] = event->bitrate_bps(); + } + encoded_deltas = EncodeDeltas(base_event->bitrate_bps(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_bitrate_bps_deltas(encoded_deltas); + } + + // fraction_loss + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateLossBased* event = batch[i + 1]; + values[i] = event->fraction_loss(); + } + encoded_deltas = EncodeDeltas(base_event->fraction_loss(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_fraction_loss_deltas(encoded_deltas); + } + + // total_packets + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateLossBased* event = batch[i + 1]; + values[i] = event->total_packets(); + } + encoded_deltas = EncodeDeltas(base_event->total_packets(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_total_packets_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeDtlsTransportState( + rtc::ArrayView<const RtcEventDtlsTransportState*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventDtlsTransportState* base_event : batch) { + rtclog2::DtlsTransportStateEvent* proto_batch = + event_stream->add_dtls_transport_state_events(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_dtls_transport_state( + ConvertToProtoFormat(base_event->dtls_transport_state())); + } +} + +void RtcEventLogEncoderNewFormat::EncodeDtlsWritableState( + rtc::ArrayView<const RtcEventDtlsWritableState*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventDtlsWritableState* base_event : batch) { + rtclog2::DtlsWritableState* proto_batch = + event_stream->add_dtls_writable_states(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_writable(base_event->writable()); + } +} + +void RtcEventLogEncoderNewFormat::EncodeProbeClusterCreated( + rtc::ArrayView<const RtcEventProbeClusterCreated*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventProbeClusterCreated* base_event : batch) { + rtclog2::BweProbeCluster* proto_batch = event_stream->add_probe_clusters(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_id(base_event->id()); + proto_batch->set_bitrate_bps(base_event->bitrate_bps()); + proto_batch->set_min_packets(base_event->min_probes()); + proto_batch->set_min_bytes(base_event->min_bytes()); + } +} + +void RtcEventLogEncoderNewFormat::EncodeProbeResultFailure( + rtc::ArrayView<const RtcEventProbeResultFailure*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventProbeResultFailure* base_event : batch) { + rtclog2::BweProbeResultFailure* proto_batch = + event_stream->add_probe_failure(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_id(base_event->id()); + proto_batch->set_failure( + ConvertToProtoFormat(base_event->failure_reason())); + } + // TODO(terelius): Should we delta-compress this event type? +} + +void RtcEventLogEncoderNewFormat::EncodeProbeResultSuccess( + rtc::ArrayView<const RtcEventProbeResultSuccess*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventProbeResultSuccess* base_event : batch) { + rtclog2::BweProbeResultSuccess* proto_batch = + event_stream->add_probe_success(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_id(base_event->id()); + proto_batch->set_bitrate_bps(base_event->bitrate_bps()); + } + // TODO(terelius): Should we delta-compress this event type? +} + +void RtcEventLogEncoderNewFormat::EncodeRouteChange( + rtc::ArrayView<const RtcEventRouteChange*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventRouteChange* base_event : batch) { + rtclog2::RouteChange* proto_batch = event_stream->add_route_changes(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_connected(base_event->connected()); + proto_batch->set_overhead(base_event->overhead()); + } + // TODO(terelius): Should we delta-compress this event type? +} + +void RtcEventLogEncoderNewFormat::EncodeRemoteEstimate( + rtc::ArrayView<const RtcEventRemoteEstimate*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) + return; + + // Base event + const auto* const base_event = batch[0]; + rtclog2::RemoteEstimates* proto_batch = event_stream->add_remote_estimates(); + + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + + absl::optional<uint64_t> base_link_capacity_lower; + if (base_event->link_capacity_lower_.IsFinite()) { + base_link_capacity_lower = + base_event->link_capacity_lower_.kbps<uint32_t>(); + proto_batch->set_link_capacity_lower_kbps(*base_link_capacity_lower); + } + absl::optional<uint64_t> base_link_capacity_upper; + if (base_event->link_capacity_upper_.IsFinite()) { + base_link_capacity_upper = + base_event->link_capacity_upper_.kbps<uint32_t>(); + proto_batch->set_link_capacity_upper_kbps(*base_link_capacity_upper); + } + + if (batch.size() == 1) + return; + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const auto* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // link_capacity_lower_kbps + for (size_t i = 0; i < values.size(); ++i) { + const auto* event = batch[i + 1]; + if (event->link_capacity_lower_.IsFinite()) { + values[i] = event->link_capacity_lower_.kbps<uint32_t>(); + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_link_capacity_lower, values); + if (!encoded_deltas.empty()) { + proto_batch->set_link_capacity_lower_kbps_deltas(encoded_deltas); + } + + // link_capacity_upper_kbps + for (size_t i = 0; i < values.size(); ++i) { + const auto* event = batch[i + 1]; + if (event->link_capacity_upper_.IsFinite()) { + values[i] = event->link_capacity_upper_.kbps<uint32_t>(); + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_link_capacity_upper, values); + if (!encoded_deltas.empty()) { + proto_batch->set_link_capacity_upper_kbps_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeRtcpPacketIncoming( + rtc::ArrayView<const RtcEventRtcpPacketIncoming*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) { + return; + } + EncodeRtcpPacket(batch, event_stream->add_incoming_rtcp_packets()); +} + +void RtcEventLogEncoderNewFormat::EncodeRtcpPacketOutgoing( + rtc::ArrayView<const RtcEventRtcpPacketOutgoing*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) { + return; + } + EncodeRtcpPacket(batch, event_stream->add_outgoing_rtcp_packets()); +} + +void RtcEventLogEncoderNewFormat::EncodeRtpPacketIncoming( + const std::map<uint32_t, std::vector<const RtcEventRtpPacketIncoming*>>& + batch, + rtclog2::EventStream* event_stream) { + for (const auto& it : batch) { + RTC_DCHECK(!it.second.empty()); + EncodeRtpPacket(it.second, event_stream->add_incoming_rtp_packets()); + } +} + +void RtcEventLogEncoderNewFormat::EncodeFramesDecoded( + rtc::ArrayView<const RtcEventFrameDecoded* const> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) { + return; + } + const RtcEventFrameDecoded* const base_event = batch[0]; + rtclog2::FrameDecodedEvents* proto_batch = + event_stream->add_frame_decoded_events(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_ssrc(base_event->ssrc()); + proto_batch->set_render_time_ms(base_event->render_time_ms()); + proto_batch->set_width(base_event->width()); + proto_batch->set_height(base_event->height()); + proto_batch->set_codec(ConvertToProtoFormat(base_event->codec())); + proto_batch->set_qp(base_event->qp()); + + if (batch.size() == 1) { + return; + } + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // SSRC + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = event->ssrc(); + } + encoded_deltas = EncodeDeltas(base_event->ssrc(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_ssrc_deltas(encoded_deltas); + } + + // render_time_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = ToUnsigned(event->render_time_ms()); + } + encoded_deltas = + EncodeDeltas(ToUnsigned(base_event->render_time_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_render_time_ms_deltas(encoded_deltas); + } + + // width + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = ToUnsigned(event->width()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->width()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_width_deltas(encoded_deltas); + } + + // height + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = ToUnsigned(event->height()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->height()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_height_deltas(encoded_deltas); + } + + // codec + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = static_cast<uint64_t>(ConvertToProtoFormat(event->codec())); + } + encoded_deltas = EncodeDeltas( + static_cast<uint64_t>(ConvertToProtoFormat(base_event->codec())), values); + if (!encoded_deltas.empty()) { + proto_batch->set_codec_deltas(encoded_deltas); + } + + // qp + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = event->qp(); + } + encoded_deltas = EncodeDeltas(base_event->qp(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_qp_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeGenericPacketsSent( + rtc::ArrayView<const RtcEventGenericPacketSent*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) { + return; + } + const RtcEventGenericPacketSent* const base_event = batch[0]; + rtclog2::GenericPacketSent* proto_batch = + event_stream->add_generic_packets_sent(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_packet_number(base_event->packet_number()); + proto_batch->set_overhead_length(base_event->overhead_length()); + proto_batch->set_payload_length(base_event->payload_length()); + proto_batch->set_padding_length(base_event->padding_length()); + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + if (batch.size() == 1) { + return; + } + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketSent* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // packet_number + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketSent* event = batch[i + 1]; + values[i] = ToUnsigned(event->packet_number()); + } + encoded_deltas = + EncodeDeltas(ToUnsigned(base_event->packet_number()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_packet_number_deltas(encoded_deltas); + } + + // overhead_length + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketSent* event = batch[i + 1]; + values[i] = event->overhead_length(); + } + encoded_deltas = EncodeDeltas(base_event->overhead_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_overhead_length_deltas(encoded_deltas); + } + + // payload_length + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketSent* event = batch[i + 1]; + values[i] = event->payload_length(); + } + encoded_deltas = EncodeDeltas(base_event->payload_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_payload_length_deltas(encoded_deltas); + } + + // padding_length + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketSent* event = batch[i + 1]; + values[i] = event->padding_length(); + } + encoded_deltas = EncodeDeltas(base_event->padding_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_padding_length_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeGenericPacketsReceived( + rtc::ArrayView<const RtcEventGenericPacketReceived*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) { + return; + } + const RtcEventGenericPacketReceived* const base_event = batch[0]; + rtclog2::GenericPacketReceived* proto_batch = + event_stream->add_generic_packets_received(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_packet_number(base_event->packet_number()); + proto_batch->set_packet_length(base_event->packet_length()); + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + if (batch.size() == 1) { + return; + } + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketReceived* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // packet_number + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketReceived* event = batch[i + 1]; + values[i] = ToUnsigned(event->packet_number()); + } + encoded_deltas = + EncodeDeltas(ToUnsigned(base_event->packet_number()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_packet_number_deltas(encoded_deltas); + } + + // packet_length + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketReceived* event = batch[i + 1]; + values[i] = event->packet_length(); + } + encoded_deltas = EncodeDeltas(base_event->packet_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_packet_length_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeGenericAcksReceived( + rtc::ArrayView<const RtcEventGenericAckReceived*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) { + return; + } + const RtcEventGenericAckReceived* const base_event = batch[0]; + rtclog2::GenericAckReceived* proto_batch = + event_stream->add_generic_acks_received(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_packet_number(base_event->packet_number()); + proto_batch->set_acked_packet_number(base_event->acked_packet_number()); + absl::optional<uint64_t> base_receive_timestamp; + if (base_event->receive_acked_packet_time_ms()) { + int64_t receive_acked_packet_time_ms = + base_event->receive_acked_packet_time_ms().value(); + base_receive_timestamp = ToUnsigned(receive_acked_packet_time_ms); + proto_batch->set_receive_acked_packet_time_ms(receive_acked_packet_time_ms); + } + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + if (batch.size() == 1) { + return; + } + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericAckReceived* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // packet_number + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericAckReceived* event = batch[i + 1]; + values[i] = ToUnsigned(event->packet_number()); + } + encoded_deltas = + EncodeDeltas(ToUnsigned(base_event->packet_number()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_packet_number_deltas(encoded_deltas); + } + + // acked packet number + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericAckReceived* event = batch[i + 1]; + values[i] = ToUnsigned(event->acked_packet_number()); + } + encoded_deltas = + EncodeDeltas(ToUnsigned(base_event->acked_packet_number()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_acked_packet_number_deltas(encoded_deltas); + } + + // receive timestamp + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericAckReceived* event = batch[i + 1]; + if (event->receive_acked_packet_time_ms()) { + values[i] = ToUnsigned(event->receive_acked_packet_time_ms().value()); + } else { + values[i] = absl::nullopt; + } + } + encoded_deltas = EncodeDeltas(base_receive_timestamp, values); + if (!encoded_deltas.empty()) { + proto_batch->set_receive_acked_packet_time_ms_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeRtpPacketOutgoing( + const std::map<uint32_t, std::vector<const RtcEventRtpPacketOutgoing*>>& + batch, + rtclog2::EventStream* event_stream) { + for (const auto& it : batch) { + RTC_DCHECK(!it.second.empty()); + EncodeRtpPacket(it.second, event_stream->add_outgoing_rtp_packets()); + } +} + +void RtcEventLogEncoderNewFormat::EncodeVideoRecvStreamConfig( + rtc::ArrayView<const RtcEventVideoReceiveStreamConfig*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventVideoReceiveStreamConfig* base_event : batch) { + rtclog2::VideoRecvStreamConfig* proto_batch = + event_stream->add_video_recv_stream_configs(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_remote_ssrc(base_event->config().remote_ssrc); + proto_batch->set_local_ssrc(base_event->config().local_ssrc); + proto_batch->set_rtx_ssrc(base_event->config().rtx_ssrc); + + rtclog2::RtpHeaderExtensionConfig* proto_config = + proto_batch->mutable_header_extensions(); + bool has_recognized_extensions = + ConvertToProtoFormat(base_event->config().rtp_extensions, proto_config); + if (!has_recognized_extensions) + proto_batch->clear_header_extensions(); + } +} + +void RtcEventLogEncoderNewFormat::EncodeVideoSendStreamConfig( + rtc::ArrayView<const RtcEventVideoSendStreamConfig*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventVideoSendStreamConfig* base_event : batch) { + rtclog2::VideoSendStreamConfig* proto_batch = + event_stream->add_video_send_stream_configs(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_ssrc(base_event->config().local_ssrc); + proto_batch->set_rtx_ssrc(base_event->config().rtx_ssrc); + + rtclog2::RtpHeaderExtensionConfig* proto_config = + proto_batch->mutable_header_extensions(); + bool has_recognized_extensions = + ConvertToProtoFormat(base_event->config().rtp_extensions, proto_config); + if (!has_recognized_extensions) + proto_batch->clear_header_extensions(); + } +} + +void RtcEventLogEncoderNewFormat::EncodeIceCandidatePairConfig( + rtc::ArrayView<const RtcEventIceCandidatePairConfig*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventIceCandidatePairConfig* base_event : batch) { + rtclog2::IceCandidatePairConfig* proto_batch = + event_stream->add_ice_candidate_configs(); + + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_config_type(ConvertToProtoFormat(base_event->type())); + proto_batch->set_candidate_pair_id(base_event->candidate_pair_id()); + const auto& desc = base_event->candidate_pair_desc(); + proto_batch->set_local_candidate_type( + ConvertToProtoFormat(desc.local_candidate_type)); + proto_batch->set_local_relay_protocol( + ConvertToProtoFormat(desc.local_relay_protocol)); + proto_batch->set_local_network_type( + ConvertToProtoFormat(desc.local_network_type)); + proto_batch->set_local_address_family( + ConvertToProtoFormat(desc.local_address_family)); + proto_batch->set_remote_candidate_type( + ConvertToProtoFormat(desc.remote_candidate_type)); + proto_batch->set_remote_address_family( + ConvertToProtoFormat(desc.remote_address_family)); + proto_batch->set_candidate_pair_protocol( + ConvertToProtoFormat(desc.candidate_pair_protocol)); + } + // TODO(terelius): Should we delta-compress this event type? +} + +void RtcEventLogEncoderNewFormat::EncodeIceCandidatePairEvent( + rtc::ArrayView<const RtcEventIceCandidatePair*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventIceCandidatePair* base_event : batch) { + rtclog2::IceCandidatePairEvent* proto_batch = + event_stream->add_ice_candidate_events(); + + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + + proto_batch->set_event_type(ConvertToProtoFormat(base_event->type())); + proto_batch->set_candidate_pair_id(base_event->candidate_pair_id()); + proto_batch->set_transaction_id(base_event->transaction_id()); + } + // TODO(terelius): Should we delta-compress this event type? +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h new file mode 100644 index 0000000000..6747f41f07 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h @@ -0,0 +1,164 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_NEW_FORMAT_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_NEW_FORMAT_H_ + +#include <deque> +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "api/array_view.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder.h" + +namespace webrtc { + +namespace rtclog2 { +class EventStream; // Auto-generated from protobuf. +} // namespace rtclog2 + +class RtcEventAlrState; +class RtcEventRouteChange; +class RtcEventRemoteEstimate; +class RtcEventAudioNetworkAdaptation; +class RtcEventAudioPlayout; +class RtcEventAudioReceiveStreamConfig; +class RtcEventAudioSendStreamConfig; +class RtcEventBweUpdateDelayBased; +class RtcEventBweUpdateLossBased; +class RtcEventDtlsTransportState; +class RtcEventDtlsWritableState; +class RtcEventLoggingStarted; +class RtcEventLoggingStopped; +class RtcEventNetEqSetMinimumDelay; +class RtcEventProbeClusterCreated; +class RtcEventProbeResultFailure; +class RtcEventProbeResultSuccess; +class RtcEventRtcpPacketIncoming; +class RtcEventRtcpPacketOutgoing; +class RtcEventRtpPacketIncoming; +class RtcEventRtpPacketOutgoing; +class RtcEventVideoReceiveStreamConfig; +class RtcEventVideoSendStreamConfig; +class RtcEventIceCandidatePairConfig; +class RtcEventIceCandidatePair; +class RtpPacket; +class RtcEventFrameDecoded; +class RtcEventGenericAckReceived; +class RtcEventGenericPacketReceived; +class RtcEventGenericPacketSent; + +class RtcEventLogEncoderNewFormat final : public RtcEventLogEncoder { + public: + RtcEventLogEncoderNewFormat(); + ~RtcEventLogEncoderNewFormat() override = default; + + std::string EncodeBatch( + std::deque<std::unique_ptr<RtcEvent>>::const_iterator begin, + std::deque<std::unique_ptr<RtcEvent>>::const_iterator end) override; + + std::string EncodeLogStart(int64_t timestamp_us, + int64_t utc_time_us) override; + std::string EncodeLogEnd(int64_t timestamp_us) override; + + private: + bool encode_neteq_set_minimum_delay_kill_switch_ = false; + + // Encoding entry-point for the various RtcEvent subclasses. + void EncodeAlrState(rtc::ArrayView<const RtcEventAlrState*> batch, + rtclog2::EventStream* event_stream); + void EncodeAudioNetworkAdaptation( + rtc::ArrayView<const RtcEventAudioNetworkAdaptation*> batch, + rtclog2::EventStream* event_stream); + void EncodeAudioPlayout(rtc::ArrayView<const RtcEventAudioPlayout*> batch, + rtclog2::EventStream* event_stream); + void EncodeAudioRecvStreamConfig( + rtc::ArrayView<const RtcEventAudioReceiveStreamConfig*> batch, + rtclog2::EventStream* event_stream); + void EncodeAudioSendStreamConfig( + rtc::ArrayView<const RtcEventAudioSendStreamConfig*> batch, + rtclog2::EventStream* event_stream); + void EncodeBweUpdateDelayBased( + rtc::ArrayView<const RtcEventBweUpdateDelayBased*> batch, + rtclog2::EventStream* event_stream); + void EncodeBweUpdateLossBased( + rtc::ArrayView<const RtcEventBweUpdateLossBased*> batch, + rtclog2::EventStream* event_stream); + void EncodeDtlsTransportState( + rtc::ArrayView<const RtcEventDtlsTransportState*> batch, + rtclog2::EventStream* event_stream); + void EncodeDtlsWritableState( + rtc::ArrayView<const RtcEventDtlsWritableState*> batch, + rtclog2::EventStream* event_stream); + void EncodeFramesDecoded( + rtc::ArrayView<const RtcEventFrameDecoded* const> batch, + rtclog2::EventStream* event_stream); + void EncodeGenericAcksReceived( + rtc::ArrayView<const RtcEventGenericAckReceived*> batch, + rtclog2::EventStream* event_stream); + void EncodeGenericPacketsReceived( + rtc::ArrayView<const RtcEventGenericPacketReceived*> batch, + rtclog2::EventStream* event_stream); + void EncodeGenericPacketsSent( + rtc::ArrayView<const RtcEventGenericPacketSent*> batch, + rtclog2::EventStream* event_stream); + void EncodeIceCandidatePairConfig( + rtc::ArrayView<const RtcEventIceCandidatePairConfig*> batch, + rtclog2::EventStream* event_stream); + void EncodeIceCandidatePairEvent( + rtc::ArrayView<const RtcEventIceCandidatePair*> batch, + rtclog2::EventStream* event_stream); + void EncodeLoggingStarted(rtc::ArrayView<const RtcEventLoggingStarted*> batch, + rtclog2::EventStream* event_stream); + void EncodeLoggingStopped(rtc::ArrayView<const RtcEventLoggingStopped*> batch, + rtclog2::EventStream* event_stream); + void EncodeNetEqSetMinimumDelay( + rtc::ArrayView<const RtcEventNetEqSetMinimumDelay*> batch, + rtclog2::EventStream* event_stream); + void EncodeProbeClusterCreated( + rtc::ArrayView<const RtcEventProbeClusterCreated*> batch, + rtclog2::EventStream* event_stream); + void EncodeProbeResultFailure( + rtc::ArrayView<const RtcEventProbeResultFailure*> batch, + rtclog2::EventStream* event_stream); + void EncodeProbeResultSuccess( + rtc::ArrayView<const RtcEventProbeResultSuccess*> batch, + rtclog2::EventStream* event_stream); + void EncodeRouteChange(rtc::ArrayView<const RtcEventRouteChange*> batch, + rtclog2::EventStream* event_stream); + void EncodeRemoteEstimate(rtc::ArrayView<const RtcEventRemoteEstimate*> batch, + rtclog2::EventStream* event_stream); + void EncodeRtcpPacketIncoming( + rtc::ArrayView<const RtcEventRtcpPacketIncoming*> batch, + rtclog2::EventStream* event_stream); + void EncodeRtcpPacketOutgoing( + rtc::ArrayView<const RtcEventRtcpPacketOutgoing*> batch, + rtclog2::EventStream* event_stream); + void EncodeRtpPacketIncoming( + const std::map<uint32_t, std::vector<const RtcEventRtpPacketIncoming*>>& + batch, + rtclog2::EventStream* event_stream); + void EncodeRtpPacketOutgoing( + const std::map<uint32_t, std::vector<const RtcEventRtpPacketOutgoing*>>& + batch, + rtclog2::EventStream* event_stream); + void EncodeVideoRecvStreamConfig( + rtc::ArrayView<const RtcEventVideoReceiveStreamConfig*> batch, + rtclog2::EventStream* event_stream); + void EncodeVideoSendStreamConfig( + rtc::ArrayView<const RtcEventVideoSendStreamConfig*> batch, + rtclog2::EventStream* event_stream); +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_NEW_FORMAT_H_ 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..612f85bf61 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc @@ -0,0 +1,1416 @@ +/* + * 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/field_trial.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>(); +} + +TEST_P(RtcEventLogEncoderTest, + RtcEventRtpPacketIncomingNoDependencyDescriptor) { + test::ScopedFieldTrials no_dd( + "WebRTC-RtcEventLogEncodeDependencyDescriptor/Disabled/"); + TestRtpPackets<RtcEventRtpPacketIncoming, LoggedRtpPacketIncoming>(); +} + +TEST_P(RtcEventLogEncoderTest, + RtcEventRtpPacketOutgoingNoDependencyDescriptor) { + test::ScopedFieldTrials no_dd( + "WebRTC-RtcEventLogEncodeDependencyDescriptor/Disabled/"); + 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 diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.cc new file mode 100644 index 0000000000..131aae1de8 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.cc @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2021 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 "logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h" + +#include <string> +#include <vector> + +#include "absl/types/optional.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" +#include "logging/rtc_event_log/encoder/var_int.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_begin_log.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_dtls_transport_state.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" +#include "logging/rtc_event_log/events/rtc_event_end_log.h" +#include "logging/rtc_event_log/events/rtc_event_frame_decoded.h" +#include "logging/rtc_event_log/events/rtc_event_generic_ack_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_sent.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.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_remote_estimate.h" +#include "logging/rtc_event_log/events/rtc_event_route_change.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 "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +std::string RtcEventLogEncoderV3::EncodeLogStart(int64_t timestamp_us, + int64_t utc_time_us) { + std::unique_ptr<RtcEventBeginLog> begin_log = + std::make_unique<RtcEventBeginLog>(Timestamp::Micros(timestamp_us), + Timestamp::Micros(utc_time_us)); + std::vector<const RtcEvent*> batch; + batch.push_back(begin_log.get()); + + std::string encoded_event = RtcEventBeginLog::Encode(batch); + + return encoded_event; +} + +std::string RtcEventLogEncoderV3::EncodeLogEnd(int64_t timestamp_us) { + std::unique_ptr<RtcEventEndLog> end_log = + std::make_unique<RtcEventEndLog>(Timestamp::Micros(timestamp_us)); + std::vector<const RtcEvent*> batch; + batch.push_back(end_log.get()); + + std::string encoded_event = RtcEventEndLog::Encode(batch); + + return encoded_event; +} + +RtcEventLogEncoderV3::RtcEventLogEncoderV3() { + encoders_[RtcEvent::Type::AlrStateEvent] = RtcEventAlrState::Encode; + encoders_[RtcEvent::Type::AudioNetworkAdaptation] = + RtcEventAudioNetworkAdaptation::Encode; + encoders_[RtcEvent::Type::AudioPlayout] = RtcEventAudioPlayout::Encode; + encoders_[RtcEvent::Type::AudioReceiveStreamConfig] = + RtcEventAudioReceiveStreamConfig::Encode; + encoders_[RtcEvent::Type::AudioSendStreamConfig] = + RtcEventAudioSendStreamConfig::Encode; + encoders_[RtcEvent::Type::BweUpdateDelayBased] = + RtcEventBweUpdateDelayBased::Encode; + encoders_[RtcEvent::Type::BweUpdateLossBased] = + RtcEventBweUpdateLossBased::Encode; + encoders_[RtcEvent::Type::DtlsTransportState] = + RtcEventDtlsTransportState::Encode; + encoders_[RtcEvent::Type::DtlsWritableState] = + RtcEventDtlsWritableState::Encode; + encoders_[RtcEvent::Type::FrameDecoded] = RtcEventFrameDecoded::Encode; + encoders_[RtcEvent::Type::GenericAckReceived] = + RtcEventGenericAckReceived::Encode; + encoders_[RtcEvent::Type::GenericPacketReceived] = + RtcEventGenericPacketReceived::Encode; + encoders_[RtcEvent::Type::GenericPacketSent] = + RtcEventGenericPacketSent::Encode; + encoders_[RtcEvent::Type::IceCandidatePairConfig] = + RtcEventIceCandidatePairConfig::Encode; + encoders_[RtcEvent::Type::IceCandidatePairEvent] = + RtcEventIceCandidatePair::Encode; + encoders_[RtcEvent::Type::ProbeClusterCreated] = + RtcEventProbeClusterCreated::Encode; + encoders_[RtcEvent::Type::ProbeResultFailure] = + RtcEventProbeResultFailure::Encode; + encoders_[RtcEvent::Type::ProbeResultSuccess] = + RtcEventProbeResultSuccess::Encode; + encoders_[RtcEvent::Type::RemoteEstimateEvent] = + RtcEventRemoteEstimate::Encode; + encoders_[RtcEvent::Type::RouteChangeEvent] = RtcEventRouteChange::Encode; + encoders_[RtcEvent::Type::RtcpPacketIncoming] = + RtcEventRtcpPacketIncoming::Encode; + encoders_[RtcEvent::Type::RtcpPacketOutgoing] = + RtcEventRtcpPacketOutgoing::Encode; + encoders_[RtcEvent::Type::RtpPacketIncoming] = + RtcEventRtpPacketIncoming::Encode; + encoders_[RtcEvent::Type::RtpPacketOutgoing] = + RtcEventRtpPacketOutgoing::Encode; + encoders_[RtcEvent::Type::VideoReceiveStreamConfig] = + RtcEventVideoReceiveStreamConfig::Encode; + encoders_[RtcEvent::Type::VideoSendStreamConfig] = + RtcEventVideoSendStreamConfig::Encode; +} + +std::string RtcEventLogEncoderV3::EncodeBatch( + std::deque<std::unique_ptr<RtcEvent>>::const_iterator begin, + std::deque<std::unique_ptr<RtcEvent>>::const_iterator end) { + struct EventGroupKey { + // Events are grouped by event type. For compression efficiency, + // events can optionally have a secondary key, in most cases the + // SSRC. + RtcEvent::Type type; + uint32_t secondary_group_key; + + bool operator<(EventGroupKey other) const { + return type < other.type || + (type == other.type && + secondary_group_key < other.secondary_group_key); + } + }; + + std::map<EventGroupKey, std::vector<const RtcEvent*>> event_groups; + + for (auto it = begin; it != end; ++it) { + event_groups[{(*it)->GetType(), (*it)->GetGroupKey()}].push_back(it->get()); + } + + std::string encoded_output; + for (auto& kv : event_groups) { + auto it = encoders_.find(kv.first.type); + RTC_DCHECK(it != encoders_.end()); + if (it != encoders_.end()) { + auto& encoder = it->second; + // TODO(terelius): Use some "string builder" or preallocate? + encoded_output += encoder(kv.second); + } + } + + return encoded_output; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h new file mode 100644 index 0000000000..cb796ec562 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_V3_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_V3_H_ + +#include <deque> +#include <map> +#include <memory> +#include <string> + +#include "api/array_view.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder.h" +#include "logging/rtc_event_log/events/rtc_event_definition.h" + +namespace webrtc { + +class RtcEventLogEncoderV3 final : public RtcEventLogEncoder { + public: + RtcEventLogEncoderV3(); + ~RtcEventLogEncoderV3() override = default; + + std::string EncodeBatch( + std::deque<std::unique_ptr<RtcEvent>>::const_iterator begin, + std::deque<std::unique_ptr<RtcEvent>>::const_iterator end) override; + + std::string EncodeLogStart(int64_t timestamp_us, + int64_t utc_time_us) override; + std::string EncodeLogEnd(int64_t timestamp_us) override; + + private: + std::map<RtcEvent::Type, + std::function<std::string(rtc::ArrayView<const RtcEvent*>)>> + encoders_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_V3_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/var_int.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/var_int.cc new file mode 100644 index 0000000000..a84a233d6b --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/var_int.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018 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 "logging/rtc_event_log/encoder/var_int.h" + +#include "rtc_base/bitstream_reader.h" +#include "rtc_base/checks.h" + +// TODO(eladalon): Add unit tests. + +namespace webrtc { + +const size_t kMaxVarIntLengthBytes = 10; // ceil(64 / 7.0) is 10. + +std::string EncodeVarInt(uint64_t input) { + std::string output; + output.reserve(kMaxVarIntLengthBytes); + + do { + uint8_t byte = static_cast<uint8_t>(input & 0x7f); + input >>= 7; + if (input > 0) { + byte |= 0x80; + } + output += byte; + } while (input > 0); + + RTC_DCHECK_GE(output.size(), 1u); + RTC_DCHECK_LE(output.size(), kMaxVarIntLengthBytes); + + return output; +} + +// There is some code duplication between the flavors of this function. +// For performance's sake, it's best to just keep it. +std::pair<bool, absl::string_view> DecodeVarInt(absl::string_view input, + uint64_t* output) { + RTC_DCHECK(output); + + uint64_t decoded = 0; + for (size_t i = 0; i < input.length() && i < kMaxVarIntLengthBytes; ++i) { + decoded += (static_cast<uint64_t>(input[i] & 0x7f) + << static_cast<uint64_t>(7 * i)); + if (!(input[i] & 0x80)) { + *output = decoded; + return {true, input.substr(i + 1)}; + } + } + + return {false, input}; +} + +// There is some code duplication between the flavors of this function. +// For performance's sake, it's best to just keep it. +uint64_t DecodeVarInt(BitstreamReader& input) { + uint64_t decoded = 0; + for (size_t i = 0; i < kMaxVarIntLengthBytes; ++i) { + uint8_t byte = input.Read<uint8_t>(); + decoded += + (static_cast<uint64_t>(byte & 0x7f) << static_cast<uint64_t>(7 * i)); + if (!(byte & 0x80)) { + return decoded; + } + } + + input.Invalidate(); + return 0; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/var_int.h b/third_party/libwebrtc/logging/rtc_event_log/encoder/var_int.h new file mode 100644 index 0000000000..4624e046ba --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/var_int.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_ENCODER_VAR_INT_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_VAR_INT_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <string> +#include <utility> + +#include "absl/strings/string_view.h" +#include "rtc_base/bitstream_reader.h" + +namespace webrtc { + +extern const size_t kMaxVarIntLengthBytes; + +// Encode a given uint64_t as a varint. From least to most significant, +// each batch of seven bits are put into the lower bits of a byte, and the last +// remaining bit in that byte (the highest one) marks whether additional bytes +// follow (which happens if and only if there are other bits in `input` which +// are non-zero). +// Notes: If input == 0, one byte is used. If input is uint64_t::max, exactly +// kMaxVarIntLengthBytes are used. +std::string EncodeVarInt(uint64_t input); + +// Inverse of EncodeVarInt(). +// Returns true and the remaining (unread) slice of the input if decoding +// succeeds. Returns false otherwise and `output` is not modified. +std::pair<bool, absl::string_view> DecodeVarInt(absl::string_view input, + uint64_t* output); + +// Same as other version, but uses a BitstreamReader for input. +// If decoding is successful returns the decoded varint. +// If not successful, `input` reader is set into the failure state, return value +// is unspecified. +uint64_t DecodeVarInt(BitstreamReader& input); + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_VAR_INT_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.cc b/third_party/libwebrtc/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.cc new file mode 100644 index 0000000000..0c93e6226d --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.cc @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021 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 "logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h" + +#include <algorithm> + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" +#include "rtc_base/logging.h" + +using webrtc_event_logging::MaxUnsignedValueOfBitWidth; +using webrtc_event_logging::SignedBitWidth; +using webrtc_event_logging::UnsignedBitWidth; +using webrtc_event_logging::UnsignedDelta; + +namespace webrtc { + +FixedLengthEncodingParametersV3 +FixedLengthEncodingParametersV3::CalculateParameters( + uint64_t base, + const rtc::ArrayView<const uint64_t> values, + uint64_t value_bit_width, + bool values_optional) { + // As a special case, if all of the elements are identical to the base + // we just encode the base value with a special delta header. + if (std::all_of(values.cbegin(), values.cend(), + [base](uint64_t val) { return val == base; })) { + // Delta header with signed=true and delta_bitwidth=64 + return FixedLengthEncodingParametersV3(/*delta_bit_width=*/64, + /*signed_deltas=*/true, + values_optional, value_bit_width); + } + + const uint64_t bit_mask = MaxUnsignedValueOfBitWidth(value_bit_width); + + // Calculate the bitwidth required to encode all deltas when using a + // unsigned or signed represenation, respectively. For the unsigned + // representation, we just track the largest delta. For the signed + // representation, we have two possibilities for each delta; either + // going "forward" (i.e. current - previous) or "backwards" + // (i.e. previous - current) where both values are calculated with + // wrap around. We then track the largest positive and negative + // magnitude across the batch, assuming that we choose the smaller + // delta for each element. + uint64_t max_unsigned_delta = 0; + uint64_t max_positive_signed_delta = 0; + uint64_t min_negative_signed_delta = 0; + uint64_t prev = base; + for (uint64_t current : values) { + uint64_t positive_delta = UnsignedDelta(prev, current, bit_mask); + uint64_t negative_delta = UnsignedDelta(current, prev, bit_mask); + + max_unsigned_delta = std::max(max_unsigned_delta, positive_delta); + + if (positive_delta < negative_delta) { + max_positive_signed_delta = + std::max(max_positive_signed_delta, positive_delta); + } else { + min_negative_signed_delta = + std::max(min_negative_signed_delta, negative_delta); + } + + prev = current; + } + + // We now know the largest unsigned delta and the largest magnitudes of + // positive and negative signed deltas. Get the bitwidths required for + // each of the two encodings. + const uint64_t unsigned_delta_bit_width = + UnsignedBitWidth(max_unsigned_delta); + const uint64_t signed_delta_bit_width = + SignedBitWidth(max_positive_signed_delta, min_negative_signed_delta); + + // Note: Preference for unsigned if the two have the same width (efficiency). + bool use_signed_deltas = signed_delta_bit_width < unsigned_delta_bit_width; + uint64_t delta_bit_width = + use_signed_deltas ? signed_delta_bit_width : unsigned_delta_bit_width; + + // use_signed_deltas && delta_bit_width==64 is reserved for "all values + // equal". + RTC_DCHECK(!use_signed_deltas || delta_bit_width < 64); + + RTC_DCHECK(ValidParameters(delta_bit_width, use_signed_deltas, + values_optional, value_bit_width)); + return FixedLengthEncodingParametersV3(delta_bit_width, use_signed_deltas, + values_optional, value_bit_width); +} + +uint64_t FixedLengthEncodingParametersV3::DeltaHeaderAsInt() const { + uint64_t header = delta_bit_width_ - 1; + RTC_CHECK_LT(header, 1u << 6); + if (signed_deltas_) { + header += 1u << 6; + } + RTC_CHECK_LT(header, 1u << 7); + if (values_optional_) { + header += 1u << 7; + } + return header; +} + +absl::optional<FixedLengthEncodingParametersV3> +FixedLengthEncodingParametersV3::ParseDeltaHeader(uint64_t header, + uint64_t value_bit_width) { + uint64_t delta_bit_width = (header & ((1u << 6) - 1)) + 1; + bool signed_deltas = header & (1u << 6); + bool values_optional = header & (1u << 7); + + if (header >= (1u << 8)) { + RTC_LOG(LS_ERROR) << "Failed to parse delta header; unread bits remaining."; + return absl::nullopt; + } + + if (!ValidParameters(delta_bit_width, signed_deltas, values_optional, + value_bit_width)) { + RTC_LOG(LS_ERROR) << "Failed to parse delta header. Invalid combination of " + "values: delta_bit_width=" + << delta_bit_width << " signed_deltas=" << signed_deltas + << " values_optional=" << values_optional + << " value_bit_width=" << value_bit_width; + return absl::nullopt; + } + + return FixedLengthEncodingParametersV3(delta_bit_width, signed_deltas, + values_optional, value_bit_width); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h b/third_party/libwebrtc/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h new file mode 100644 index 0000000000..666fae1c63 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_FIXED_LENGTH_ENCODING_PARAMETERS_V3_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_FIXED_LENGTH_ENCODING_PARAMETERS_V3_H_ + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" + +namespace webrtc { + +// Parameters for fixed-size delta-encoding/decoding. +// These are tailored for the sequence which will be encoded (e.g. widths). +class FixedLengthEncodingParametersV3 final { + public: + static bool ValidParameters(uint64_t delta_bit_width, + bool signed_deltas, + bool values_optional, + uint64_t value_bit_width) { + return (1 <= delta_bit_width && delta_bit_width <= 64 && + 1 <= value_bit_width && value_bit_width <= 64 && + (delta_bit_width <= value_bit_width || + (signed_deltas && delta_bit_width == 64))); + } + + static FixedLengthEncodingParametersV3 CalculateParameters( + uint64_t base, + rtc::ArrayView<const uint64_t> values, + uint64_t value_bit_width, + bool values_optional); + static absl::optional<FixedLengthEncodingParametersV3> ParseDeltaHeader( + uint64_t header, + uint64_t value_bit_width); + + uint64_t DeltaHeaderAsInt() const; + + // Number of bits necessary to hold the widest(*) of the deltas between the + // values in the sequence. + // (*) - Widest might not be the largest, if signed deltas are used. + uint64_t delta_bit_width() const { return delta_bit_width_; } + + // Whether deltas are signed. + bool signed_deltas() const { return signed_deltas_; } + + // Whether the values of the sequence are optional. That is, it may be + // that some of them do not have a value (not even a sentinel value indicating + // invalidity). + bool values_optional() const { return values_optional_; } + + // Whether all values are equal. 64-bit signed deltas are assumed to not + // occur, since those could equally well be represented using 64 bit unsigned + // deltas. + bool values_equal() const { + return delta_bit_width() == 64 && signed_deltas(); + } + + // Number of bits necessary to hold the largest value in the sequence. + uint64_t value_bit_width() const { return value_bit_width_; } + + // Masks where only the bits relevant to the deltas/values are turned on. + uint64_t delta_mask() const { return delta_mask_; } + uint64_t value_mask() const { return value_mask_; } + + private: + FixedLengthEncodingParametersV3(uint64_t delta_bit_width, + bool signed_deltas, + bool values_optional, + uint64_t value_bit_width) + : delta_bit_width_(delta_bit_width), + signed_deltas_(signed_deltas), + values_optional_(values_optional), + value_bit_width_(value_bit_width), + delta_mask_( + webrtc_event_logging::MaxUnsignedValueOfBitWidth(delta_bit_width_)), + value_mask_(webrtc_event_logging::MaxUnsignedValueOfBitWidth( + value_bit_width_)) {} + + uint64_t delta_bit_width_; + bool signed_deltas_; + bool values_optional_; + uint64_t value_bit_width_; + + uint64_t delta_mask_; + uint64_t value_mask_; +}; + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_FIXED_LENGTH_ENCODING_PARAMETERS_V3_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/logged_rtp_rtcp.h b/third_party/libwebrtc/logging/rtc_event_log/events/logged_rtp_rtcp.h new file mode 100644 index 0000000000..fe5fcfd765 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/logged_rtp_rtcp.h @@ -0,0 +1,261 @@ +/* + * Copyright 2022 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_LOGGED_RTP_RTCP_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_LOGGED_RTP_RTCP_H_ + +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtp_headers.h" +#include "api/units/timestamp.h" +#include "modules/rtp_rtcp/source/rtcp_packet/bye.h" +#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h" +#include "modules/rtp_rtcp/source/rtcp_packet/fir.h" +#include "modules/rtp_rtcp/source/rtcp_packet/loss_notification.h" +#include "modules/rtp_rtcp/source/rtcp_packet/nack.h" +#include "modules/rtp_rtcp/source/rtcp_packet/pli.h" +#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/remb.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" + +namespace webrtc { + +struct LoggedRtpPacket { + LoggedRtpPacket(Timestamp timestamp, + RTPHeader header, + size_t header_length, + size_t total_length) + : timestamp(timestamp), + header(header), + header_length(header_length), + total_length(total_length) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp; + // TODO(terelius): This allocates space for 15 CSRCs even if none are used. + RTPHeader header; + // RTPHeader::extension is a mess, save DD wire format instead. + std::vector<uint8_t> dependency_descriptor_wire_format; + size_t header_length; + size_t total_length; +}; + +struct LoggedRtpPacketIncoming { + LoggedRtpPacketIncoming(Timestamp timestamp, + RTPHeader header, + size_t header_length, + size_t total_length) + : rtp(timestamp, header, header_length, total_length) {} + int64_t log_time_us() const { return rtp.timestamp.us(); } + int64_t log_time_ms() const { return rtp.timestamp.ms(); } + Timestamp log_time() const { return rtp.timestamp; } + + LoggedRtpPacket rtp; +}; + +struct LoggedRtpPacketOutgoing { + LoggedRtpPacketOutgoing(Timestamp timestamp, + RTPHeader header, + size_t header_length, + size_t total_length) + : rtp(timestamp, header, header_length, total_length) {} + int64_t log_time_us() const { return rtp.timestamp.us(); } + int64_t log_time_ms() const { return rtp.timestamp.ms(); } + Timestamp log_time() const { return rtp.timestamp; } + + LoggedRtpPacket rtp; +}; + +struct LoggedRtcpPacket { + LoggedRtcpPacket(Timestamp timestamp, const std::vector<uint8_t>& packet) + : timestamp(timestamp), raw_data(packet) {} + LoggedRtcpPacket(Timestamp timestamp, absl::string_view packet) + : timestamp(timestamp), raw_data(packet.size()) { + memcpy(raw_data.data(), packet.data(), packet.size()); + } + + LoggedRtcpPacket(const LoggedRtcpPacket& rhs) = default; + + ~LoggedRtcpPacket() = default; + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp; + std::vector<uint8_t> raw_data; +}; + +struct LoggedRtcpPacketIncoming { + LoggedRtcpPacketIncoming(Timestamp timestamp, + const std::vector<uint8_t>& packet) + : rtcp(timestamp, packet) {} + LoggedRtcpPacketIncoming(Timestamp timestamp, absl::string_view packet) + : rtcp(timestamp, packet) {} + + int64_t log_time_us() const { return rtcp.timestamp.us(); } + int64_t log_time_ms() const { return rtcp.timestamp.ms(); } + Timestamp log_time() const { return rtcp.timestamp; } + + LoggedRtcpPacket rtcp; +}; + +struct LoggedRtcpPacketOutgoing { + LoggedRtcpPacketOutgoing(Timestamp timestamp, + const std::vector<uint8_t>& packet) + : rtcp(timestamp, packet) {} + LoggedRtcpPacketOutgoing(Timestamp timestamp, absl::string_view packet) + : rtcp(timestamp, packet) {} + + int64_t log_time_us() const { return rtcp.timestamp.us(); } + int64_t log_time_ms() const { return rtcp.timestamp.ms(); } + Timestamp log_time() const { return rtcp.timestamp; } + + LoggedRtcpPacket rtcp; +}; + +struct LoggedRtcpPacketReceiverReport { + LoggedRtcpPacketReceiverReport() = default; + LoggedRtcpPacketReceiverReport(Timestamp timestamp, + const rtcp::ReceiverReport& rr) + : timestamp(timestamp), rr(rr) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::ReceiverReport rr; +}; + +struct LoggedRtcpPacketSenderReport { + LoggedRtcpPacketSenderReport() = default; + LoggedRtcpPacketSenderReport(Timestamp timestamp, + const rtcp::SenderReport& sr) + : timestamp(timestamp), sr(sr) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::SenderReport sr; +}; + +struct LoggedRtcpPacketExtendedReports { + LoggedRtcpPacketExtendedReports() = default; + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::ExtendedReports xr; +}; + +struct LoggedRtcpPacketRemb { + LoggedRtcpPacketRemb() = default; + LoggedRtcpPacketRemb(Timestamp timestamp, const rtcp::Remb& remb) + : timestamp(timestamp), remb(remb) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::Remb remb; +}; + +struct LoggedRtcpPacketNack { + LoggedRtcpPacketNack() = default; + LoggedRtcpPacketNack(Timestamp timestamp, const rtcp::Nack& nack) + : timestamp(timestamp), nack(nack) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::Nack nack; +}; + +struct LoggedRtcpPacketFir { + LoggedRtcpPacketFir() = default; + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::Fir fir; +}; + +struct LoggedRtcpPacketPli { + LoggedRtcpPacketPli() = default; + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::Pli pli; +}; + +struct LoggedRtcpPacketTransportFeedback { + LoggedRtcpPacketTransportFeedback() + : transport_feedback(/*include_timestamps=*/true) {} + LoggedRtcpPacketTransportFeedback( + Timestamp timestamp, + const rtcp::TransportFeedback& transport_feedback) + : timestamp(timestamp), transport_feedback(transport_feedback) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::TransportFeedback transport_feedback; +}; + +struct LoggedRtcpPacketLossNotification { + LoggedRtcpPacketLossNotification() = default; + LoggedRtcpPacketLossNotification( + Timestamp timestamp, + const rtcp::LossNotification& loss_notification) + : timestamp(timestamp), loss_notification(loss_notification) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::LossNotification loss_notification; +}; + +struct LoggedRtcpPacketBye { + LoggedRtcpPacketBye() = default; + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtcp::Bye bye; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_LOGGED_RTP_RTCP_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_alr_state.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_alr_state.cc new file mode 100644 index 0000000000..25941eb16b --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_alr_state.cc @@ -0,0 +1,38 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_alr_state.h" + +#include "absl/memory/memory.h" + +namespace webrtc { +constexpr RtcEvent::Type RtcEventAlrState::kType; +constexpr RtcEventDefinition<RtcEventAlrState, LoggedAlrStateEvent, bool> + RtcEventAlrState::definition_; + +RtcEventAlrState::RtcEventAlrState(bool in_alr) : in_alr_(in_alr) {} + +RtcEventAlrState::RtcEventAlrState(const RtcEventAlrState& other) + : RtcEvent(other.timestamp_us_), in_alr_(other.in_alr_) {} + +RtcEventAlrState::~RtcEventAlrState() = default; + +std::unique_ptr<RtcEventAlrState> RtcEventAlrState::Copy() const { + return absl::WrapUnique<RtcEventAlrState>(new RtcEventAlrState(*this)); +} + +RtcEventLogParseStatus RtcEventAlrState::Parse( + absl::string_view s, + bool batched, + std::vector<LoggedAlrStateEvent>& output) { + return RtcEventAlrState::definition_.ParseBatch(s, batched, output); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_alr_state.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_alr_state.h new file mode 100644 index 0000000000..9f595ecd90 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_alr_state.h @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ALR_STATE_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ALR_STATE_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_definition.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" + +namespace webrtc { + +struct LoggedAlrStateEvent { + LoggedAlrStateEvent() = default; + LoggedAlrStateEvent(Timestamp timestamp, bool in_alr) + : timestamp(timestamp), in_alr(in_alr) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + bool in_alr; +}; + +class RtcEventAlrState final : public RtcEvent { + public: + static constexpr Type kType = Type::AlrStateEvent; + + explicit RtcEventAlrState(bool in_alr); + ~RtcEventAlrState() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventAlrState> Copy() const; + + bool in_alr() const { return in_alr_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + return RtcEventAlrState::definition_.EncodeBatch(batch); + } + + static RtcEventLogParseStatus Parse(absl::string_view s, + bool batched, + std::vector<LoggedAlrStateEvent>& output); + + private: + RtcEventAlrState(const RtcEventAlrState& other); + + const bool in_alr_; + + static constexpr RtcEventDefinition<RtcEventAlrState, + LoggedAlrStateEvent, + bool> + definition_{{"AlrState", RtcEventAlrState::kType}, + {&RtcEventAlrState::in_alr_, + &LoggedAlrStateEvent::in_alr, + {"in_alr", /*id=*/1, FieldType::kFixed8, /*width=*/1}}}; +}; + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ALR_STATE_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.cc new file mode 100644 index 0000000000..5f2d55c357 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.cc @@ -0,0 +1,39 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h" + +#include <utility> + +#include "absl/memory/memory.h" +#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +RtcEventAudioNetworkAdaptation::RtcEventAudioNetworkAdaptation( + std::unique_ptr<AudioEncoderRuntimeConfig> config) + : config_(std::move(config)) { + RTC_DCHECK(config_); +} + +RtcEventAudioNetworkAdaptation::RtcEventAudioNetworkAdaptation( + const RtcEventAudioNetworkAdaptation& other) + : RtcEvent(other.timestamp_us_), + config_(std::make_unique<AudioEncoderRuntimeConfig>(*other.config_)) {} + +RtcEventAudioNetworkAdaptation::~RtcEventAudioNetworkAdaptation() = default; + +std::unique_ptr<RtcEventAudioNetworkAdaptation> +RtcEventAudioNetworkAdaptation::Copy() const { + return absl::WrapUnique(new RtcEventAudioNetworkAdaptation(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h new file mode 100644 index 0000000000..d4cae3abfa --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_NETWORK_ADAPTATION_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_NETWORK_ADAPTATION_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" + +namespace webrtc { + +struct LoggedAudioNetworkAdaptationEvent { + LoggedAudioNetworkAdaptationEvent() = default; + LoggedAudioNetworkAdaptationEvent(Timestamp timestamp, + const AudioEncoderRuntimeConfig& config) + : timestamp(timestamp), config(config) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + AudioEncoderRuntimeConfig config; +}; + +struct AudioEncoderRuntimeConfig; + +class RtcEventAudioNetworkAdaptation final : public RtcEvent { + public: + static constexpr Type kType = Type::AudioNetworkAdaptation; + + explicit RtcEventAudioNetworkAdaptation( + std::unique_ptr<AudioEncoderRuntimeConfig> config); + ~RtcEventAudioNetworkAdaptation() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventAudioNetworkAdaptation> Copy() const; + + const AudioEncoderRuntimeConfig& config() const { return *config_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedAudioNetworkAdaptationEvent>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventAudioNetworkAdaptation(const RtcEventAudioNetworkAdaptation& other); + + const std::unique_ptr<const AudioEncoderRuntimeConfig> config_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_NETWORK_ADAPTATION_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_playout.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_playout.cc new file mode 100644 index 0000000000..21a3f9266c --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_playout.cc @@ -0,0 +1,32 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_audio_playout.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +constexpr RtcEventDefinition<RtcEventAudioPlayout, + LoggedAudioPlayoutEvent, + uint32_t> + RtcEventAudioPlayout::definition_; + +RtcEventAudioPlayout::RtcEventAudioPlayout(uint32_t ssrc) : ssrc_(ssrc) {} + +RtcEventAudioPlayout::RtcEventAudioPlayout(const RtcEventAudioPlayout& other) + : RtcEvent(other.timestamp_us_), ssrc_(other.ssrc_) {} + +std::unique_ptr<RtcEventAudioPlayout> RtcEventAudioPlayout::Copy() const { + return absl::WrapUnique<RtcEventAudioPlayout>( + new RtcEventAudioPlayout(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_playout.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_playout.h new file mode 100644 index 0000000000..196c3ca247 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_playout.h @@ -0,0 +1,88 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_PLAYOUT_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_PLAYOUT_H_ + +#include <stdint.h> + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_definition.h" + +namespace webrtc { + +struct LoggedAudioPlayoutEvent { + LoggedAudioPlayoutEvent() = default; + LoggedAudioPlayoutEvent(Timestamp timestamp, uint32_t ssrc) + : timestamp(timestamp), ssrc(ssrc) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + uint32_t ssrc; +}; + +class RtcEventAudioPlayout final : public RtcEvent { + public: + static constexpr Type kType = Type::AudioPlayout; + + explicit RtcEventAudioPlayout(uint32_t ssrc); + ~RtcEventAudioPlayout() override = default; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventAudioPlayout> Copy() const; + + uint32_t ssrc() const { return ssrc_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + return RtcEventAudioPlayout::definition_.EncodeBatch(batch); + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::map<uint32_t, std::vector<LoggedAudioPlayoutEvent>>& output) { + std::vector<LoggedAudioPlayoutEvent> temp_output; + auto status = RtcEventAudioPlayout::definition_.ParseBatch( + encoded_bytes, batched, temp_output); + for (const LoggedAudioPlayoutEvent& event : temp_output) { + output[event.ssrc].push_back(event); + } + return status; + } + + private: + RtcEventAudioPlayout(const RtcEventAudioPlayout& other); + + const uint32_t ssrc_; + + static constexpr RtcEventDefinition<RtcEventAudioPlayout, + LoggedAudioPlayoutEvent, + uint32_t> + definition_{{"AudioPlayout", RtcEventAudioPlayout::kType}, + {&RtcEventAudioPlayout::ssrc_, + &LoggedAudioPlayoutEvent::ssrc, + {"ssrc", /*id=*/1, FieldType::kFixed32, /*width=*/32}}}; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_PLAYOUT_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.cc new file mode 100644 index 0000000000..87caaff098 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.cc @@ -0,0 +1,40 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h" + +#include <utility> + +#include "absl/memory/memory.h" +#include "logging/rtc_event_log/rtc_stream_config.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +RtcEventAudioReceiveStreamConfig::RtcEventAudioReceiveStreamConfig( + std::unique_ptr<rtclog::StreamConfig> config) + : config_(std::move(config)) { + RTC_DCHECK(config_); +} + +RtcEventAudioReceiveStreamConfig::RtcEventAudioReceiveStreamConfig( + const RtcEventAudioReceiveStreamConfig& other) + : RtcEvent(other.timestamp_us_), + config_(std::make_unique<rtclog::StreamConfig>(*other.config_)) {} + +RtcEventAudioReceiveStreamConfig::~RtcEventAudioReceiveStreamConfig() = default; + +std::unique_ptr<RtcEventAudioReceiveStreamConfig> +RtcEventAudioReceiveStreamConfig::Copy() const { + return absl::WrapUnique<RtcEventAudioReceiveStreamConfig>( + new RtcEventAudioReceiveStreamConfig(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h new file mode 100644 index 0000000000..9863e235af --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_RECEIVE_STREAM_CONFIG_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_RECEIVE_STREAM_CONFIG_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/rtc_stream_config.h" + +namespace webrtc { + +struct LoggedAudioRecvConfig { + LoggedAudioRecvConfig() = default; + LoggedAudioRecvConfig(Timestamp timestamp, const rtclog::StreamConfig config) + : timestamp(timestamp), config(config) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtclog::StreamConfig config; +}; + +class RtcEventAudioReceiveStreamConfig final : public RtcEvent { + public: + static constexpr Type kType = Type::AudioReceiveStreamConfig; + + explicit RtcEventAudioReceiveStreamConfig( + std::unique_ptr<rtclog::StreamConfig> config); + ~RtcEventAudioReceiveStreamConfig() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return true; } + + std::unique_ptr<RtcEventAudioReceiveStreamConfig> Copy() const; + + const rtclog::StreamConfig& config() const { return *config_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedAudioRecvConfig>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventAudioReceiveStreamConfig( + const RtcEventAudioReceiveStreamConfig& other); + + const std::unique_ptr<const rtclog::StreamConfig> config_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_RECEIVE_STREAM_CONFIG_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.cc new file mode 100644 index 0000000000..681ae11e63 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.cc @@ -0,0 +1,40 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h" + +#include <utility> + +#include "absl/memory/memory.h" +#include "logging/rtc_event_log/rtc_stream_config.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +RtcEventAudioSendStreamConfig::RtcEventAudioSendStreamConfig( + std::unique_ptr<rtclog::StreamConfig> config) + : config_(std::move(config)) { + RTC_DCHECK(config_); +} + +RtcEventAudioSendStreamConfig::RtcEventAudioSendStreamConfig( + const RtcEventAudioSendStreamConfig& other) + : RtcEvent(other.timestamp_us_), + config_(std::make_unique<rtclog::StreamConfig>(*other.config_)) {} + +RtcEventAudioSendStreamConfig::~RtcEventAudioSendStreamConfig() = default; + +std::unique_ptr<RtcEventAudioSendStreamConfig> +RtcEventAudioSendStreamConfig::Copy() const { + return absl::WrapUnique<RtcEventAudioSendStreamConfig>( + new RtcEventAudioSendStreamConfig(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h new file mode 100644 index 0000000000..550723bcf0 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_SEND_STREAM_CONFIG_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_SEND_STREAM_CONFIG_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/rtc_stream_config.h" + +namespace webrtc { + +struct LoggedAudioSendConfig { + LoggedAudioSendConfig() = default; + LoggedAudioSendConfig(Timestamp timestamp, const rtclog::StreamConfig config) + : timestamp(timestamp), config(config) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtclog::StreamConfig config; +}; + +class RtcEventAudioSendStreamConfig final : public RtcEvent { + public: + static constexpr Type kType = Type::AudioSendStreamConfig; + + explicit RtcEventAudioSendStreamConfig( + std::unique_ptr<rtclog::StreamConfig> config); + ~RtcEventAudioSendStreamConfig() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return true; } + + std::unique_ptr<RtcEventAudioSendStreamConfig> Copy() const; + + const rtclog::StreamConfig& config() const { return *config_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedAudioSendConfig>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventAudioSendStreamConfig(const RtcEventAudioSendStreamConfig& other); + + const std::unique_ptr<const rtclog::StreamConfig> config_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_AUDIO_SEND_STREAM_CONFIG_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_begin_log.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_begin_log.cc new file mode 100644 index 0000000000..49b9effa9e --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_begin_log.cc @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 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 "logging/rtc_event_log/events/rtc_event_begin_log.h" + +#include "absl/strings/string_view.h" + +namespace webrtc { +constexpr RtcEvent::Type RtcEventBeginLog::kType; +constexpr EventParameters RtcEventBeginLog::event_params_; +constexpr FieldParameters RtcEventBeginLog::utc_start_time_params_; + +RtcEventBeginLog::RtcEventBeginLog(Timestamp timestamp, + Timestamp utc_start_time) + : RtcEvent(timestamp.us()), utc_start_time_ms_(utc_start_time.ms()) {} + +RtcEventBeginLog::RtcEventBeginLog(const RtcEventBeginLog& other) + : RtcEvent(other.timestamp_us_) {} + +RtcEventBeginLog::~RtcEventBeginLog() = default; + +std::string RtcEventBeginLog::Encode(rtc::ArrayView<const RtcEvent*> batch) { + EventEncoder encoder(event_params_, batch); + + encoder.EncodeField( + utc_start_time_params_, + ExtractRtcEventMember(batch, &RtcEventBeginLog::utc_start_time_ms_)); + + return encoder.AsString(); +} + +RtcEventLogParseStatus RtcEventBeginLog::Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedStartEvent>& output) { + EventParser parser; + auto status = parser.Initialize(encoded_bytes, batched); + if (!status.ok()) + return status; + + rtc::ArrayView<LoggedStartEvent> output_batch = + ExtendLoggedBatch(output, parser.NumEventsInBatch()); + + constexpr FieldParameters timestamp_params{ + "timestamp_ms", FieldParameters::kTimestampField, FieldType::kVarInt, 64}; + RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>> result = + parser.ParseNumericField(timestamp_params); + if (!result.ok()) + return result.status(); + status = PopulateRtcEventTimestamp( + result.value(), &LoggedStartEvent::timestamp, output_batch); + if (!status.ok()) + return status; + + result = parser.ParseNumericField(utc_start_time_params_); + if (!result.ok()) + return result.status(); + status = PopulateRtcEventTimestamp( + result.value(), &LoggedStartEvent::utc_start_time, output_batch); + if (!status.ok()) + return status; + + return RtcEventLogParseStatus::Success(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_begin_log.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_begin_log.h new file mode 100644 index 0000000000..f3b74c117e --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_begin_log.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BEGIN_LOG_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BEGIN_LOG_H_ + +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" + +namespace webrtc { + +struct LoggedStartEvent { + LoggedStartEvent() = default; + + explicit LoggedStartEvent(Timestamp timestamp) + : LoggedStartEvent(timestamp, timestamp) {} + + LoggedStartEvent(Timestamp timestamp, Timestamp utc_start_time) + : timestamp(timestamp), utc_start_time(utc_start_time) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp utc_time() const { return utc_start_time; } + + Timestamp timestamp = Timestamp::PlusInfinity(); + Timestamp utc_start_time = Timestamp::PlusInfinity(); +}; + +class RtcEventBeginLog final : public RtcEvent { + public: + static constexpr Type kType = Type::BeginV3Log; + + RtcEventBeginLog(Timestamp timestamp, Timestamp utc_start_time); + ~RtcEventBeginLog() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch); + + static RtcEventLogParseStatus Parse(absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedStartEvent>& output); + + private: + RtcEventBeginLog(const RtcEventBeginLog& other); + + int64_t utc_start_time_ms_; + + static constexpr EventParameters event_params_{"BeginLog", + RtcEventBeginLog::kType}; + static constexpr FieldParameters utc_start_time_params_{ + "utc_start_time_ms", /*id=*/1, FieldType::kVarInt, /*width=*/64}; +}; + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BEGIN_LOG_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.cc new file mode 100644 index 0000000000..0e98b2ff11 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.cc @@ -0,0 +1,43 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h" + +#include "absl/memory/memory.h" +#include "api/network_state_predictor.h" + +namespace webrtc { + +constexpr RtcEventDefinition<RtcEventBweUpdateDelayBased, + LoggedBweDelayBasedUpdate, + int32_t, + BandwidthUsage> + RtcEventBweUpdateDelayBased::definition_; + +RtcEventBweUpdateDelayBased::RtcEventBweUpdateDelayBased( + int32_t bitrate_bps, + BandwidthUsage detector_state) + : bitrate_bps_(bitrate_bps), detector_state_(detector_state) {} + +RtcEventBweUpdateDelayBased::RtcEventBweUpdateDelayBased( + const RtcEventBweUpdateDelayBased& other) + : RtcEvent(other.timestamp_us_), + bitrate_bps_(other.bitrate_bps_), + detector_state_(other.detector_state_) {} + +RtcEventBweUpdateDelayBased::~RtcEventBweUpdateDelayBased() = default; + +std::unique_ptr<RtcEventBweUpdateDelayBased> RtcEventBweUpdateDelayBased::Copy() + const { + return absl::WrapUnique<RtcEventBweUpdateDelayBased>( + new RtcEventBweUpdateDelayBased(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h new file mode 100644 index 0000000000..796f119388 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h @@ -0,0 +1,136 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BWE_UPDATE_DELAY_BASED_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BWE_UPDATE_DELAY_BASED_H_ + +#include <stdint.h> + +#include <limits> +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/network_state_predictor.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_definition.h" + +namespace webrtc { + +// Separate the event log encoding from the enum values. +// As long as the enum values are the same as the encodings, +// the two conversion functions can be compiled to (roughly) +// a range check each. +template <> +class RtcEventLogEnum<BandwidthUsage> { + static constexpr uint64_t kBwNormal = 0; + static constexpr uint64_t kBwUnderusing = 1; + static constexpr uint64_t kBwOverusing = 2; + + public: + static uint64_t Encode(BandwidthUsage x) { + switch (x) { + case BandwidthUsage::kBwNormal: + return kBwNormal; + case BandwidthUsage::kBwUnderusing: + return kBwUnderusing; + case BandwidthUsage::kBwOverusing: + return kBwOverusing; + case BandwidthUsage::kLast: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return std::numeric_limits<uint64_t>::max(); + } + static RtcEventLogParseStatusOr<BandwidthUsage> Decode(uint64_t x) { + switch (x) { + case kBwNormal: + return BandwidthUsage::kBwNormal; + case kBwUnderusing: + return BandwidthUsage::kBwUnderusing; + case kBwOverusing: + return BandwidthUsage::kBwOverusing; + } + return RtcEventLogParseStatus::Error("Failed to decode BandwidthUsage enum", + __FILE__, __LINE__); + } +}; + +struct LoggedBweDelayBasedUpdate { + LoggedBweDelayBasedUpdate() = default; + LoggedBweDelayBasedUpdate(Timestamp timestamp, + int32_t bitrate_bps, + BandwidthUsage detector_state) + : timestamp(timestamp), + bitrate_bps(bitrate_bps), + detector_state(detector_state) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int32_t bitrate_bps; + BandwidthUsage detector_state; +}; + +class RtcEventBweUpdateDelayBased final : public RtcEvent { + public: + static constexpr Type kType = Type::BweUpdateDelayBased; + + RtcEventBweUpdateDelayBased(int32_t bitrate_bps, + BandwidthUsage detector_state); + ~RtcEventBweUpdateDelayBased() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventBweUpdateDelayBased> Copy() const; + + int32_t bitrate_bps() const { return bitrate_bps_; } + BandwidthUsage detector_state() const { return detector_state_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + return RtcEventBweUpdateDelayBased::definition_.EncodeBatch(batch); + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedBweDelayBasedUpdate>& output) { + return RtcEventBweUpdateDelayBased::definition_.ParseBatch(encoded_bytes, + batched, output); + } + + private: + RtcEventBweUpdateDelayBased(const RtcEventBweUpdateDelayBased& other); + + const int32_t bitrate_bps_; + const BandwidthUsage detector_state_; + + static constexpr RtcEventDefinition<RtcEventBweUpdateDelayBased, + LoggedBweDelayBasedUpdate, + int32_t, + BandwidthUsage> + definition_{ + {"BweDelayBased", RtcEventBweUpdateDelayBased::kType}, + {&RtcEventBweUpdateDelayBased::bitrate_bps_, + &LoggedBweDelayBasedUpdate::bitrate_bps, + {"bitrate_bps", /*id=*/1, FieldType::kVarInt, /*width=*/32}}, + {&RtcEventBweUpdateDelayBased::detector_state_, + &LoggedBweDelayBasedUpdate::detector_state, + {"detector_state", /*id=*/2, FieldType::kVarInt, /*width=*/64}}}; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BWE_UPDATE_DELAY_BASED_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.cc new file mode 100644 index 0000000000..44524ab033 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.cc @@ -0,0 +1,39 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventBweUpdateLossBased::RtcEventBweUpdateLossBased(int32_t bitrate_bps, + uint8_t fraction_loss, + int32_t total_packets) + : bitrate_bps_(bitrate_bps), + fraction_loss_(fraction_loss), + total_packets_(total_packets) {} + +RtcEventBweUpdateLossBased::RtcEventBweUpdateLossBased( + const RtcEventBweUpdateLossBased& other) + : RtcEvent(other.timestamp_us_), + bitrate_bps_(other.bitrate_bps_), + fraction_loss_(other.fraction_loss_), + total_packets_(other.total_packets_) {} + +RtcEventBweUpdateLossBased::~RtcEventBweUpdateLossBased() = default; + +std::unique_ptr<RtcEventBweUpdateLossBased> RtcEventBweUpdateLossBased::Copy() + const { + return absl::WrapUnique<RtcEventBweUpdateLossBased>( + new RtcEventBweUpdateLossBased(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h new file mode 100644 index 0000000000..fd41b316e0 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h @@ -0,0 +1,89 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BWE_UPDATE_LOSS_BASED_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BWE_UPDATE_LOSS_BASED_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +struct LoggedBweLossBasedUpdate { + LoggedBweLossBasedUpdate() = default; + LoggedBweLossBasedUpdate(Timestamp timestamp, + int32_t bitrate_bps, + uint8_t fraction_lost, + int32_t expected_packets) + : timestamp(timestamp), + bitrate_bps(bitrate_bps), + fraction_lost(fraction_lost), + expected_packets(expected_packets) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int32_t bitrate_bps; + uint8_t fraction_lost; + int32_t expected_packets; +}; + +class RtcEventBweUpdateLossBased final : public RtcEvent { + public: + static constexpr Type kType = Type::BweUpdateLossBased; + + RtcEventBweUpdateLossBased(int32_t bitrate_bps_, + uint8_t fraction_loss_, + int32_t total_packets_); + ~RtcEventBweUpdateLossBased() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventBweUpdateLossBased> Copy() const; + + int32_t bitrate_bps() const { return bitrate_bps_; } + uint8_t fraction_loss() const { return fraction_loss_; } + int32_t total_packets() const { return total_packets_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedBweLossBasedUpdate>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventBweUpdateLossBased(const RtcEventBweUpdateLossBased& other); + + const int32_t bitrate_bps_; + const uint8_t fraction_loss_; + const int32_t total_packets_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BWE_UPDATE_LOSS_BASED_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_definition.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_definition.h new file mode 100644 index 0000000000..8688c5fc7b --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_definition.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DEFINITION_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DEFINITION_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +template <typename EventType, typename LoggedType, typename T> +struct RtcEventFieldDefinition { + const T EventType::*event_member; + T LoggedType::*logged_member; + FieldParameters params; +}; + +// Base case +template <typename EventType, typename LoggedType, typename... Ts> +class RtcEventDefinitionImpl { + public: + void EncodeImpl(EventEncoder&, rtc::ArrayView<const RtcEvent*>) const {} + RtcEventLogParseStatus ParseImpl(EventParser&, + rtc::ArrayView<LoggedType>) const { + return RtcEventLogParseStatus::Success(); + } +}; + +// Recursive case +template <typename EventType, typename LoggedType, typename T, typename... Ts> +class RtcEventDefinitionImpl<EventType, LoggedType, T, Ts...> { + public: + constexpr RtcEventDefinitionImpl( + RtcEventFieldDefinition<EventType, LoggedType, T> field, + RtcEventFieldDefinition<EventType, LoggedType, Ts>... rest) + : field_(field), rest_(rest...) {} + + void EncodeImpl(EventEncoder& encoder, + rtc::ArrayView<const RtcEvent*> batch) const { + auto values = ExtractRtcEventMember(batch, field_.event_member); + encoder.EncodeField(field_.params, values); + rest_.EncodeImpl(encoder, batch); + } + + RtcEventLogParseStatus ParseImpl( + EventParser& parser, + rtc::ArrayView<LoggedType> output_batch) const { + RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>> result = + parser.ParseNumericField(field_.params); + if (!result.ok()) + return result.status(); + auto status = PopulateRtcEventMember(result.value(), field_.logged_member, + output_batch); + if (!status.ok()) + return status; + + return rest_.ParseImpl(parser, output_batch); + } + + private: + RtcEventFieldDefinition<EventType, LoggedType, T> field_; + RtcEventDefinitionImpl<EventType, LoggedType, Ts...> rest_; +}; + +// The RtcEventDefinition sets up a mapping between the fields +// in an RtcEvent and the corresponding fields in the parsed struct. +// For example, an RtcFoo class containing two fields; `uint32_t bar` +// and `bool baz` (a log timestamp is always implicitly added) +// might have a definition +// RtcEventDefinition<RtcFoo, LoggedFoo, uint32_t, bool>( +// {"foo", RtcFoo::Type}, +// {&RtcFoo::bar_, &LoggedFoo::bar, {"bar", 1, FieldType::kVarInt, 32}}, +// {&RtcFoo::baz_, &LoggedFoo::baz, {"baz", 2, FieldType::kFixed8, 1}}, +// ); +// In addition to defining string names to aid debugging, +// this specifies that +// * RtcFoo::Type uniquely identifies an RtcFoo in the encoded stream +// * The `bar` field has ID 1, is encoded as a VarInt +// (when not delta compressed), and wraps around after 32 bits. +// * The `baz` field has ID 2, is encoded as an 8-bit field +// (when not delta compressed), and wraps around after 1 bit. +// Note that the numerical field and event IDs can't be changed since +// that would break compatibility with old logs. +// In most cases (including all cases where wrap around isn't +// expected), the wrap around should be equal to the bitwidth of +// the field. +template <typename EventType, typename LoggedType, typename... Ts> +class RtcEventDefinition { + public: + constexpr RtcEventDefinition( + EventParameters params, + RtcEventFieldDefinition<EventType, LoggedType, Ts>... fields) + : params_(params), fields_(fields...) {} + + std::string EncodeBatch(rtc::ArrayView<const RtcEvent*> batch) const { + EventEncoder encoder(params_, batch); + fields_.EncodeImpl(encoder, batch); + return encoder.AsString(); + } + + RtcEventLogParseStatus ParseBatch(absl::string_view s, + bool batched, + std::vector<LoggedType>& output) const { + EventParser parser; + auto status = parser.Initialize(s, batched); + if (!status.ok()) + return status; + + rtc::ArrayView<LoggedType> output_batch = + ExtendLoggedBatch(output, parser.NumEventsInBatch()); + + constexpr FieldParameters timestamp_params{"timestamp_ms", + FieldParameters::kTimestampField, + FieldType::kVarInt, 64}; + RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>> result = + parser.ParseNumericField(timestamp_params); + if (!result.ok()) + return result.status(); + status = PopulateRtcEventTimestamp(result.value(), &LoggedType::timestamp, + output_batch); + if (!status.ok()) + return status; + + return fields_.ParseImpl(parser, output_batch); + } + + private: + EventParameters params_; + RtcEventDefinitionImpl<EventType, LoggedType, Ts...> fields_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DEFINITION_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_dtls_transport_state.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_dtls_transport_state.cc new file mode 100644 index 0000000000..f00342df72 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_dtls_transport_state.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 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 "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventDtlsTransportState::RtcEventDtlsTransportState(DtlsTransportState state) + : dtls_transport_state_(state) {} + +RtcEventDtlsTransportState::RtcEventDtlsTransportState( + const RtcEventDtlsTransportState& other) + : RtcEvent(other.timestamp_us_), + dtls_transport_state_(other.dtls_transport_state_) {} + +RtcEventDtlsTransportState::~RtcEventDtlsTransportState() = default; + +std::unique_ptr<RtcEventDtlsTransportState> RtcEventDtlsTransportState::Copy() + const { + return absl::WrapUnique<RtcEventDtlsTransportState>( + new RtcEventDtlsTransportState(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h new file mode 100644 index 0000000000..b9af213256 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_TRANSPORT_STATE_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_TRANSPORT_STATE_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/dtls_transport_interface.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +struct LoggedDtlsTransportState { + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + DtlsTransportState dtls_transport_state; +}; + +class RtcEventDtlsTransportState : public RtcEvent { + public: + static constexpr Type kType = Type::DtlsTransportState; + + explicit RtcEventDtlsTransportState(DtlsTransportState state); + ~RtcEventDtlsTransportState() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventDtlsTransportState> Copy() const; + + DtlsTransportState dtls_transport_state() const { + return dtls_transport_state_; + } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedDtlsTransportState>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventDtlsTransportState(const RtcEventDtlsTransportState& other); + + const DtlsTransportState dtls_transport_state_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_TRANSPORT_STATE_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_dtls_writable_state.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_dtls_writable_state.cc new file mode 100644 index 0000000000..d4cb093ce6 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_dtls_writable_state.cc @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 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 "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventDtlsWritableState::RtcEventDtlsWritableState(bool writable) + : writable_(writable) {} + +RtcEventDtlsWritableState::RtcEventDtlsWritableState( + const RtcEventDtlsWritableState& other) + : RtcEvent(other.timestamp_us_), writable_(other.writable_) {} + +RtcEventDtlsWritableState::~RtcEventDtlsWritableState() = default; + +std::unique_ptr<RtcEventDtlsWritableState> RtcEventDtlsWritableState::Copy() + const { + return absl::WrapUnique<RtcEventDtlsWritableState>( + new RtcEventDtlsWritableState(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h new file mode 100644 index 0000000000..c820f184d7 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_WRITABLE_STATE_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_WRITABLE_STATE_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +struct LoggedDtlsWritableState { + LoggedDtlsWritableState() = default; + explicit LoggedDtlsWritableState(bool writable) : writable(writable) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + bool writable; +}; + +class RtcEventDtlsWritableState : public RtcEvent { + public: + static constexpr Type kType = Type::DtlsWritableState; + + explicit RtcEventDtlsWritableState(bool writable); + ~RtcEventDtlsWritableState() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventDtlsWritableState> Copy() const; + + bool writable() const { return writable_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedDtlsWritableState>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventDtlsWritableState(const RtcEventDtlsWritableState& other); + + const bool writable_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_WRITABLE_STATE_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_end_log.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_end_log.cc new file mode 100644 index 0000000000..52abf9e842 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_end_log.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 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 "logging/rtc_event_log/events/rtc_event_end_log.h" + +#include "absl/strings/string_view.h" + +namespace webrtc { +constexpr RtcEvent::Type RtcEventEndLog::kType; +constexpr EventParameters RtcEventEndLog::event_params_; + +RtcEventEndLog::RtcEventEndLog(Timestamp timestamp) + : RtcEvent(timestamp.us()) {} + +RtcEventEndLog::RtcEventEndLog(const RtcEventEndLog& other) + : RtcEvent(other.timestamp_us_) {} + +RtcEventEndLog::~RtcEventEndLog() = default; + +std::string RtcEventEndLog::Encode(rtc::ArrayView<const RtcEvent*> batch) { + EventEncoder encoder(event_params_, batch); + return encoder.AsString(); +} + +RtcEventLogParseStatus RtcEventEndLog::Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedStopEvent>& output) { + EventParser parser; + auto status = parser.Initialize(encoded_bytes, batched); + if (!status.ok()) + return status; + + rtc::ArrayView<LoggedStopEvent> output_batch = + ExtendLoggedBatch(output, parser.NumEventsInBatch()); + + constexpr FieldParameters timestamp_params{ + "timestamp_ms", FieldParameters::kTimestampField, FieldType::kVarInt, 64}; + RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>> result = + parser.ParseNumericField(timestamp_params); + if (!result.ok()) + return result.status(); + status = PopulateRtcEventTimestamp(result.value(), + &LoggedStopEvent::timestamp, output_batch); + if (!status.ok()) + return status; + + return RtcEventLogParseStatus::Success(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_end_log.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_end_log.h new file mode 100644 index 0000000000..79648bdb8d --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_end_log.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_END_LOG_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_END_LOG_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" + +namespace webrtc { + +struct LoggedStopEvent { + LoggedStopEvent() = default; + + explicit LoggedStopEvent(Timestamp timestamp) : timestamp(timestamp) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::PlusInfinity(); +}; + +class RtcEventEndLog final : public RtcEvent { + public: + static constexpr Type kType = Type::EndV3Log; + + explicit RtcEventEndLog(Timestamp timestamp); + ~RtcEventEndLog() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch); + + static RtcEventLogParseStatus Parse(absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedStopEvent>& output); + + private: + RtcEventEndLog(const RtcEventEndLog& other); + + static constexpr EventParameters event_params_{"EndLog", + RtcEventEndLog::kType}; +}; + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_END_LOG_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding.cc new file mode 100644 index 0000000000..68188ce856 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding.cc @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2021 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 "logging/rtc_event_log/events/rtc_event_field_encoding.h" + +#include <algorithm> +#include <limits> +#include <memory> +#include <utility> + +#include "logging/rtc_event_log/encoder/bit_writer.h" +#include "logging/rtc_event_log/encoder/var_int.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +using webrtc_event_logging::UnsignedDelta; + +namespace { + +std::string SerializeLittleEndian(uint64_t value, uint8_t bytes) { + RTC_DCHECK_LE(bytes, sizeof(uint64_t)); + RTC_DCHECK_GE(bytes, 1); + if (bytes < sizeof(uint64_t)) { + // Note that shifting a 64-bit value by 64 (or more) bits is undefined. + RTC_DCHECK_EQ(value >> (8 * bytes), 0); + } + std::string output(bytes, 0); + // Getting a non-const pointer to the representation. See e.g. + // https://en.cppreference.com/w/cpp/string/basic_string: + // "The elements of a basic_string are stored contiguously, + // that is, [...] a pointer to s[0] can be passed to functions + // that expect a pointer to the first element of a null-terminated + // CharT[] array." + uint8_t* p = reinterpret_cast<uint8_t*>(&output[0]); +#ifdef WEBRTC_ARCH_LITTLE_ENDIAN + memcpy(p, &value, bytes); +#else + while (bytes > 0) { + *p = static_cast<uint8_t>(value & 0xFF); + value >>= 8; + ++p; + --bytes; + } +#endif // WEBRTC_ARCH_LITTLE_ENDIAN + return output; +} + +} // namespace + +namespace webrtc { + +std::string EncodeOptionalValuePositions(std::vector<bool> positions) { + BitWriter writer((positions.size() + 7) / 8); + for (bool position : positions) { + writer.WriteBits(position ? 1u : 0u, 1); + } + return writer.GetString(); +} + +std::string EncodeSingleValue(uint64_t value, FieldType field_type) { + switch (field_type) { + case FieldType::kFixed8: + return SerializeLittleEndian(value, /*bytes=*/1); + case FieldType::kFixed32: + return SerializeLittleEndian(value, /*bytes=*/4); + case FieldType::kFixed64: + return SerializeLittleEndian(value, /*bytes=*/8); + case FieldType::kVarInt: + return EncodeVarInt(value); + case FieldType::kString: + RTC_DCHECK_NOTREACHED(); + return std::string(); + } + RTC_DCHECK_NOTREACHED(); + return std::string(); +} + +absl::optional<FieldType> ConvertFieldType(uint64_t value) { + switch (value) { + case static_cast<uint64_t>(FieldType::kFixed8): + return FieldType::kFixed8; + case static_cast<uint64_t>(FieldType::kFixed32): + return FieldType::kFixed32; + case static_cast<uint64_t>(FieldType::kFixed64): + return FieldType::kFixed64; + case static_cast<uint64_t>(FieldType::kVarInt): + return FieldType::kVarInt; + case static_cast<uint64_t>(FieldType::kString): + return FieldType::kString; + default: + return absl::nullopt; + } +} + +std::string EncodeDeltasV3(FixedLengthEncodingParametersV3 params, + uint64_t base, + rtc::ArrayView<const uint64_t> values) { + size_t outputbound = (values.size() * params.delta_bit_width() + 7) / 8; + BitWriter writer(outputbound); + + uint64_t previous = base; + for (uint64_t value : values) { + if (params.signed_deltas()) { + uint64_t positive_delta = + UnsignedDelta(previous, value, params.value_mask()); + uint64_t negative_delta = + UnsignedDelta(value, previous, params.value_mask()); + uint64_t delta; + if (positive_delta <= negative_delta) { + delta = positive_delta; + } else { + // Compute the two's complement representation of a negative + // delta, in a field width params_.delta_mask(). + RTC_DCHECK_GE(params.delta_mask(), negative_delta); + RTC_DCHECK_LT(params.delta_mask() - negative_delta, + params.delta_mask()); + delta = params.delta_mask() - negative_delta + 1; + RTC_DCHECK_LE(delta, params.delta_mask()); + } + writer.WriteBits(delta, params.delta_bit_width()); + } else { + uint64_t delta = UnsignedDelta(previous, value, params.value_mask()); + writer.WriteBits(delta, params.delta_bit_width()); + } + previous = value; + } + + return writer.GetString(); +} + +EventEncoder::EventEncoder(EventParameters params, + rtc::ArrayView<const RtcEvent*> batch) { + batch_size_ = batch.size(); + if (!batch.empty()) { + // Encode event type. + uint32_t batched = batch.size() > 1 ? 1 : 0; + event_tag_ = (static_cast<uint32_t>(params.id) << 1) + batched; + + // Event tag and number of encoded bytes will be filled in when the + // encoding is finalized in AsString(). + + // Encode number of events in batch + if (batched) { + encoded_fields_.push_back(EncodeVarInt(batch.size())); + } + + // Encode timestamp + std::vector<uint64_t> timestamps; + timestamps.reserve(batch.size()); + for (const RtcEvent* event : batch) { + timestamps.push_back(EncodeAsUnsigned(event->timestamp_ms())); + } + constexpr FieldParameters timestamp_params{"timestamp_ms", + FieldParameters::kTimestampField, + FieldType::kVarInt, 64}; + EncodeField(timestamp_params, timestamps); + } +} + +void EventEncoder::EncodeField(const FieldParameters& params, + const ValuesWithPositions& values) { + return EncodeField(params, values.values, &values.position_mask); +} + +void EventEncoder::EncodeField(const FieldParameters& params, + const std::vector<uint64_t>& values, + const std::vector<bool>* positions) { + if (positions) { + RTC_DCHECK_EQ(positions->size(), batch_size_); + RTC_DCHECK_LE(values.size(), batch_size_); + } else { + RTC_DCHECK_EQ(values.size(), batch_size_); + } + + if (values.size() == 0) { + // If all values for a particular field is empty/nullopt, + // then we completely skip the field even if the the batch is non-empty. + return; + } + + // We know that each event starts with the varint encoded timestamp, + // so we omit that field tag (field id + field type). In all other + // cases, we write the field tag. + if (params.field_id != FieldParameters::kTimestampField) { + RTC_DCHECK_LE(params.field_id, std::numeric_limits<uint64_t>::max() >> 3); + uint64_t field_tag = params.field_id << 3; + field_tag += static_cast<uint64_t>(params.field_type); + encoded_fields_.push_back(EncodeVarInt(field_tag)); + } + + RTC_CHECK_GE(values.size(), 1); + if (batch_size_ == 1) { + encoded_fields_.push_back(EncodeSingleValue(values[0], params.field_type)); + return; + } + + const bool values_optional = values.size() != batch_size_; + + // Compute delta parameters + rtc::ArrayView<const uint64_t> all_values(values); + uint64_t base = values[0]; + rtc::ArrayView<const uint64_t> remaining_values(all_values.subview(1)); + + FixedLengthEncodingParametersV3 delta_params = + FixedLengthEncodingParametersV3::CalculateParameters( + base, remaining_values, params.value_width, values_optional); + + encoded_fields_.push_back(EncodeVarInt(delta_params.DeltaHeaderAsInt())); + + if (values_optional) { + RTC_CHECK(positions); + encoded_fields_.push_back(EncodeOptionalValuePositions(*positions)); + } + // Base element, encoded as uint8, uint32, uint64 or varint + encoded_fields_.push_back(EncodeSingleValue(base, params.field_type)); + + // If all (existing) values are equal to the base, then we can skip + // writing the all-zero deltas, and instead infer those from the delta + // header. + if (!delta_params.values_equal()) { + encoded_fields_.push_back( + EncodeDeltasV3(delta_params, base, remaining_values)); + } +} + +void EventEncoder::EncodeField(const FieldParameters& params, + const std::vector<absl::string_view>& values) { + RTC_DCHECK_EQ(values.size(), batch_size_); + + if (values.size() == 0) { + // If all values for a particular field is empty/nullopt, + // then we completely skip the field even if the the batch is non-empty. + return; + } + + // Write the field tag. + RTC_CHECK_NE(params.field_id, FieldParameters::kTimestampField); + RTC_DCHECK_LE(params.field_id, std::numeric_limits<uint64_t>::max() >> 3); + RTC_DCHECK_EQ(params.field_type, FieldType::kString); + uint64_t field_tag = params.field_id << 3; + field_tag += static_cast<uint64_t>(params.field_type); + encoded_fields_.push_back(EncodeVarInt(field_tag)); + + if (values.size() > 1) { + // If multiple values in the batch, write the encoding + // parameters. (Values >0 reserved for future use.) + uint64_t encoding_params = 0; + encoded_fields_.push_back(EncodeVarInt(encoding_params)); + } + + // Write the strings as (length, data) pairs. + for (absl::string_view s : values) { + encoded_fields_.push_back(EncodeVarInt(s.size())); + encoded_fields_.push_back(std::string(s)); + } +} + +std::string EventEncoder::AsString() { + std::string encoded_event; + + if (batch_size_ == 0) { + RTC_DCHECK_EQ(encoded_fields_.size(), 0); + return encoded_event; + } + + // Compute size of encoded fields. + size_t total_fields_size = 0; + for (const std::string& s : encoded_fields_) { + total_fields_size += s.size(); + } + + constexpr size_t kExpectedMaxEventTagBytes = 4; + constexpr size_t kExpectedMaxSizeEncodingBytes = 4; + encoded_event.reserve(kExpectedMaxEventTagBytes + + kExpectedMaxSizeEncodingBytes + total_fields_size); + + // Encode event tag (event id and whether batch or single event). + encoded_event.append(EncodeVarInt(event_tag_)); + + // Encode size of the remaining fields. + encoded_event.append(EncodeVarInt(total_fields_size)); + + // Append encoded fields. + for (const std::string& s : encoded_fields_) { + encoded_event.append(s); + } + + return encoded_event; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding.h new file mode 100644 index 0000000000..33b77b80f5 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_H_ + +#include <string> +#include <vector> + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" +#include "logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +// To maintain backwards compatibility with past (or future) logs, +// the constants in this enum must not be changed. +// New field types with numerical IDs 5-7 can be added, but old +// parsers will fail to parse events containing the new fields. +enum class FieldType : uint8_t { + kFixed8 = 0, + kFixed32 = 1, + kFixed64 = 2, + kVarInt = 3, + kString = 4, +}; + +// EventParameters map an event name to a numerical ID. +struct EventParameters { + // The name is primarily used for debugging purposes. + const char* const name; + // + const RtcEvent::Type id; +}; + +// FieldParameters define the encoding for a field. +struct FieldParameters { + // The name is primarily used for debugging purposes. + const char* const name; + // Numerical ID for the field. Must be strictly greater than 0, + // and unique within each event type. + const uint64_t field_id; + // Encoding type for the base (i.e. non-delta) field in a batch. + const FieldType field_type; + // Number of bits after which wrap-around occurs. In most cases, + // this should be the number of bits in the field data type, i.e. + // 8 for an uint8_t, 32 for a int32_t and so on. However, `value_width` + // can be used to achieve a more efficient encoding if it is known + // that the field uses a smaller number of bits. For example, a + // 15-bit counter could set `value_width` to 15 even if the data is + // actually stored in a uint32_t. + const uint64_t value_width; + // Field ID 0 is reserved for timestamps. + static constexpr uint64_t kTimestampField = 0; +}; + +// The EventEncoder is used to encode a batch of events. +class EventEncoder { + public: + EventEncoder(EventParameters params, rtc::ArrayView<const RtcEvent*> batch); + + void EncodeField(const FieldParameters& params, + const std::vector<uint64_t>& values, + const std::vector<bool>* positions = nullptr); + + void EncodeField(const FieldParameters& params, + const ValuesWithPositions& values); + + void EncodeField(const FieldParameters& params, + const std::vector<absl::string_view>& values); + + std::string AsString(); + + private: + size_t batch_size_; + uint32_t event_tag_; + std::vector<std::string> encoded_fields_; +}; + +std::string EncodeSingleValue(uint64_t value, FieldType field_type); +std::string EncodeDeltasV3(FixedLengthEncodingParametersV3 params, + uint64_t base, + rtc::ArrayView<const uint64_t> values); + +// Given a batch of RtcEvents and a member pointer, extract that +// member from each event in the batch. Signed integer members are +// encoded as unsigned, and the bitsize increased so the result can +// represented as a std::vector<uint64_t>. +// This is intended to be used in conjuction with +// EventEncoder::EncodeField to encode a batch of events as follows: +// auto values = ExtractRtcEventMember(batch, RtcEventFoo::timestamp_ms); +// encoder.EncodeField(timestamp_params, values) +template <typename T, + typename E, + std::enable_if_t<std::is_integral<T>::value, bool> = true> +std::vector<uint64_t> ExtractRtcEventMember( + rtc::ArrayView<const RtcEvent*> batch, + const T E::*member) { + std::vector<uint64_t> values; + values.reserve(batch.size()); + for (const RtcEvent* event : batch) { + RTC_CHECK_EQ(event->GetType(), E::kType); + T value = static_cast<const E*>(event)->*member; + values.push_back(EncodeAsUnsigned(value)); + } + return values; +} + +// Extract an optional field from a batch of RtcEvents. +// The function returns a vector of positions in addition to the vector of +// values. The vector `positions` has the same length as the batch where +// `positions[i] == true` iff the batch[i]->member has a value. +// The values vector only contains the values that exists, so it +// may be shorter than the batch. +template <typename T, + typename E, + std::enable_if_t<std::is_integral<T>::value, bool> = true> +ValuesWithPositions ExtractRtcEventMember(rtc::ArrayView<const RtcEvent*> batch, + const absl::optional<T> E::*member) { + ValuesWithPositions result; + result.position_mask.reserve(batch.size()); + result.values.reserve(batch.size()); + for (const RtcEvent* event : batch) { + RTC_CHECK_EQ(event->GetType(), E::kType); + absl::optional<T> field = static_cast<const E*>(event)->*member; + result.position_mask.push_back(field.has_value()); + if (field.has_value()) { + result.values.push_back(EncodeAsUnsigned(field.value())); + } + } + return result; +} + +// Extract an enum field from a batch of RtcEvents. +// Requires specializing RtcEventLogEnum<T> for the enum type T. +template <typename T, + typename E, + std::enable_if_t<std::is_enum<T>::value, bool> = true> +std::vector<uint64_t> ExtractRtcEventMember( + rtc::ArrayView<const RtcEvent*> batch, + const T E::*member) { + std::vector<uint64_t> values; + values.reserve(batch.size()); + for (const RtcEvent* event : batch) { + RTC_CHECK_EQ(event->GetType(), E::kType); + T value = static_cast<const E*>(event)->*member; + values.push_back(RtcEventLogEnum<T>::Encode(value)); + } + return values; +} + +// Extract a string field from a batch of RtcEvents. +template <typename E> +std::vector<absl::string_view> ExtractRtcEventMember( + rtc::ArrayView<const RtcEvent*> batch, + const std::string E::*member) { + std::vector<absl::string_view> values; + values.reserve(batch.size()); + for (const RtcEvent* event : batch) { + RTC_CHECK_EQ(event->GetType(), E::kType); + absl::string_view str = static_cast<const E*>(event)->*member; + values.push_back(str); + } + return values; +} + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc new file mode 100644 index 0000000000..f0cdf8a0f7 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc @@ -0,0 +1,399 @@ + +/* + * Copyright (c) 2021 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 "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "logging/rtc_event_log/encoder/var_int.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" +#include "rtc_base/bitstream_reader.h" +#include "rtc_base/checks.h" + +namespace { +absl::optional<webrtc::FieldType> ConvertFieldType(uint64_t value) { + switch (value) { + case static_cast<uint64_t>(webrtc::FieldType::kFixed8): + return webrtc::FieldType::kFixed8; + case static_cast<uint64_t>(webrtc::FieldType::kFixed32): + return webrtc::FieldType::kFixed32; + case static_cast<uint64_t>(webrtc::FieldType::kFixed64): + return webrtc::FieldType::kFixed64; + case static_cast<uint64_t>(webrtc::FieldType::kVarInt): + return webrtc::FieldType::kVarInt; + case static_cast<uint64_t>(webrtc::FieldType::kString): + return webrtc::FieldType::kString; + default: + return absl::nullopt; + } +} +} // namespace + +namespace webrtc { + +uint64_t EventParser::ReadLittleEndian(uint8_t bytes) { + RTC_DCHECK_LE(bytes, sizeof(uint64_t)); + RTC_DCHECK_GE(bytes, 1); + + uint64_t value = 0; + + if (bytes > pending_data_.length()) { + SetError(); + return value; + } + + const uint8_t* p = reinterpret_cast<const uint8_t*>(pending_data_.data()); + unsigned int shift = 0; + uint8_t remaining = bytes; + while (remaining > 0) { + value += (static_cast<uint64_t>(*p) << shift); + shift += 8; + ++p; + --remaining; + } + + pending_data_ = pending_data_.substr(bytes); + return value; +} + +uint64_t EventParser::ReadVarInt() { + uint64_t output = 0; + bool success; + std::tie(success, pending_data_) = DecodeVarInt(pending_data_, &output); + if (!success) { + SetError(); + } + return output; +} + +uint64_t EventParser::ReadOptionalValuePositions() { + RTC_DCHECK(positions_.empty()); + size_t bits_to_read = NumEventsInBatch(); + positions_.reserve(bits_to_read); + if (pending_data_.size() * 8 < bits_to_read) { + SetError(); + return 0; + } + + BitstreamReader reader(pending_data_); + for (size_t i = 0; i < bits_to_read; i++) { + positions_.push_back(reader.ReadBit()); + } + if (!reader.Ok()) { + SetError(); + return 0; + } + + size_t num_existing_values = + std::count(positions_.begin(), positions_.end(), 1); + pending_data_ = pending_data_.substr((bits_to_read + 7) / 8); + return num_existing_values; +} + +uint64_t EventParser::ReadSingleValue(FieldType field_type) { + switch (field_type) { + case FieldType::kFixed8: + return ReadLittleEndian(/*bytes=*/1); + case FieldType::kFixed32: + return ReadLittleEndian(/*bytes=*/4); + case FieldType::kFixed64: + return ReadLittleEndian(/*bytes=*/8); + case FieldType::kVarInt: + return ReadVarInt(); + case FieldType::kString: + RTC_DCHECK_NOTREACHED(); + SetError(); + return 0; + } + RTC_DCHECK_NOTREACHED(); + SetError(); + return 0; +} + +void EventParser::ReadDeltasAndPopulateValues( + FixedLengthEncodingParametersV3 params, + uint64_t num_deltas, + uint64_t base) { + RTC_DCHECK(values_.empty()); + values_.reserve(num_deltas + 1); + values_.push_back(base); + + if (pending_data_.size() * 8 < num_deltas * params.delta_bit_width()) { + SetError(); + return; + } + + BitstreamReader reader(pending_data_); + const uint64_t top_bit = static_cast<uint64_t>(1) + << (params.delta_bit_width() - 1); + + uint64_t value = base; + for (uint64_t i = 0; i < num_deltas; ++i) { + uint64_t delta = reader.ReadBits(params.delta_bit_width()); + RTC_DCHECK_LE(value, webrtc_event_logging::MaxUnsignedValueOfBitWidth( + params.value_bit_width())); + RTC_DCHECK_LE(delta, webrtc_event_logging::MaxUnsignedValueOfBitWidth( + params.delta_bit_width())); + bool negative_delta = params.signed_deltas() && ((delta & top_bit) != 0); + if (negative_delta) { + uint64_t delta_abs = (~delta & params.delta_mask()) + 1; + value = (value - delta_abs) & params.value_mask(); + } else { + value = (value + delta) & params.value_mask(); + } + values_.push_back(value); + } + + if (!reader.Ok()) { + SetError(); + return; + } + + pending_data_ = + pending_data_.substr((num_deltas * params.delta_bit_width() + 7) / 8); +} + +RtcEventLogParseStatus EventParser::Initialize(absl::string_view s, + bool batched) { + pending_data_ = s; + num_events_ = 1; + + if (batched) { + num_events_ = ReadVarInt(); + if (!Ok()) { + return RtcEventLogParseStatus::Error( + "Failed to read number of events in batch.", __FILE__, __LINE__); + } + } + return RtcEventLogParseStatus::Success(); +} + +RtcEventLogParseStatus EventParser::ParseNumericFieldInternal( + uint64_t value_bit_width, + FieldType field_type) { + RTC_DCHECK(values_.empty()); + RTC_DCHECK(positions_.empty()); + + if (num_events_ == 1) { + // Just a single value in the batch. + uint64_t base = ReadSingleValue(field_type); + if (!Ok()) { + return RtcEventLogParseStatus::Error("Failed to read value", __FILE__, + __LINE__); + } + positions_.push_back(true); + values_.push_back(base); + } else { + // Delta compressed batch. + // Read delta header. + uint64_t header_value = ReadVarInt(); + if (!Ok()) + return RtcEventLogParseStatus::Error("Failed to read delta header", + __FILE__, __LINE__); + // NB: value_bit_width may be incorrect for the field, if this isn't the + // field we are looking for. + absl::optional<FixedLengthEncodingParametersV3> delta_header = + FixedLengthEncodingParametersV3::ParseDeltaHeader(header_value, + value_bit_width); + if (!delta_header.has_value()) { + return RtcEventLogParseStatus::Error("Failed to parse delta header", + __FILE__, __LINE__); + } + + uint64_t num_existing_deltas = NumEventsInBatch() - 1; + if (delta_header->values_optional()) { + size_t num_nonempty_values = ReadOptionalValuePositions(); + if (!Ok()) { + return RtcEventLogParseStatus::Error( + "Failed to read positions of optional values", __FILE__, __LINE__); + } + if (num_nonempty_values < 1 || NumEventsInBatch() < num_nonempty_values) { + return RtcEventLogParseStatus::Error( + "Expected at least one non_empty value", __FILE__, __LINE__); + } + num_existing_deltas = num_nonempty_values - 1; + } else { + // All elements in the batch have values. + positions_.assign(NumEventsInBatch(), 1u); + } + + // Read base. + uint64_t base = ReadSingleValue(field_type); + if (!Ok()) { + return RtcEventLogParseStatus::Error("Failed to read value", __FILE__, + __LINE__); + } + + if (delta_header->values_equal()) { + // Duplicate the base value num_existing_deltas times. + values_.assign(num_existing_deltas + 1, base); + } else { + // Read deltas; ceil(num_existing_deltas*delta_width/8) bits + ReadDeltasAndPopulateValues(delta_header.value(), num_existing_deltas, + base); + if (!Ok()) { + return RtcEventLogParseStatus::Error("Failed to decode deltas", + __FILE__, __LINE__); + } + } + } + return RtcEventLogParseStatus::Success(); +} + +RtcEventLogParseStatus EventParser::ParseStringFieldInternal() { + RTC_DCHECK(strings_.empty()); + if (num_events_ > 1) { + // String encoding params reserved for future use. + uint64_t encoding_params = ReadVarInt(); + if (!Ok()) { + return RtcEventLogParseStatus::Error("Failed to read string encoding", + __FILE__, __LINE__); + } + if (encoding_params != 0) { + return RtcEventLogParseStatus::Error( + "Unrecognized string encoding parameters", __FILE__, __LINE__); + } + } + strings_.reserve(num_events_); + for (uint64_t i = 0; i < num_events_; ++i) { + // Just a single value in the batch. + uint64_t size = ReadVarInt(); + if (!Ok()) { + return RtcEventLogParseStatus::Error("Failed to read string size", + __FILE__, __LINE__); + } + if (size > pending_data_.size()) { + return RtcEventLogParseStatus::Error("String size exceeds remaining data", + __FILE__, __LINE__); + } + strings_.push_back(pending_data_.substr(0, size)); + pending_data_ = pending_data_.substr(size); + } + return RtcEventLogParseStatus::Success(); +} + +RtcEventLogParseStatus EventParser::ParseField(const FieldParameters& params) { + // Verify that the event parses fields in increasing order. + if (params.field_id == FieldParameters::kTimestampField) { + RTC_DCHECK_EQ(last_field_id_, FieldParameters::kTimestampField); + } else { + RTC_DCHECK_GT(params.field_id, last_field_id_); + } + last_field_id_ = params.field_id; + + // Initialization for positional fields that don't encode field ID and type. + uint64_t field_id = params.field_id; + FieldType field_type = params.field_type; + + // Fields are encoded in increasing field_id order. + // Skip unknown fields with field_id < params.field_id until we either + // find params.field_id or a field with higher id, in which case we know that + // params.field_id doesn't exist. + while (!pending_data_.empty()) { + absl::string_view field_start = pending_data_; + ClearTemporaries(); + + // Read tag for non-positional fields. + if (params.field_id != FieldParameters::kTimestampField) { + uint64_t field_tag = ReadVarInt(); + if (!Ok()) + return RtcEventLogParseStatus::Error("Failed to read field tag", + __FILE__, __LINE__); + // Split tag into field ID and field type. + field_id = field_tag >> 3; + absl::optional<FieldType> conversion = ConvertFieldType(field_tag & 7u); + if (!conversion.has_value()) + return RtcEventLogParseStatus::Error("Failed to parse field type", + __FILE__, __LINE__); + field_type = conversion.value(); + } + + if (field_id > params.field_id) { + // We've passed all fields with ids less than or equal to what we are + // looking for. Reset pending_data_ to first field with id higher than + // params.field_id, since we didn't find the field we were looking for. + pending_data_ = field_start; + return RtcEventLogParseStatus::Success(); + } + + if (field_type == FieldType::kString) { + auto status = ParseStringFieldInternal(); + if (!status.ok()) { + return status; + } + } else { + auto status = ParseNumericFieldInternal(params.value_width, field_type); + if (!status.ok()) { + return status; + } + } + + if (field_id == params.field_id) { + // The field we're looking for has been found and values populated. + return RtcEventLogParseStatus::Success(); + } + } + + // Field not found because the event ended. + ClearTemporaries(); + return RtcEventLogParseStatus::Success(); +} + +RtcEventLogParseStatusOr<rtc::ArrayView<absl::string_view>> +EventParser::ParseStringField(const FieldParameters& params, + bool required_field) { + using StatusOr = RtcEventLogParseStatusOr<rtc::ArrayView<absl::string_view>>; + RTC_DCHECK_EQ(params.field_type, FieldType::kString); + auto status = ParseField(params); + if (!status.ok()) + return StatusOr(status); + rtc::ArrayView<absl::string_view> strings = GetStrings(); + if (required_field && strings.size() != NumEventsInBatch()) { + return StatusOr::Error("Required string field not found", __FILE__, + __LINE__); + } + return StatusOr(strings); +} + +RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>> +EventParser::ParseNumericField(const FieldParameters& params, + bool required_field) { + using StatusOr = RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>>; + RTC_DCHECK_NE(params.field_type, FieldType::kString); + auto status = ParseField(params); + if (!status.ok()) + return StatusOr(status); + rtc::ArrayView<uint64_t> values = GetValues(); + if (required_field && values.size() != NumEventsInBatch()) { + return StatusOr::Error("Required numerical field not found", __FILE__, + __LINE__); + } + return StatusOr(values); +} + +RtcEventLogParseStatusOr<EventParser::ValueAndPostionView> +EventParser::ParseOptionalNumericField(const FieldParameters& params, + bool required_field) { + using StatusOr = RtcEventLogParseStatusOr<ValueAndPostionView>; + RTC_DCHECK_NE(params.field_type, FieldType::kString); + auto status = ParseField(params); + if (!status.ok()) + return StatusOr(status); + ValueAndPostionView view{GetValues(), GetPositions()}; + if (required_field && view.positions.size() != NumEventsInBatch()) { + return StatusOr::Error("Required numerical field not found", __FILE__, + __LINE__); + } + return StatusOr(view); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h new file mode 100644 index 0000000000..89dbb19298 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_PARSER_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_PARSER_H_ + +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" + +namespace webrtc { + +class EventParser { + public: + struct ValueAndPostionView { + rtc::ArrayView<uint64_t> values; + rtc::ArrayView<uint8_t> positions; + }; + + EventParser() = default; + + // N.B: This method stores a abls::string_view into the string to be + // parsed. The caller is responsible for ensuring that the actual string + // remains unmodified and outlives the EventParser. + RtcEventLogParseStatus Initialize(absl::string_view s, bool batched); + + // Attempts to parse the field specified by `params`, skipping past + // other fields that may occur before it. If 'required_field == true', + // then failing to find the field is an error, otherwise the functions + // return success, but with an empty view of values. + RtcEventLogParseStatusOr<rtc::ArrayView<absl::string_view>> ParseStringField( + const FieldParameters& params, + bool required_field = true); + RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>> ParseNumericField( + const FieldParameters& params, + bool required_field = true); + RtcEventLogParseStatusOr<ValueAndPostionView> ParseOptionalNumericField( + const FieldParameters& params, + bool required_field = true); + + // Number of events in a batch. + uint64_t NumEventsInBatch() const { return num_events_; } + + // Bytes remaining in `pending_data_`. Assuming there are no unknown + // fields, BytesRemaining() should return 0 when all known fields + // in the event have been parsed. + size_t RemainingBytes() const { return pending_data_.size(); } + + private: + uint64_t ReadLittleEndian(uint8_t bytes); + uint64_t ReadVarInt(); + uint64_t ReadSingleValue(FieldType field_type); + uint64_t ReadOptionalValuePositions(); + void ReadDeltasAndPopulateValues(FixedLengthEncodingParametersV3 params, + uint64_t num_deltas, + uint64_t base); + RtcEventLogParseStatus ParseNumericFieldInternal(uint64_t value_bit_width, + FieldType field_type); + RtcEventLogParseStatus ParseStringFieldInternal(); + + // Attempts to parse the field specified by `params`, skipping past + // other fields that may occur before it. Returns + // RtcEventLogParseStatus::Success() and populates `values_` (and + // `positions_`) if the field is found. Returns + // RtcEventLogParseStatus::Success() and clears `values_` (and `positions_`) + // if the field doesn't exist. Returns a RtcEventLogParseStatus::Error() if + // the log is incomplete, malformed or otherwise can't be parsed. + RtcEventLogParseStatus ParseField(const FieldParameters& params); + + void SetError() { error_ = true; } + bool Ok() const { return !error_; } + + rtc::ArrayView<uint64_t> GetValues() { return values_; } + rtc::ArrayView<uint8_t> GetPositions() { return positions_; } + rtc::ArrayView<absl::string_view> GetStrings() { return strings_; } + + void ClearTemporaries() { + positions_.clear(); + values_.clear(); + strings_.clear(); + } + + // Tracks whether an error has occurred in one of the helper + // functions above. + bool error_ = false; + + // Temporary storage for result. + std::vector<uint8_t> positions_; + std::vector<uint64_t> values_; + std::vector<absl::string_view> strings_; + + // String to be consumed. + absl::string_view pending_data_; + uint64_t num_events_ = 1; + uint64_t last_field_id_ = FieldParameters::kTimestampField; +}; + +// Inverse of the ExtractRtcEventMember function used when parsing +// a log. Uses a vector of values to populate a specific field in a +// vector of structs. +template <typename T, + typename E, + std::enable_if_t<std::is_integral<T>::value, bool> = true> +ABSL_MUST_USE_RESULT RtcEventLogParseStatus +PopulateRtcEventMember(const rtc::ArrayView<uint64_t> values, + T E::*member, + rtc::ArrayView<E> output) { + size_t batch_size = values.size(); + RTC_CHECK_EQ(output.size(), batch_size); + for (size_t i = 0; i < batch_size; ++i) { + output[i].*member = DecodeFromUnsignedToType<T>(values[i]); + } + return RtcEventLogParseStatus::Success(); +} + +// Same as above, but for optional fields. +template <typename T, + typename E, + std::enable_if_t<std::is_integral<T>::value, bool> = true> +ABSL_MUST_USE_RESULT RtcEventLogParseStatus +PopulateRtcEventMember(const rtc::ArrayView<uint8_t> positions, + const rtc::ArrayView<uint64_t> values, + absl::optional<T> E::*member, + rtc::ArrayView<E> output) { + size_t batch_size = positions.size(); + RTC_CHECK_EQ(output.size(), batch_size); + RTC_CHECK_LE(values.size(), batch_size); + auto value_it = values.begin(); + for (size_t i = 0; i < batch_size; ++i) { + if (positions[i]) { + RTC_CHECK(value_it != values.end()); + output[i].*member = DecodeFromUnsignedToType<T>(value_it); + ++value_it; + } else { + output[i].*member = absl::nullopt; + } + } + RTC_CHECK(value_it == values.end()); + return RtcEventLogParseStatus::Success(); +} + +// Same as above, but for enum fields. +template <typename T, + typename E, + std::enable_if_t<std::is_enum<T>::value, bool> = true> +ABSL_MUST_USE_RESULT RtcEventLogParseStatus +PopulateRtcEventMember(const rtc::ArrayView<uint64_t> values, + T E::*member, + rtc::ArrayView<E> output) { + size_t batch_size = values.size(); + RTC_CHECK_EQ(output.size(), batch_size); + for (size_t i = 0; i < batch_size; ++i) { + auto result = RtcEventLogEnum<T>::Decode(values[i]); + if (!result.ok()) { + return result.status(); + } + output[i].*member = result.value(); + } + return RtcEventLogParseStatus::Success(); +} + +// Same as above, but for string fields. +template <typename E> +ABSL_MUST_USE_RESULT RtcEventLogParseStatus +PopulateRtcEventMember(const rtc::ArrayView<absl::string_view> values, + std::string E::*member, + rtc::ArrayView<E> output) { + size_t batch_size = values.size(); + RTC_CHECK_EQ(output.size(), batch_size); + for (size_t i = 0; i < batch_size; ++i) { + output[i].*member = values[i]; + } + return RtcEventLogParseStatus::Success(); +} + +// Same as above, but for Timestamp fields. +// N.B. Assumes that the encoded value uses millisecond precision. +template <typename E> +ABSL_MUST_USE_RESULT RtcEventLogParseStatus +PopulateRtcEventTimestamp(const rtc::ArrayView<uint64_t>& values, + Timestamp E::*timestamp, + rtc::ArrayView<E> output) { + size_t batch_size = values.size(); + RTC_CHECK_EQ(batch_size, output.size()); + for (size_t i = 0; i < batch_size; ++i) { + output[i].*timestamp = + Timestamp::Millis(DecodeFromUnsignedToType<int64_t>(values[i])); + } + return RtcEventLogParseStatus::Success(); +} + +template <typename E> +rtc::ArrayView<E> ExtendLoggedBatch(std::vector<E>& output, + size_t new_elements) { + size_t old_size = output.size(); + output.insert(output.end(), old_size + new_elements, E()); + rtc::ArrayView<E> output_batch = output; + output_batch.subview(old_size); + RTC_DCHECK_EQ(output_batch.size(), new_elements); + return output_batch; +} + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_ENCODING_PARSER_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc new file mode 100644 index 0000000000..18beda1417 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc @@ -0,0 +1,886 @@ +/* Copyright (c) 2021 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 "logging/rtc_event_log/events/rtc_event_field_encoding.h" + +#include <limits> +#include <memory> +#include <string> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/encoder/var_int.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { +constexpr int32_t kInt32Max = std::numeric_limits<int32_t>::max(); +constexpr int32_t kInt32Min = std::numeric_limits<int32_t>::min(); +constexpr uint32_t kUint32Max = std::numeric_limits<uint32_t>::max(); +constexpr int64_t kInt64Max = std::numeric_limits<int64_t>::max(); +constexpr int64_t kInt64Min = std::numeric_limits<int64_t>::min(); +constexpr uint64_t kUint64Max = std::numeric_limits<uint64_t>::max(); + +template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true> +size_t ExpectedVarIntSize(T value) { + size_t bytes = 0; + uint64_t x = EncodeAsUnsigned(value); + do { + ++bytes; + x = x >> 7; + } while (x > 0); + return bytes; +} + +template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true> +size_t ExpectedBaseValueSize(const FieldParameters& params, T value) { + switch (params.field_type) { + case FieldType::kFixed8: + return 1; + case FieldType::kFixed32: + return 4; + case FieldType::kFixed64: + return 8; + case FieldType::kVarInt: + return ExpectedVarIntSize(value); + default: + break; + } + RTC_DCHECK_NOTREACHED(); + return 0; +} + +template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true> +size_t ExpectedEncodingSize(const FieldParameters& params, + const std::vector<T>& v, + size_t expected_bits_per_delta) { + if (v.size() == 0) + return 0; + + uint64_t numeric_field_type = static_cast<uint64_t>(params.field_type); + RTC_DCHECK_LT(numeric_field_type, 1u << 3); + size_t tag_size = + ExpectedVarIntSize((params.field_id << 3) + numeric_field_type); + T base = v[0]; + size_t base_size = ExpectedBaseValueSize(params, base); + if (v.size() == 1) + return tag_size + base_size; + + size_t delta_header_size = 1; + // Check if there is an element *not* equal to base. + if (std::all_of(v.begin(), v.end(), [base](T x) { return x == base; })) { + return tag_size + base_size + delta_header_size; + } + + size_t delta_size = ((v.size() - 1) * expected_bits_per_delta + 7) / 8; + return tag_size + base_size + delta_header_size + delta_size; +} + +template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true> +size_t ExpectedEncodingSize(const FieldParameters& params, + const std::vector<absl::optional<T>>& v, + size_t expected_bits_per_delta) { + size_t num_existing_values = + v.size() - std::count(v.begin(), v.end(), absl::nullopt); + auto first_existing_value = std::find_if( + v.begin(), v.end(), [](absl::optional<T> x) { return x.has_value(); }); + if (num_existing_values == 0) + return 0; + + uint64_t numeric_field_type = static_cast<uint64_t>(params.field_type); + RTC_DCHECK_LT(numeric_field_type, 1u << 3); + size_t tag_size = + ExpectedVarIntSize((params.field_id << 3) + numeric_field_type); + T base = first_existing_value->value(); + size_t base_size = ExpectedBaseValueSize(params, base); + if (num_existing_values == 1 && v.size() == 1) + return tag_size + base_size; + + size_t delta_header_size = (num_existing_values == v.size() ? 1 : 2); + size_t positions_size = + (num_existing_values == v.size() ? 0 : (v.size() + 7) / 8); + // Check if there is an element *not* equal to base. + if (std::all_of(v.begin(), v.end(), + [base](absl::optional<T> x) { return x == base; })) { + return tag_size + base_size + delta_header_size + positions_size; + } + + size_t delta_size = + ((num_existing_values - 1) * expected_bits_per_delta + 7) / 8; + return tag_size + base_size + delta_header_size + positions_size + delta_size; +} + +size_t ExpectedStringEncodingSize(const FieldParameters& params, + const std::vector<std::string>& values) { + EXPECT_EQ(params.field_type, FieldType::kString); + uint64_t numeric_field_type = static_cast<uint64_t>(params.field_type); + RTC_DCHECK_LT(numeric_field_type, 1u << 3); + size_t tag_size = + ExpectedVarIntSize((params.field_id << 3) + numeric_field_type); + + size_t expected_size = tag_size; + if (values.size() > 1) { + // VarInt encoding header reserved for future use. Currently always 0. + expected_size += 1; + } + for (const auto& s : values) { + expected_size += ExpectedVarIntSize(s.size()); + expected_size += s.size(); + } + return expected_size; +} + +} // namespace + +class RtcTestEvent final : public RtcEvent { + public: + RtcTestEvent(bool b, + int32_t signed32, + uint32_t unsigned32, + int64_t signed64, + uint64_t unsigned64) + : b_(b), + signed32_(signed32), + unsigned32_(unsigned32), + signed64_(signed64), + unsigned64_(unsigned64) {} + RtcTestEvent(bool b, + int32_t signed32, + uint32_t unsigned32, + int64_t signed64, + uint64_t unsigned64, + absl::optional<int32_t> optional_signed32, + absl::optional<int64_t> optional_signed64, + uint32_t wrapping21, + absl::string_view string) + : b_(b), + signed32_(signed32), + unsigned32_(unsigned32), + signed64_(signed64), + unsigned64_(unsigned64), + optional_signed32_(optional_signed32), + optional_signed64_(optional_signed64), + wrapping21_(wrapping21), + string_(string) {} + ~RtcTestEvent() override = default; + + Type GetType() const override { return static_cast<Type>(4711); } + bool IsConfigEvent() const override { return false; } + + static constexpr EventParameters event_params{ + "TestEvent", static_cast<RtcEvent::Type>(4711)}; + static constexpr FieldParameters timestamp_params{ + "timestamp_ms", FieldParameters::kTimestampField, FieldType::kVarInt, 64}; + static constexpr FieldParameters bool_params{"b", 2, FieldType::kFixed8, 1}; + static constexpr FieldParameters signed32_params{"signed32", 3, + FieldType::kVarInt, 32}; + static constexpr FieldParameters unsigned32_params{"unsigned32", 4, + FieldType::kFixed32, 32}; + static constexpr FieldParameters signed64_params{"signed64", 5, + FieldType::kFixed64, 64}; + static constexpr FieldParameters unsigned64_params{"unsigned64", 6, + FieldType::kVarInt, 64}; + static constexpr FieldParameters optional32_params{"optional_signed32", 7, + FieldType::kFixed32, 32}; + static constexpr FieldParameters optional64_params{"optional_signed64", 8, + FieldType::kVarInt, 64}; + static constexpr FieldParameters wrapping21_params{"wrapping21", 9, + FieldType::kFixed32, 21}; + static constexpr FieldParameters string_params{ + "string", 10, FieldType::kString, /*value_width = */ 0}; + + static constexpr Type kType = static_cast<RtcEvent::Type>(4711); + + const bool b_; + const int32_t signed32_; + const uint32_t unsigned32_; + const int64_t signed64_; + const uint64_t unsigned64_; + const absl::optional<int32_t> optional_signed32_ = absl::nullopt; + const absl::optional<int64_t> optional_signed64_ = absl::nullopt; + const uint32_t wrapping21_ = 0; + const std::string string_; +}; + +constexpr EventParameters RtcTestEvent::event_params; +constexpr FieldParameters RtcTestEvent::timestamp_params; +constexpr FieldParameters RtcTestEvent::bool_params; +constexpr FieldParameters RtcTestEvent::signed32_params; +constexpr FieldParameters RtcTestEvent::unsigned32_params; +constexpr FieldParameters RtcTestEvent::signed64_params; +constexpr FieldParameters RtcTestEvent::unsigned64_params; + +constexpr FieldParameters RtcTestEvent::optional32_params; +constexpr FieldParameters RtcTestEvent::optional64_params; +constexpr FieldParameters RtcTestEvent::wrapping21_params; +constexpr FieldParameters RtcTestEvent::string_params; + +constexpr RtcEvent::Type RtcTestEvent::kType; + +class RtcEventFieldTest : public ::testing::Test { + protected: + void SetUp() override {} + + void CreateFullEvents( + const std::vector<bool>& bool_values, + const std::vector<int32_t>& signed32_values, + const std::vector<uint32_t>& unsigned32_values, + const std::vector<int64_t>& signed64_values, + const std::vector<uint64_t>& unsigned64_values, + const std::vector<absl::optional<int32_t>>& optional32_values, + const std::vector<absl::optional<int64_t>>& optional64_values, + const std::vector<uint32_t>& wrapping21_values, + const std::vector<std::string>& string_values) { + size_t size = bool_values.size(); + RTC_CHECK_EQ(signed32_values.size(), size); + RTC_CHECK_EQ(unsigned32_values.size(), size); + RTC_CHECK_EQ(signed64_values.size(), size); + RTC_CHECK_EQ(unsigned64_values.size(), size); + RTC_CHECK_EQ(optional32_values.size(), size); + RTC_CHECK_EQ(optional64_values.size(), size); + RTC_CHECK_EQ(wrapping21_values.size(), size); + RTC_CHECK_EQ(string_values.size(), size); + + for (size_t i = 0; i < size; i++) { + batch_.push_back(new RtcTestEvent( + bool_values[i], signed32_values[i], unsigned32_values[i], + signed64_values[i], unsigned64_values[i], optional32_values[i], + optional64_values[i], wrapping21_values[i], string_values[i])); + } + } + + void PrintBytes(absl::string_view s) { + for (auto c : s) { + fprintf(stderr, "%d ", static_cast<uint8_t>(c)); + } + fprintf(stderr, "\n"); + } + + void ParseEventHeader(absl::string_view encoded_event) { + uint64_t event_tag; + bool success; + std::tie(success, encoded_event) = DecodeVarInt(encoded_event, &event_tag); + ASSERT_TRUE(success); + uint64_t event_id = event_tag >> 1; + ASSERT_EQ(event_id, static_cast<uint64_t>(RtcTestEvent::event_params.id)); + bool batched = event_tag & 1u; + ASSERT_EQ(batched, batch_.size() > 1u); + + uint64_t size; + std::tie(success, encoded_event) = DecodeVarInt(encoded_event, &size); + ASSERT_EQ(encoded_event.size(), size); + + ASSERT_TRUE(parser_.Initialize(encoded_event, batched).ok()); + } + + void ParseAndVerifyTimestamps() { + auto result = parser_.ParseNumericField(RtcTestEvent::timestamp_params); + ASSERT_TRUE(result.ok()) << result.message().c_str(); + ASSERT_EQ(result.value().size(), batch_.size()); + for (size_t i = 0; i < batch_.size(); i++) { + EXPECT_EQ(result.value()[i], + static_cast<uint64_t>(batch_[i]->timestamp_ms())); + } + } + + void ParseAndVerifyStringField( + const FieldParameters& params, + const std::vector<std::string>& expected_values, + size_t expected_skipped_bytes = 0) { + size_t expected_size = ExpectedStringEncodingSize(params, expected_values) + + expected_skipped_bytes; + size_t size_before = parser_.RemainingBytes(); + auto result = parser_.ParseStringField(params); + ASSERT_TRUE(result.ok()) << result.message().c_str(); + ASSERT_EQ(result.value().size(), expected_values.size()); + for (size_t i = 0; i < expected_values.size(); i++) { + EXPECT_EQ(result.value()[i], expected_values[i]); + } + size_t size_after = parser_.RemainingBytes(); + EXPECT_EQ(size_before - size_after, expected_size) + << " for field " << params.name; + } + + template <typename T> + void ParseAndVerifyField(const FieldParameters& params, + const std::vector<T>& expected_values, + size_t expected_bits_per_delta, + size_t expected_skipped_bytes = 0) { + size_t expected_size = + ExpectedEncodingSize(params, expected_values, expected_bits_per_delta) + + expected_skipped_bytes; + size_t size_before = parser_.RemainingBytes(); + auto result = parser_.ParseNumericField(params); + ASSERT_TRUE(result.ok()) << result.message().c_str(); + ASSERT_EQ(result.value().size(), expected_values.size()); + for (size_t i = 0; i < expected_values.size(); i++) { + EXPECT_EQ(DecodeFromUnsignedToType<T>(result.value()[i]), + expected_values[i]); + } + size_t size_after = parser_.RemainingBytes(); + EXPECT_EQ(size_before - size_after, expected_size) + << " for field " << params.name; + } + + template <typename T> + void ParseAndVerifyOptionalField( + const FieldParameters& params, + const std::vector<absl::optional<T>>& expected_values, + size_t expected_bits_per_delta, + size_t expected_skipped_bytes = 0) { + size_t expected_size = + ExpectedEncodingSize(params, expected_values, expected_bits_per_delta) + + expected_skipped_bytes; + size_t size_before = parser_.RemainingBytes(); + auto result = parser_.ParseOptionalNumericField(params); + ASSERT_TRUE(result.ok()) << result.message().c_str(); + rtc::ArrayView<uint64_t> values = result.value().values; + rtc::ArrayView<uint8_t> positions = result.value().positions; + ASSERT_EQ(positions.size(), expected_values.size()); + auto value_it = values.begin(); + for (size_t i = 0; i < expected_values.size(); i++) { + if (positions[i]) { + ASSERT_NE(value_it, values.end()); + ASSERT_TRUE(expected_values[i].has_value()); + EXPECT_EQ(DecodeFromUnsignedToType<T>(*value_it), + expected_values[i].value()); + ++value_it; + } else { + EXPECT_EQ(absl::nullopt, expected_values[i]); + } + } + EXPECT_EQ(value_it, values.end()); + size_t size_after = parser_.RemainingBytes(); + EXPECT_EQ(size_before - size_after, expected_size); + } + + void ParseAndVerifyMissingField(const FieldParameters& params) { + auto result = parser_.ParseNumericField(params, /*required_field=*/false); + ASSERT_TRUE(result.ok()) << result.message().c_str(); + EXPECT_EQ(result.value().size(), 0u); + } + + void ParseAndVerifyMissingOptionalField(const FieldParameters& params) { + auto result = + parser_.ParseOptionalNumericField(params, /*required_field=*/false); + ASSERT_TRUE(result.ok()) << result.message().c_str(); + rtc::ArrayView<uint64_t> values = result.value().values; + rtc::ArrayView<uint8_t> positions = result.value().positions; + EXPECT_EQ(positions.size(), 0u); + EXPECT_EQ(values.size(), 0u); + } + + void TearDown() override { + for (const RtcEvent* event : batch_) { + delete event; + } + } + + std::vector<const RtcEvent*> batch_; + EventParser parser_; +}; + +TEST_F(RtcEventFieldTest, EmptyList) { + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField(RtcTestEvent::bool_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); + std::string s = encoder.AsString(); + EXPECT_TRUE(s.empty()); +} + +TEST_F(RtcEventFieldTest, Singleton) { + std::vector<bool> bool_values = {true}; + std::vector<int32_t> signed32_values = {-2}; + std::vector<uint32_t> unsigned32_values = {123456789}; + std::vector<int64_t> signed64_values = {-9876543210}; + std::vector<uint64_t> unsigned64_values = {9876543210}; + std::vector<absl::optional<int32_t>> optional32_values = {kInt32Min}; + std::vector<absl::optional<int64_t>> optional64_values = {kInt64Max}; + std::vector<uint32_t> wrapping21_values = {(1 << 21) - 1}; + std::vector<std::string> string_values = {"foo"}; + + CreateFullEvents(bool_values, signed32_values, unsigned32_values, + signed64_values, unsigned64_values, optional32_values, + optional64_values, wrapping21_values, string_values); + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField(RtcTestEvent::bool_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); + encoder.EncodeField(RtcTestEvent::signed32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); + encoder.EncodeField( + RtcTestEvent::unsigned32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); + encoder.EncodeField(RtcTestEvent::signed64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); + encoder.EncodeField( + RtcTestEvent::unsigned64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + encoder.EncodeField(RtcTestEvent::string_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); + std::string s = encoder.AsString(); + + // Optional debug printing + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, + /*no deltas*/ 0); + ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, + optional32_values, /*no deltas*/ 0); + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*no deltas*/ 0); + ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, EqualElements) { + std::vector<bool> bool_values = {true, true, true, true}; + std::vector<int32_t> signed32_values = {-2, -2, -2, -2}; + std::vector<uint32_t> unsigned32_values = {123456789, 123456789, 123456789, + 123456789}; + std::vector<int64_t> signed64_values = {-9876543210, -9876543210, -9876543210, + -9876543210}; + std::vector<uint64_t> unsigned64_values = {9876543210, 9876543210, 9876543210, + 9876543210}; + std::vector<absl::optional<int32_t>> optional32_values = { + kInt32Min, kInt32Min, kInt32Min, kInt32Min}; + std::vector<absl::optional<int64_t>> optional64_values = { + kInt64Max, kInt64Max, kInt64Max, kInt64Max}; + std::vector<uint32_t> wrapping21_values = {(1 << 21) - 1, (1 << 21) - 1, + (1 << 21) - 1, (1 << 21) - 1}; + std::vector<std::string> string_values = {"foo", "foo", "foo", "foo"}; + + CreateFullEvents(bool_values, signed32_values, unsigned32_values, + signed64_values, unsigned64_values, optional32_values, + optional64_values, wrapping21_values, string_values); + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField(RtcTestEvent::bool_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); + encoder.EncodeField(RtcTestEvent::signed32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); + encoder.EncodeField( + RtcTestEvent::unsigned32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); + encoder.EncodeField(RtcTestEvent::signed64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); + encoder.EncodeField( + RtcTestEvent::unsigned64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + encoder.EncodeField(RtcTestEvent::string_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); + std::string s = encoder.AsString(); + + // Optional debug printing + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, + /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, + /*no deltas*/ 0); + ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, + optional32_values, /*no deltas*/ 0); + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, /*no deltas*/ 0); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*no deltas*/ 0); + ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, Increasing) { + std::vector<bool> bool_values = {false, true, false, true}; + std::vector<int32_t> signed32_values = {-2, -1, 0, 1}; + std::vector<uint32_t> unsigned32_values = {kUint32Max - 1, kUint32Max, 0, 1}; + std::vector<int64_t> signed64_values = {kInt64Max - 1, kInt64Max, kInt64Min, + kInt64Min + 1}; + std::vector<uint64_t> unsigned64_values = {kUint64Max - 1, kUint64Max, 0, 1}; + std::vector<absl::optional<int32_t>> optional32_values = { + kInt32Max - 1, kInt32Max, kInt32Min, kInt32Min + 1}; + std::vector<absl::optional<int64_t>> optional64_values = { + kInt64Max - 1, kInt64Max, kInt64Min, kInt64Min + 1}; + std::vector<uint32_t> wrapping21_values = {(1 << 21) - 2, (1 << 21) - 1, 0, + 1}; + std::vector<std::string> string_values = { + "", "a", "bc", "def"}; // No special compression of strings. + + CreateFullEvents(bool_values, signed32_values, unsigned32_values, + signed64_values, unsigned64_values, optional32_values, + optional64_values, wrapping21_values, string_values); + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField(RtcTestEvent::bool_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); + encoder.EncodeField(RtcTestEvent::signed32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); + encoder.EncodeField( + RtcTestEvent::unsigned32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); + encoder.EncodeField(RtcTestEvent::signed64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); + encoder.EncodeField( + RtcTestEvent::unsigned64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + encoder.EncodeField(RtcTestEvent::string_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); + std::string s = encoder.AsString(); + + // Optional debug printing + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, + /*delta bits*/ 1); + ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, + optional32_values, /*delta bits*/ 1); + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*delta bits*/ 1); + ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, Decreasing) { + std::vector<bool> bool_values = {true, false, true, false}; + std::vector<int32_t> signed32_values = {2, 1, 0, -1}; + std::vector<uint32_t> unsigned32_values = {1, 0, kUint32Max, kUint32Max - 1}; + std::vector<int64_t> signed64_values = {kInt64Min + 1, kInt64Min, kInt64Max, + kInt64Max - 1}; + std::vector<uint64_t> unsigned64_values = {1, 0, kUint64Max, kUint64Max - 1}; + std::vector<absl::optional<int32_t>> optional32_values = { + kInt32Min + 1, kInt32Min, kInt32Max, kInt32Max - 1}; + std::vector<absl::optional<int64_t>> optional64_values = { + kInt64Min + 1, kInt64Min, kInt64Max, kInt64Max - 1}; + std::vector<uint32_t> wrapping21_values = {1, 0, (1 << 21) - 1, + (1 << 21) - 2}; + std::vector<std::string> string_values = { + "def", "bc", "a", ""}; // No special compression of strings. + + CreateFullEvents(bool_values, signed32_values, unsigned32_values, + signed64_values, unsigned64_values, optional32_values, + optional64_values, wrapping21_values, string_values); + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField(RtcTestEvent::bool_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); + encoder.EncodeField(RtcTestEvent::signed32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); + encoder.EncodeField( + RtcTestEvent::unsigned32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); + encoder.EncodeField(RtcTestEvent::signed64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); + encoder.EncodeField( + RtcTestEvent::unsigned64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + encoder.EncodeField(RtcTestEvent::string_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); + std::string s = encoder.AsString(); + + // Optional debug printing + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, + /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, + /*delta bits*/ 1); + ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, + optional32_values, /*delta bits*/ 1); + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*delta bits*/ 1); + ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, SkipsDeprecatedFields) { + // Expect parser to skip fields it doesn't recognize, but find subsequent + // fields. + std::vector<bool> bool_values = {true, false}; + std::vector<int32_t> signed32_values = {kInt32Min / 2, kInt32Max / 2}; + std::vector<uint32_t> unsigned32_values = {0, kUint32Max / 2}; + std::vector<int64_t> signed64_values = {kInt64Min / 2, kInt64Max / 2}; + std::vector<uint64_t> unsigned64_values = {0, kUint64Max / 2}; + std::vector<absl::optional<int32_t>> optional32_values = {kInt32Max / 2, + kInt32Min / 2}; + std::vector<absl::optional<int64_t>> optional64_values = {kInt64Min / 2, + kInt64Max / 2}; + std::vector<uint32_t> wrapping21_values = {0, 1 << 20}; + std::vector<std::string> string_values = {"foo", "bar"}; + + size_t signed32_encoding_size = + /*tag*/ 1 + /* varint base*/ 5 + /* delta_header*/ 1 + /*deltas*/ 4; + size_t signed64_encoding_size = + /*tag*/ 1 + /* fixed64 base*/ 8 + /* delta_header*/ 1 + /*deltas*/ 8; + size_t optional32_encoding_size = + /*tag*/ 1 + /* fixed32 base*/ 4 + /* delta_header*/ 1 + /*deltas*/ 4; + + CreateFullEvents(bool_values, signed32_values, unsigned32_values, + signed64_values, unsigned64_values, optional32_values, + optional64_values, wrapping21_values, string_values); + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField(RtcTestEvent::bool_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::b_)); + encoder.EncodeField(RtcTestEvent::signed32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); + encoder.EncodeField( + RtcTestEvent::unsigned32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned32_)); + encoder.EncodeField(RtcTestEvent::signed64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); + encoder.EncodeField( + RtcTestEvent::unsigned64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::unsigned64_)); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + encoder.EncodeField(RtcTestEvent::string_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); + std::string s = encoder.AsString(); + + // Optional debug printing + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyField(RtcTestEvent::bool_params, bool_values, + /*delta_bits=*/1); + // Skips parsing the `signed32_values`. The following unsigned fields should + // still be found. + ParseAndVerifyField(RtcTestEvent::unsigned32_params, unsigned32_values, + /*delta_bits=*/31, + /*expected_skipped_bytes=*/signed32_encoding_size); + // Skips parsing the `signed64_values`. The following unsigned fields should + // still be found. + ParseAndVerifyField(RtcTestEvent::unsigned64_params, unsigned64_values, + /*delta_bits=*/63, signed64_encoding_size); + // Skips parsing the `optional32_values`. The following unsigned fields should + // still be found. + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, + /*delta_bits=*/63, optional32_encoding_size); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*delta_bits=*/20); + ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, SkipsMissingFields) { + // Expect parsing of missing field to succeed but return an empty list. + + std::vector<bool> bool_values = {true, false}; + std::vector<int32_t> signed32_values = {kInt32Min / 2, kInt32Max / 2}; + std::vector<uint32_t> unsigned32_values = {0, kUint32Max / 2}; + std::vector<int64_t> signed64_values = {kInt64Min / 2, kInt64Max / 2}; + std::vector<uint64_t> unsigned64_values = {0, kUint64Max / 2}; + std::vector<absl::optional<int32_t>> optional32_values = {kInt32Max / 2, + kInt32Min / 2}; + std::vector<absl::optional<int64_t>> optional64_values = {kInt64Min / 2, + kInt64Max / 2}; + std::vector<uint32_t> wrapping21_values = {0, 1 << 20}; + std::vector<std::string> string_values = {"foo", "foo"}; + + CreateFullEvents(bool_values, signed32_values, unsigned32_values, + signed64_values, unsigned64_values, optional32_values, + optional64_values, wrapping21_values, string_values); + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + // Skip encoding the `bool_values`. + encoder.EncodeField(RtcTestEvent::signed32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed32_)); + // Skip encoding the `unsigned32_values`. + encoder.EncodeField(RtcTestEvent::signed64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::signed64_)); + // Skip encoding the `unsigned64_values`. + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + // Skip encoding the `optional64_values`. + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + encoder.EncodeField(RtcTestEvent::string_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::string_)); + std::string s = encoder.AsString(); + + // Optional debug printing + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyMissingField(RtcTestEvent::bool_params); + ParseAndVerifyField(RtcTestEvent::signed32_params, signed32_values, + /*delta_bits=*/31); + ParseAndVerifyMissingField(RtcTestEvent::unsigned32_params); + ParseAndVerifyField(RtcTestEvent::signed64_params, signed64_values, + /*delta_bits=*/63); + ParseAndVerifyMissingField(RtcTestEvent::unsigned64_params); + ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, + optional32_values, /*delta_bits=*/31); + ParseAndVerifyMissingOptionalField(RtcTestEvent::optional64_params); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*delta_bits=*/20); + ParseAndVerifyStringField(RtcTestEvent::string_params, string_values); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, OptionalFields) { + std::vector<absl::optional<int32_t>> optional32_values = { + 2, absl::nullopt, 4, absl::nullopt, 6, absl::nullopt}; + std::vector<absl::optional<int64_t>> optional64_values = { + absl::nullopt, 1024, absl::nullopt, 1025, absl::nullopt, 1026}; + std::vector<uint32_t> wrapping21_values = {(1 << 21) - 3, 0, 2, 5, 5, 6}; + + for (size_t i = 0; i < optional32_values.size(); i++) { + batch_.push_back(new RtcTestEvent(0, 0, 0, 0, 0, optional32_values[i], + optional64_values[i], + wrapping21_values[i], "")); + } + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + encoder.EncodeField( + RtcTestEvent::wrapping21_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::wrapping21_)); + std::string s = encoder.AsString(); + + // Optional debug output + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyOptionalField(RtcTestEvent::optional32_params, + optional32_values, /*delta bits*/ 2); + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, /*delta bits*/ 1); + ParseAndVerifyField(RtcTestEvent::wrapping21_params, wrapping21_values, + /*delta bits*/ 2); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +TEST_F(RtcEventFieldTest, AllNulloptTreatedAsMissing) { + std::vector<absl::optional<int32_t>> optional32_values = { + absl::nullopt, absl::nullopt, absl::nullopt, + absl::nullopt, absl::nullopt, absl::nullopt}; + std::vector<absl::optional<int64_t>> optional64_values = { + absl::nullopt, 1024, absl::nullopt, 1025, absl::nullopt, 1026}; + + for (size_t i = 0; i < optional32_values.size(); i++) { + batch_.push_back(new RtcTestEvent(0, 0, 0, 0, 0, optional32_values[i], + optional64_values[i], 0, "")); + } + + EventEncoder encoder(RtcTestEvent::event_params, batch_); + encoder.EncodeField( + RtcTestEvent::optional32_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed32_)); + encoder.EncodeField( + RtcTestEvent::optional64_params, + ExtractRtcEventMember(batch_, &RtcTestEvent::optional_signed64_)); + std::string s = encoder.AsString(); + + // Optional debug output + // PrintBytes(s); + + ParseEventHeader(s); + ParseAndVerifyTimestamps(); + ParseAndVerifyMissingOptionalField(RtcTestEvent::optional32_params); + ParseAndVerifyOptionalField(RtcTestEvent::optional64_params, + optional64_values, /*delta_bits=*/1); + EXPECT_EQ(parser_.RemainingBytes(), 0u); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_extraction.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_extraction.cc new file mode 100644 index 0000000000..99f0b3697c --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_extraction.cc @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 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 "logging/rtc_event_log/events/rtc_event_field_extraction.h" + +#include <algorithm> +#include <limits> + +#include "rtc_base/checks.h" + +namespace webrtc_event_logging { + +// The bitwidth required to encode values in the range +// [0, `max_pos_magnitude`] using an unsigned representation. +uint8_t UnsignedBitWidth(uint64_t max_magnitude) { + uint8_t required_bits = 1; + while (max_magnitude >>= 1) { + ++required_bits; + } + return required_bits; +} + +// The bitwidth required to encode signed values in the range +// [-`max_neg_magnitude`, `max_pos_magnitude`] using a signed +// 2-complement representation. +uint8_t SignedBitWidth(uint64_t max_pos_magnitude, uint64_t max_neg_magnitude) { + const uint8_t bitwidth_positive = + max_pos_magnitude > 0 ? UnsignedBitWidth(max_pos_magnitude) : 0; + const uint8_t bitwidth_negative = + (max_neg_magnitude > 1) ? UnsignedBitWidth(max_neg_magnitude - 1) : 0; + return 1 + std::max(bitwidth_positive, bitwidth_negative); +} + +// Return the maximum integer of a given bit width. +uint64_t MaxUnsignedValueOfBitWidth(uint64_t bit_width) { + RTC_DCHECK_GE(bit_width, 1); + RTC_DCHECK_LE(bit_width, 64); + return (bit_width == 64) ? std::numeric_limits<uint64_t>::max() + : ((static_cast<uint64_t>(1) << bit_width) - 1); +} + +// Computes the delta between `previous` and `current`, under the assumption +// that `bit_mask` is the largest value before wrap-around occurs. The bitmask +// must be of the form 2^x-1. (We use the wrap-around to more efficiently +// compress counters that wrap around at different bit widths than the +// backing C++ data type.) +uint64_t UnsignedDelta(uint64_t previous, uint64_t current, uint64_t bit_mask) { + RTC_DCHECK_LE(previous, bit_mask); + RTC_DCHECK_LE(current, bit_mask); + return (current - previous) & bit_mask; +} + +} // namespace webrtc_event_logging diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_extraction.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_extraction.h new file mode 100644 index 0000000000..eb9d67f1c2 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_extraction.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_EXTRACTION_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_EXTRACTION_H_ + +#include <string> +#include <vector> + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" +#include "rtc_base/logging.h" + +namespace webrtc_event_logging { +uint8_t UnsignedBitWidth(uint64_t max_magnitude); +uint8_t SignedBitWidth(uint64_t max_pos_magnitude, uint64_t max_neg_magnitude); +uint64_t MaxUnsignedValueOfBitWidth(uint64_t bit_width); +uint64_t UnsignedDelta(uint64_t previous, uint64_t current, uint64_t bit_mask); +} // namespace webrtc_event_logging + +namespace webrtc { +template <typename T, std::enable_if_t<std::is_signed<T>::value, bool> = true> +uint64_t EncodeAsUnsigned(T value) { + return webrtc_event_logging::ToUnsigned(value); +} + +template <typename T, std::enable_if_t<std::is_unsigned<T>::value, bool> = true> +uint64_t EncodeAsUnsigned(T value) { + return static_cast<uint64_t>(value); +} + +template <typename T, std::enable_if_t<std::is_signed<T>::value, bool> = true> +T DecodeFromUnsignedToType(uint64_t value) { + T signed_value = 0; + bool success = webrtc_event_logging::ToSigned<T>(value, &signed_value); + if (!success) { + RTC_LOG(LS_ERROR) << "Failed to convert " << value << "to signed type."; + // TODO(terelius): Propagate error? + } + return signed_value; +} + +template <typename T, std::enable_if_t<std::is_unsigned<T>::value, bool> = true> +T DecodeFromUnsignedToType(uint64_t value) { + // TODO(terelius): Check range? + return static_cast<T>(value); +} + +// RtcEventLogEnum<T> defines a mapping between an enum T +// and the event log encodings. To log a new enum type T, +// specialize RtcEventLogEnum<T> and add static methods +// static uint64_t Encode(T x) {} +// static RtcEventLogParseStatusOr<T> Decode(uint64_t x) {} +template <typename T> +class RtcEventLogEnum { + static_assert(sizeof(T) != sizeof(T), + "Missing specialisation of RtcEventLogEnum for type"); +}; + +// Represents a vector<optional<uint64_t>> optional_values +// as a bit-vector `position_mask` which identifies the positions +// of existing values, and a (potentially shorter) +// `vector<uint64_t> values` containing the actual values. +// The bit vector is constructed such that position_mask[i] +// is true iff optional_values[i] has a value, and `values.size()` +// is equal to the number of set bits in `position_mask`. +struct ValuesWithPositions { + std::vector<bool> position_mask; + std::vector<uint64_t> values; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FIELD_EXTRACTION_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_extraction_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_extraction_unittest.cc new file mode 100644 index 0000000000..f9fb993af0 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_field_extraction_unittest.cc @@ -0,0 +1,97 @@ +/* Copyright (c) 2021 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 "logging/rtc_event_log/events/rtc_event_field_extraction.h" + +#include "rtc_base/random.h" +#include "test/gtest.h" + +namespace webrtc { + +TEST(UnsignedBitWidthTest, SmallValues) { + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(0), 1u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(1), 1u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(2), 2u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(3), 2u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(4), 3u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(5), 3u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(6), 3u); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(7), 3u); +} + +TEST(UnsignedBitWidthTest, PowersOfTwo) { + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(0), 1u); + + for (unsigned i = 0; i < 64; i++) { + uint64_t x = 1; + x = x << i; + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(x), i + 1); + } +} + +TEST(UnsignedBitWidthTest, PowersOfTwoMinusOne) { + for (unsigned i = 1; i < 64; i++) { + uint64_t x = 1; + x = (x << i) - 1; + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(x), i); + } + + uint64_t x = ~static_cast<uint64_t>(0); + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(x), 64u); +} + +TEST(UnsignedBitWidthTest, RandomInputs) { + Random rand(12345); + + for (unsigned i = 0; i < 64; i++) { + uint64_t x = 1; + x = x << i; + uint64_t high = rand.Rand<uint32_t>(); + uint64_t low = rand.Rand<uint32_t>(); + x += ((high << 32) + low) % x; + EXPECT_EQ(webrtc_event_logging::UnsignedBitWidth(x), i + 1); + } +} + +TEST(SignedBitWidthTest, SignedBitWidth) { + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(0, 1), 1u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(1, 0), 2u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(1, 2), 2u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(1, 128), 8u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(127, 1), 8u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(127, 128), 8u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(1, 129), 9u); + EXPECT_EQ(webrtc_event_logging::SignedBitWidth(128, 1), 9u); +} + +TEST(MaxUnsignedValueOfBitWidthTest, MaxUnsignedValueOfBitWidth) { + EXPECT_EQ(webrtc_event_logging::MaxUnsignedValueOfBitWidth(1), 0x01u); + EXPECT_EQ(webrtc_event_logging::MaxUnsignedValueOfBitWidth(6), 0x3Fu); + EXPECT_EQ(webrtc_event_logging::MaxUnsignedValueOfBitWidth(8), 0xFFu); + EXPECT_EQ(webrtc_event_logging::MaxUnsignedValueOfBitWidth(32), 0xFFFFFFFFu); +} + +TEST(EncodeAsUnsignedTest, NegativeValues) { + // Negative values are converted as if cast to unsigned type of + // the same bitsize using 2-complement representation. + int16_t x = -1; + EXPECT_EQ(EncodeAsUnsigned(x), static_cast<uint64_t>(0xFFFF)); + int64_t y = -1; + EXPECT_EQ(EncodeAsUnsigned(y), static_cast<uint64_t>(0xFFFFFFFFFFFFFFFFull)); +} + +TEST(EncodeAsUnsignedTest, PositiveValues) { + // Postive values are unchanged. + int16_t x = 42; + EXPECT_EQ(EncodeAsUnsigned(x), static_cast<uint64_t>(42)); + int64_t y = 42; + EXPECT_EQ(EncodeAsUnsigned(y), static_cast<uint64_t>(42)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_frame_decoded.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_frame_decoded.cc new file mode 100644 index 0000000000..cde412e6c4 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_frame_decoded.cc @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 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 "logging/rtc_event_log/events/rtc_event_frame_decoded.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventFrameDecoded::RtcEventFrameDecoded(int64_t render_time_ms, + uint32_t ssrc, + int width, + int height, + VideoCodecType codec, + uint8_t qp) + : render_time_ms_(render_time_ms), + ssrc_(ssrc), + width_(width), + height_(height), + codec_(codec), + qp_(qp) {} + +RtcEventFrameDecoded::RtcEventFrameDecoded(const RtcEventFrameDecoded& other) + : RtcEvent(other.timestamp_us_), + render_time_ms_(other.render_time_ms_), + ssrc_(other.ssrc_), + width_(other.width_), + height_(other.height_), + codec_(other.codec_), + qp_(other.qp_) {} + +std::unique_ptr<RtcEventFrameDecoded> RtcEventFrameDecoded::Copy() const { + return absl::WrapUnique<RtcEventFrameDecoded>( + new RtcEventFrameDecoded(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_frame_decoded.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_frame_decoded.h new file mode 100644 index 0000000000..91190faea9 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_frame_decoded.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FRAME_DECODED_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FRAME_DECODED_H_ + +#include <stdint.h> + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "api/video/video_codec_type.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +struct LoggedFrameDecoded { + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int64_t render_time_ms; + uint32_t ssrc; + int width; + int height; + VideoCodecType codec; + uint8_t qp; +}; + +class RtcEventFrameDecoded final : public RtcEvent { + public: + static constexpr Type kType = Type::FrameDecoded; + + RtcEventFrameDecoded(int64_t render_time_ms, + uint32_t ssrc, + int width, + int height, + VideoCodecType codec, + uint8_t qp); + ~RtcEventFrameDecoded() override = default; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventFrameDecoded> Copy() const; + + int64_t render_time_ms() const { return render_time_ms_; } + uint32_t ssrc() const { return ssrc_; } + int width() const { return width_; } + int height() const { return height_; } + VideoCodecType codec() const { return codec_; } + uint8_t qp() const { return qp_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::map<uint32_t, std::vector<LoggedFrameDecoded>>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventFrameDecoded(const RtcEventFrameDecoded& other); + + const int64_t render_time_ms_; + const uint32_t ssrc_; + const int width_; + const int height_; + const VideoCodecType codec_; + const uint8_t qp_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_FRAME_DECODED_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_ack_received.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_ack_received.cc new file mode 100644 index 0000000000..ba18d50ab6 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_ack_received.cc @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 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 "logging/rtc_event_log/events/rtc_event_generic_ack_received.h" + +#include <vector> + +#include "absl/memory/memory.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +std::vector<std::unique_ptr<RtcEventGenericAckReceived>> +RtcEventGenericAckReceived::CreateLogs( + int64_t packet_number, + const std::vector<AckedPacket>& acked_packets) { + std::vector<std::unique_ptr<RtcEventGenericAckReceived>> result; + int64_t time_us = rtc::TimeMicros(); + result.reserve(acked_packets.size()); + for (const AckedPacket& packet : acked_packets) { + result.emplace_back(new RtcEventGenericAckReceived( + time_us, packet_number, packet.packet_number, + packet.receive_acked_packet_time_ms)); + } + return result; +} + +RtcEventGenericAckReceived::RtcEventGenericAckReceived( + int64_t timestamp_us, + int64_t packet_number, + int64_t acked_packet_number, + absl::optional<int64_t> receive_acked_packet_time_ms) + : RtcEvent(timestamp_us), + packet_number_(packet_number), + acked_packet_number_(acked_packet_number), + receive_acked_packet_time_ms_(receive_acked_packet_time_ms) {} + +std::unique_ptr<RtcEventGenericAckReceived> RtcEventGenericAckReceived::Copy() + const { + return absl::WrapUnique(new RtcEventGenericAckReceived(*this)); +} + +RtcEventGenericAckReceived::RtcEventGenericAckReceived( + const RtcEventGenericAckReceived& packet) = default; + +RtcEventGenericAckReceived::~RtcEventGenericAckReceived() = default; + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_ack_received.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_ack_received.h new file mode 100644 index 0000000000..57fd7cd9a6 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_ack_received.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_ACK_RECEIVED_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_ACK_RECEIVED_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +struct LoggedGenericAckReceived { + LoggedGenericAckReceived() = default; + LoggedGenericAckReceived(Timestamp timestamp, + int64_t packet_number, + int64_t acked_packet_number, + absl::optional<int64_t> receive_acked_packet_time_ms) + : timestamp(timestamp), + packet_number(packet_number), + acked_packet_number(acked_packet_number), + receive_acked_packet_time_ms(receive_acked_packet_time_ms) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int64_t packet_number; + int64_t acked_packet_number; + absl::optional<int64_t> receive_acked_packet_time_ms; +}; + +struct AckedPacket { + // The packet number that was acked. + int64_t packet_number; + + // The time where the packet was received. Not every ACK will + // include the receive timestamp. + absl::optional<int64_t> receive_acked_packet_time_ms; +}; + +class RtcEventGenericAckReceived final : public RtcEvent { + public: + static constexpr Type kType = Type::GenericAckReceived; + + // For a collection of acked packets, it creates a vector of logs to log with + // the same timestamp. + static std::vector<std::unique_ptr<RtcEventGenericAckReceived>> CreateLogs( + int64_t packet_number, + const std::vector<AckedPacket>& acked_packets); + + ~RtcEventGenericAckReceived() override; + + std::unique_ptr<RtcEventGenericAckReceived> Copy() const; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + // An identifier of the packet which contained an ack. + int64_t packet_number() const { return packet_number_; } + + // An identifier of the acked packet. + int64_t acked_packet_number() const { return acked_packet_number_; } + + // Timestamp when the `acked_packet_number` was received by the remote side. + absl::optional<int64_t> receive_acked_packet_time_ms() const { + return receive_acked_packet_time_ms_; + } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedGenericAckReceived>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventGenericAckReceived(const RtcEventGenericAckReceived& packet); + + // When the ack is received, `packet_number` identifies the packet which + // contained an ack for `acked_packet_number`, and contains the + // `receive_acked_packet_time_ms` on which the `acked_packet_number` was + // received on the remote side. The `receive_acked_packet_time_ms` may be + // null. + RtcEventGenericAckReceived( + int64_t timestamp_us, + int64_t packet_number, + int64_t acked_packet_number, + absl::optional<int64_t> receive_acked_packet_time_ms); + + const int64_t packet_number_; + const int64_t acked_packet_number_; + const absl::optional<int64_t> receive_acked_packet_time_ms_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_ACK_RECEIVED_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_packet_received.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_packet_received.cc new file mode 100644 index 0000000000..0bdc4dd505 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_packet_received.cc @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 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 "logging/rtc_event_log/events/rtc_event_generic_packet_received.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventGenericPacketReceived::RtcEventGenericPacketReceived( + int64_t packet_number, + size_t packet_length) + : packet_number_(packet_number), packet_length_(packet_length) {} + +RtcEventGenericPacketReceived::RtcEventGenericPacketReceived( + const RtcEventGenericPacketReceived& packet) = default; + +RtcEventGenericPacketReceived::~RtcEventGenericPacketReceived() = default; + +std::unique_ptr<RtcEventGenericPacketReceived> +RtcEventGenericPacketReceived::Copy() const { + return absl::WrapUnique(new RtcEventGenericPacketReceived(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_packet_received.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_packet_received.h new file mode 100644 index 0000000000..a6006ca4d4 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_packet_received.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_PACKET_RECEIVED_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_PACKET_RECEIVED_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +struct LoggedGenericPacketReceived { + LoggedGenericPacketReceived() = default; + LoggedGenericPacketReceived(Timestamp timestamp, + int64_t packet_number, + int packet_length) + : timestamp(timestamp), + packet_number(packet_number), + packet_length(packet_length) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int64_t packet_number; + int packet_length; +}; + +class RtcEventGenericPacketReceived final : public RtcEvent { + public: + static constexpr Type kType = Type::GenericPacketReceived; + + RtcEventGenericPacketReceived(int64_t packet_number, size_t packet_length); + ~RtcEventGenericPacketReceived() override; + + std::unique_ptr<RtcEventGenericPacketReceived> Copy() const; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + // An identifier of the packet. + int64_t packet_number() const { return packet_number_; } + + // Total packet length, including all packetization overheads, but not + // including ICE/TURN/IP overheads. + size_t packet_length() const { return packet_length_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedGenericPacketReceived>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventGenericPacketReceived(const RtcEventGenericPacketReceived& packet); + + const int64_t packet_number_; + const size_t packet_length_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_PACKET_RECEIVED_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_packet_sent.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_packet_sent.cc new file mode 100644 index 0000000000..e8335624b1 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_packet_sent.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 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 "logging/rtc_event_log/events/rtc_event_generic_packet_sent.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventGenericPacketSent::RtcEventGenericPacketSent(int64_t packet_number, + size_t overhead_length, + size_t payload_length, + size_t padding_length) + : packet_number_(packet_number), + overhead_length_(overhead_length), + payload_length_(payload_length), + padding_length_(padding_length) {} + +RtcEventGenericPacketSent::RtcEventGenericPacketSent( + const RtcEventGenericPacketSent& packet) = default; + +RtcEventGenericPacketSent::~RtcEventGenericPacketSent() = default; + +std::unique_ptr<RtcEventGenericPacketSent> RtcEventGenericPacketSent::Copy() + const { + return absl::WrapUnique(new RtcEventGenericPacketSent(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_packet_sent.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_packet_sent.h new file mode 100644 index 0000000000..903950a398 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_generic_packet_sent.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_PACKET_SENT_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_PACKET_SENT_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +struct LoggedGenericPacketSent { + LoggedGenericPacketSent() = default; + LoggedGenericPacketSent(Timestamp timestamp, + int64_t packet_number, + size_t overhead_length, + size_t payload_length, + size_t padding_length) + : timestamp(timestamp), + packet_number(packet_number), + overhead_length(overhead_length), + payload_length(payload_length), + padding_length(padding_length) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + size_t packet_length() const { + return payload_length + padding_length + overhead_length; + } + Timestamp timestamp = Timestamp::MinusInfinity(); + int64_t packet_number; + size_t overhead_length; + size_t payload_length; + size_t padding_length; +}; + +class RtcEventGenericPacketSent final : public RtcEvent { + public: + static constexpr Type kType = Type::GenericPacketSent; + + RtcEventGenericPacketSent(int64_t packet_number, + size_t overhead_length, + size_t payload_length, + size_t padding_length); + ~RtcEventGenericPacketSent() override; + + std::unique_ptr<RtcEventGenericPacketSent> Copy() const; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + // An identifier of the packet. + int64_t packet_number() const { return packet_number_; } + + // Total packet length, including all packetization overheads, but not + // including ICE/TURN/IP overheads. + size_t packet_length() const { + return overhead_length_ + payload_length_ + padding_length_; + } + + // The number of bytes in overhead, including framing overheads, acks if + // present, etc. + size_t overhead_length() const { return overhead_length_; } + + // Length of payload sent (size of raw audio/video/data), without + // packetization overheads. This may still include serialization overheads. + size_t payload_length() const { return payload_length_; } + + size_t padding_length() const { return padding_length_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedGenericPacketSent>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventGenericPacketSent(const RtcEventGenericPacketSent& packet); + + const int64_t packet_number_; + const size_t overhead_length_; + const size_t payload_length_; + const size_t padding_length_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_GENERIC_PACKET_SENT_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.cc new file mode 100644 index 0000000000..2b4b5ba762 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.cc @@ -0,0 +1,40 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventIceCandidatePair::RtcEventIceCandidatePair( + IceCandidatePairEventType type, + uint32_t candidate_pair_id, + uint32_t transaction_id) + : type_(type), + candidate_pair_id_(candidate_pair_id), + transaction_id_(transaction_id) {} + +RtcEventIceCandidatePair::RtcEventIceCandidatePair( + const RtcEventIceCandidatePair& other) + : RtcEvent(other.timestamp_us_), + type_(other.type_), + candidate_pair_id_(other.candidate_pair_id_), + transaction_id_(other.transaction_id_) {} + +RtcEventIceCandidatePair::~RtcEventIceCandidatePair() = default; + +std::unique_ptr<RtcEventIceCandidatePair> RtcEventIceCandidatePair::Copy() + const { + return absl::WrapUnique<RtcEventIceCandidatePair>( + new RtcEventIceCandidatePair(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h new file mode 100644 index 0000000000..bdacf15a59 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h @@ -0,0 +1,98 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ICE_CANDIDATE_PAIR_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ICE_CANDIDATE_PAIR_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +enum class IceCandidatePairEventType { + kCheckSent, + kCheckReceived, + kCheckResponseSent, + kCheckResponseReceived, + kNumValues, +}; + +struct LoggedIceCandidatePairEvent { + LoggedIceCandidatePairEvent() = default; + LoggedIceCandidatePairEvent(Timestamp timestamp, + IceCandidatePairEventType type, + uint32_t candidate_pair_id, + uint32_t transaction_id) + : timestamp(timestamp), + type(type), + candidate_pair_id(candidate_pair_id), + transaction_id(transaction_id) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + IceCandidatePairEventType type; + uint32_t candidate_pair_id; + uint32_t transaction_id; +}; + +class RtcEventIceCandidatePair final : public RtcEvent { + public: + static constexpr Type kType = Type::IceCandidatePairEvent; + + RtcEventIceCandidatePair(IceCandidatePairEventType type, + uint32_t candidate_pair_id, + uint32_t transaction_id); + + ~RtcEventIceCandidatePair() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventIceCandidatePair> Copy() const; + + IceCandidatePairEventType type() const { return type_; } + uint32_t candidate_pair_id() const { return candidate_pair_id_; } + uint32_t transaction_id() const { return transaction_id_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedIceCandidatePairEvent>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventIceCandidatePair(const RtcEventIceCandidatePair& other); + + const IceCandidatePairEventType type_; + const uint32_t candidate_pair_id_; + const uint32_t transaction_id_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ICE_CANDIDATE_PAIR_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.cc new file mode 100644 index 0000000000..eb458c4640 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.cc @@ -0,0 +1,63 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +IceCandidatePairDescription::IceCandidatePairDescription() { + local_candidate_type = IceCandidateType::kUnknown; + local_relay_protocol = IceCandidatePairProtocol::kUnknown; + local_network_type = IceCandidateNetworkType::kUnknown; + local_address_family = IceCandidatePairAddressFamily::kUnknown; + remote_candidate_type = IceCandidateType::kUnknown; + remote_address_family = IceCandidatePairAddressFamily::kUnknown; + candidate_pair_protocol = IceCandidatePairProtocol::kUnknown; +} + +IceCandidatePairDescription::IceCandidatePairDescription( + const IceCandidatePairDescription& other) { + local_candidate_type = other.local_candidate_type; + local_relay_protocol = other.local_relay_protocol; + local_network_type = other.local_network_type; + local_address_family = other.local_address_family; + remote_candidate_type = other.remote_candidate_type; + remote_address_family = other.remote_address_family; + candidate_pair_protocol = other.candidate_pair_protocol; +} + +IceCandidatePairDescription::~IceCandidatePairDescription() {} + +RtcEventIceCandidatePairConfig::RtcEventIceCandidatePairConfig( + IceCandidatePairConfigType type, + uint32_t candidate_pair_id, + const IceCandidatePairDescription& candidate_pair_desc) + : type_(type), + candidate_pair_id_(candidate_pair_id), + candidate_pair_desc_(candidate_pair_desc) {} + +RtcEventIceCandidatePairConfig::RtcEventIceCandidatePairConfig( + const RtcEventIceCandidatePairConfig& other) + : RtcEvent(other.timestamp_us_), + type_(other.type_), + candidate_pair_id_(other.candidate_pair_id_), + candidate_pair_desc_(other.candidate_pair_desc_) {} + +RtcEventIceCandidatePairConfig::~RtcEventIceCandidatePairConfig() = default; + +std::unique_ptr<RtcEventIceCandidatePairConfig> +RtcEventIceCandidatePairConfig::Copy() const { + return absl::WrapUnique<RtcEventIceCandidatePairConfig>( + new RtcEventIceCandidatePairConfig(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h new file mode 100644 index 0000000000..e72d999cff --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h @@ -0,0 +1,152 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ICE_CANDIDATE_PAIR_CONFIG_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ICE_CANDIDATE_PAIR_CONFIG_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +enum class IceCandidatePairConfigType { + kAdded, + kUpdated, + kDestroyed, + kSelected, + kNumValues, +}; + +// TODO(qingsi): Change the names of candidate types to "host", "srflx", "prflx" +// and "relay" after the naming is spec-compliant in the signaling part +enum class IceCandidateType { + kUnknown, + kLocal, + kStun, + kPrflx, + kRelay, + kNumValues, +}; + +enum class IceCandidatePairProtocol { + kUnknown, + kUdp, + kTcp, + kSsltcp, + kTls, + kNumValues, +}; + +enum class IceCandidatePairAddressFamily { + kUnknown, + kIpv4, + kIpv6, + kNumValues, +}; + +enum class IceCandidateNetworkType { + kUnknown, + kEthernet, + kLoopback, + kWifi, + kVpn, + kCellular, + kNumValues, +}; + +struct LoggedIceCandidatePairConfig { + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + IceCandidatePairConfigType type; + uint32_t candidate_pair_id; + IceCandidateType local_candidate_type; + IceCandidatePairProtocol local_relay_protocol; + IceCandidateNetworkType local_network_type; + IceCandidatePairAddressFamily local_address_family; + IceCandidateType remote_candidate_type; + IceCandidatePairAddressFamily remote_address_family; + IceCandidatePairProtocol candidate_pair_protocol; +}; + +class IceCandidatePairDescription { + public: + IceCandidatePairDescription(); + explicit IceCandidatePairDescription( + const IceCandidatePairDescription& other); + + ~IceCandidatePairDescription(); + + IceCandidateType local_candidate_type; + IceCandidatePairProtocol local_relay_protocol; + IceCandidateNetworkType local_network_type; + IceCandidatePairAddressFamily local_address_family; + IceCandidateType remote_candidate_type; + IceCandidatePairAddressFamily remote_address_family; + IceCandidatePairProtocol candidate_pair_protocol; +}; + +class RtcEventIceCandidatePairConfig final : public RtcEvent { + public: + static constexpr Type kType = Type::IceCandidatePairConfig; + + RtcEventIceCandidatePairConfig( + IceCandidatePairConfigType type, + uint32_t candidate_pair_id, + const IceCandidatePairDescription& candidate_pair_desc); + + ~RtcEventIceCandidatePairConfig() override; + + Type GetType() const override { return kType; } + // N.B. An ICE config event is not considered an RtcEventLog config event. + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventIceCandidatePairConfig> Copy() const; + + IceCandidatePairConfigType type() const { return type_; } + uint32_t candidate_pair_id() const { return candidate_pair_id_; } + const IceCandidatePairDescription& candidate_pair_desc() const { + return candidate_pair_desc_; + } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedIceCandidatePairConfig>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventIceCandidatePairConfig(const RtcEventIceCandidatePairConfig& other); + + const IceCandidatePairConfigType type_; + const uint32_t candidate_pair_id_; + const IceCandidatePairDescription candidate_pair_desc_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ICE_CANDIDATE_PAIR_CONFIG_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_log_parse_status.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_log_parse_status.h new file mode 100644 index 0000000000..e7d6de7d60 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_log_parse_status.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_LOG_PARSE_STATUS_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_LOG_PARSE_STATUS_H_ + +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" + +#define RTC_PARSE_RETURN_ERROR(X) \ + do { \ + return RtcEventLogParseStatus::Error(#X, __FILE__, __LINE__); \ + } while (0) + +#define RTC_PARSE_CHECK_OR_RETURN(X) \ + do { \ + if (!(X)) \ + return RtcEventLogParseStatus::Error(#X, __FILE__, __LINE__); \ + } while (0) + +#define RTC_PARSE_CHECK_OR_RETURN_MESSAGE(X, M) \ + do { \ + if (!(X)) \ + return RtcEventLogParseStatus::Error((M), __FILE__, __LINE__); \ + } while (0) + +#define RTC_PARSE_CHECK_OR_RETURN_OP(OP, X, Y) \ + do { \ + if (!((X)OP(Y))) \ + return RtcEventLogParseStatus::Error(#X #OP #Y, __FILE__, __LINE__); \ + } while (0) + +#define RTC_PARSE_CHECK_OR_RETURN_EQ(X, Y) \ + RTC_PARSE_CHECK_OR_RETURN_OP(==, X, Y) + +#define RTC_PARSE_CHECK_OR_RETURN_NE(X, Y) \ + RTC_PARSE_CHECK_OR_RETURN_OP(!=, X, Y) + +#define RTC_PARSE_CHECK_OR_RETURN_LT(X, Y) RTC_PARSE_CHECK_OR_RETURN_OP(<, X, Y) + +#define RTC_PARSE_CHECK_OR_RETURN_LE(X, Y) \ + RTC_PARSE_CHECK_OR_RETURN_OP(<=, X, Y) + +#define RTC_PARSE_CHECK_OR_RETURN_GT(X, Y) RTC_PARSE_CHECK_OR_RETURN_OP(>, X, Y) + +#define RTC_PARSE_CHECK_OR_RETURN_GE(X, Y) \ + RTC_PARSE_CHECK_OR_RETURN_OP(>=, X, Y) + +#define RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(X, M) \ + do { \ + if (X) { \ + RTC_LOG(LS_WARNING) << (M); \ + return RtcEventLogParseStatus::Success(); \ + } \ + } while (0) + +#define RTC_RETURN_IF_ERROR(X) \ + do { \ + const RtcEventLogParseStatus _rtc_parse_status(X); \ + if (!_rtc_parse_status.ok()) { \ + return _rtc_parse_status; \ + } \ + } while (0) + +// TODO(terelius): Compared to a generic 'Status' class, this +// class allows us additional information about the context +// in which the error occurred. This is currently limited to +// the source location (file and line), but we plan on adding +// information about the event and field name being parsed. +// If/when we start using absl::Status in WebRTC, consider +// whether payloads would be an appropriate alternative. +class RtcEventLogParseStatus { + template <typename T> + friend class RtcEventLogParseStatusOr; + + public: + static RtcEventLogParseStatus Success() { return RtcEventLogParseStatus(); } + static RtcEventLogParseStatus Error(absl::string_view error, + absl::string_view file, + int line) { + return RtcEventLogParseStatus(error, file, line); + } + + bool ok() const { return error_.empty(); } + ABSL_DEPRECATED("Use ok() instead") explicit operator bool() const { + return ok(); + } + + std::string message() const { return error_; } + + private: + RtcEventLogParseStatus() : error_() {} + RtcEventLogParseStatus(absl::string_view error, + absl::string_view file, + int line) + : error_(std::string(error) + " (" + std::string(file) + ": " + + std::to_string(line) + ")") {} + + std::string error_; +}; + +template <typename T> +class RtcEventLogParseStatusOr { + public: + RtcEventLogParseStatusOr(RtcEventLogParseStatus status) // NOLINT + : status_(status), value_() {} + RtcEventLogParseStatusOr(const T& value) // NOLINT + : status_(), value_(value) {} + + bool ok() const { return status_.ok(); } + + std::string message() const { return status_.message(); } + + RtcEventLogParseStatus status() const { return status_; } + + const T& value() const { + RTC_DCHECK(ok()); + return value_; + } + + T& value() { + RTC_DCHECK(ok()); + return value_; + } + + static RtcEventLogParseStatusOr Error(absl::string_view error, + absl::string_view file, + int line) { + return RtcEventLogParseStatusOr(error, file, line); + } + + private: + RtcEventLogParseStatusOr() : status_() {} + RtcEventLogParseStatusOr(absl::string_view error, + absl::string_view file, + int line) + : status_(error, file, line), value_() {} + + RtcEventLogParseStatus status_; + T value_; +}; + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_LOG_PARSE_STATUS_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.cc new file mode 100644 index 0000000000..7b958c181b --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 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 "logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.h" + +#include <stdint.h> + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_definition.h" + +namespace webrtc { + +RtcEventNetEqSetMinimumDelay::RtcEventNetEqSetMinimumDelay(uint32_t remote_ssrc, + int delay_ms) + : remote_ssrc_(remote_ssrc), minimum_delay_ms_(delay_ms) {} +RtcEventNetEqSetMinimumDelay::~RtcEventNetEqSetMinimumDelay() {} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.h new file mode 100644 index 0000000000..2e49adb36e --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_NETEQ_SET_MINIMUM_DELAY_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_NETEQ_SET_MINIMUM_DELAY_H_ + +#include <stdint.h> + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_definition.h" + +namespace webrtc { + +struct LoggedNetEqSetMinimumDelayEvent { + LoggedNetEqSetMinimumDelayEvent() = default; + LoggedNetEqSetMinimumDelayEvent(Timestamp timestamp, + uint32_t remote_ssrc, + int minimum_delay_ms) + : timestamp(timestamp), + remote_ssrc(remote_ssrc), + minimum_delay_ms(minimum_delay_ms) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + uint32_t remote_ssrc; + int minimum_delay_ms; +}; + +class RtcEventNetEqSetMinimumDelay final : public RtcEvent { + public: + static constexpr Type kType = Type::NetEqSetMinimumDelay; + + explicit RtcEventNetEqSetMinimumDelay(uint32_t remote_ssrc, int delay_ms); + ~RtcEventNetEqSetMinimumDelay() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + uint32_t remote_ssrc() const { return remote_ssrc_; } + int minimum_delay_ms() const { return minimum_delay_ms_; } + + std::unique_ptr<RtcEventNetEqSetMinimumDelay> Copy() const { + return absl::WrapUnique<RtcEventNetEqSetMinimumDelay>( + new RtcEventNetEqSetMinimumDelay(*this)); + } + + private: + uint32_t remote_ssrc_; + int minimum_delay_ms_; +}; + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_NETEQ_SET_MINIMUM_DELAY_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_cluster_created.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_cluster_created.cc new file mode 100644 index 0000000000..c3d9e59b47 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_cluster_created.cc @@ -0,0 +1,40 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventProbeClusterCreated::RtcEventProbeClusterCreated(int32_t id, + int32_t bitrate_bps, + uint32_t min_probes, + uint32_t min_bytes) + : id_(id), + bitrate_bps_(bitrate_bps), + min_probes_(min_probes), + min_bytes_(min_bytes) {} + +RtcEventProbeClusterCreated::RtcEventProbeClusterCreated( + const RtcEventProbeClusterCreated& other) + : RtcEvent(other.timestamp_us_), + id_(other.id_), + bitrate_bps_(other.bitrate_bps_), + min_probes_(other.min_probes_), + min_bytes_(other.min_bytes_) {} + +std::unique_ptr<RtcEventProbeClusterCreated> RtcEventProbeClusterCreated::Copy() + const { + return absl::WrapUnique<RtcEventProbeClusterCreated>( + new RtcEventProbeClusterCreated(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h new file mode 100644 index 0000000000..ae6810c39d --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_PROBE_CLUSTER_CREATED_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_PROBE_CLUSTER_CREATED_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +struct LoggedBweProbeClusterCreatedEvent { + LoggedBweProbeClusterCreatedEvent() = default; + LoggedBweProbeClusterCreatedEvent(Timestamp timestamp, + int32_t id, + int32_t bitrate_bps, + uint32_t min_packets, + uint32_t min_bytes) + : timestamp(timestamp), + id(id), + bitrate_bps(bitrate_bps), + min_packets(min_packets), + min_bytes(min_bytes) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int32_t id; + int32_t bitrate_bps; + uint32_t min_packets; + uint32_t min_bytes; +}; + +class RtcEventProbeClusterCreated final : public RtcEvent { + public: + static constexpr Type kType = Type::ProbeClusterCreated; + + RtcEventProbeClusterCreated(int32_t id, + int32_t bitrate_bps, + uint32_t min_probes, + uint32_t min_bytes); + ~RtcEventProbeClusterCreated() override = default; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventProbeClusterCreated> Copy() const; + + int32_t id() const { return id_; } + int32_t bitrate_bps() const { return bitrate_bps_; } + uint32_t min_probes() const { return min_probes_; } + uint32_t min_bytes() const { return min_bytes_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedBweProbeClusterCreatedEvent>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventProbeClusterCreated(const RtcEventProbeClusterCreated& other); + + const int32_t id_; + const int32_t bitrate_bps_; + const uint32_t min_probes_; + const uint32_t min_bytes_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_PROBE_CLUSTER_CREATED_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_result_failure.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_result_failure.cc new file mode 100644 index 0000000000..a79b0c173d --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_result_failure.cc @@ -0,0 +1,34 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_probe_result_failure.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventProbeResultFailure::RtcEventProbeResultFailure( + int32_t id, + ProbeFailureReason failure_reason) + : id_(id), failure_reason_(failure_reason) {} + +RtcEventProbeResultFailure::RtcEventProbeResultFailure( + const RtcEventProbeResultFailure& other) + : RtcEvent(other.timestamp_us_), + id_(other.id_), + failure_reason_(other.failure_reason_) {} + +std::unique_ptr<RtcEventProbeResultFailure> RtcEventProbeResultFailure::Copy() + const { + return absl::WrapUnique<RtcEventProbeResultFailure>( + new RtcEventProbeResultFailure(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_result_failure.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_result_failure.h new file mode 100644 index 0000000000..1aa6e75cb7 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_result_failure.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_PROBE_RESULT_FAILURE_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_PROBE_RESULT_FAILURE_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +enum class ProbeFailureReason { + kInvalidSendReceiveInterval = 0, + kInvalidSendReceiveRatio, + kTimeout, + kLast +}; + +struct LoggedBweProbeFailureEvent { + LoggedBweProbeFailureEvent() = default; + LoggedBweProbeFailureEvent(Timestamp timestamp, + int32_t id, + ProbeFailureReason failure_reason) + : timestamp(timestamp), id(id), failure_reason(failure_reason) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int32_t id; + ProbeFailureReason failure_reason; +}; + +class RtcEventProbeResultFailure final : public RtcEvent { + public: + static constexpr Type kType = Type::ProbeResultFailure; + + RtcEventProbeResultFailure(int32_t id, ProbeFailureReason failure_reason); + ~RtcEventProbeResultFailure() override = default; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventProbeResultFailure> Copy() const; + + int32_t id() const { return id_; } + ProbeFailureReason failure_reason() const { return failure_reason_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedBweProbeFailureEvent>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventProbeResultFailure(const RtcEventProbeResultFailure& other); + + const int32_t id_; + const ProbeFailureReason failure_reason_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_PROBE_RESULT_FAILURE_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_result_success.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_result_success.cc new file mode 100644 index 0000000000..e7bc7c25da --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_result_success.cc @@ -0,0 +1,33 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_probe_result_success.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventProbeResultSuccess::RtcEventProbeResultSuccess(int32_t id, + int32_t bitrate_bps) + : id_(id), bitrate_bps_(bitrate_bps) {} + +RtcEventProbeResultSuccess::RtcEventProbeResultSuccess( + const RtcEventProbeResultSuccess& other) + : RtcEvent(other.timestamp_us_), + id_(other.id_), + bitrate_bps_(other.bitrate_bps_) {} + +std::unique_ptr<RtcEventProbeResultSuccess> RtcEventProbeResultSuccess::Copy() + const { + return absl::WrapUnique<RtcEventProbeResultSuccess>( + new RtcEventProbeResultSuccess(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_result_success.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_result_success.h new file mode 100644 index 0000000000..49d1abec5a --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_probe_result_success.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_PROBE_RESULT_SUCCESS_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_PROBE_RESULT_SUCCESS_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +struct LoggedBweProbeSuccessEvent { + LoggedBweProbeSuccessEvent() = default; + LoggedBweProbeSuccessEvent(Timestamp timestamp, + int32_t id, + int32_t bitrate_bps) + : timestamp(timestamp), id(id), bitrate_bps(bitrate_bps) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + int32_t id; + int32_t bitrate_bps; +}; + +class RtcEventProbeResultSuccess final : public RtcEvent { + public: + static constexpr Type kType = Type::ProbeResultSuccess; + + RtcEventProbeResultSuccess(int32_t id, int32_t bitrate_bps); + ~RtcEventProbeResultSuccess() override = default; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventProbeResultSuccess> Copy() const; + + int32_t id() const { return id_; } + int32_t bitrate_bps() const { return bitrate_bps_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedBweProbeSuccessEvent>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventProbeResultSuccess(const RtcEventProbeResultSuccess& other); + + const int32_t id_; + const int32_t bitrate_bps_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_PROBE_RESULT_SUCCESS_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_remote_estimate.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_remote_estimate.h new file mode 100644 index 0000000000..4a39ecc597 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_remote_estimate.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 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. + */ +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_REMOTE_ESTIMATE_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_REMOTE_ESTIMATE_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/data_rate.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +struct LoggedRemoteEstimateEvent { + LoggedRemoteEstimateEvent() = default; + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + absl::optional<DataRate> link_capacity_lower; + absl::optional<DataRate> link_capacity_upper; +}; + +class RtcEventRemoteEstimate final : public RtcEvent { + public: + static constexpr Type kType = Type::RemoteEstimateEvent; + + RtcEventRemoteEstimate(DataRate link_capacity_lower, + DataRate link_capacity_upper) + : link_capacity_lower_(link_capacity_lower), + link_capacity_upper_(link_capacity_upper) {} + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedRemoteEstimateEvent>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + const DataRate link_capacity_lower_; + const DataRate link_capacity_upper_; +}; + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_REMOTE_ESTIMATE_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_route_change.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_route_change.cc new file mode 100644 index 0000000000..71bd78b346 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_route_change.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 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 "logging/rtc_event_log/events/rtc_event_route_change.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventRouteChange::RtcEventRouteChange(bool connected, uint32_t overhead) + : connected_(connected), overhead_(overhead) {} + +RtcEventRouteChange::RtcEventRouteChange(const RtcEventRouteChange& other) + : RtcEvent(other.timestamp_us_), + connected_(other.connected_), + overhead_(other.overhead_) {} + +RtcEventRouteChange::~RtcEventRouteChange() = default; + +std::unique_ptr<RtcEventRouteChange> RtcEventRouteChange::Copy() const { + return absl::WrapUnique<RtcEventRouteChange>(new RtcEventRouteChange(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_route_change.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_route_change.h new file mode 100644 index 0000000000..bc1461d7bb --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_route_change.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ROUTE_CHANGE_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ROUTE_CHANGE_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" + +namespace webrtc { + +struct LoggedRouteChangeEvent { + LoggedRouteChangeEvent() = default; + LoggedRouteChangeEvent(Timestamp timestamp, bool connected, uint32_t overhead) + : timestamp(timestamp), connected(connected), overhead(overhead) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + bool connected; + uint32_t overhead; +}; + +class RtcEventRouteChange final : public RtcEvent { + public: + static constexpr Type kType = Type::RouteChangeEvent; + + RtcEventRouteChange(bool connected, uint32_t overhead); + ~RtcEventRouteChange() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventRouteChange> Copy() const; + + bool connected() const { return connected_; } + uint32_t overhead() const { return overhead_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedRouteChangeEvent>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventRouteChange(const RtcEventRouteChange& other); + + const bool connected_; + const uint32_t overhead_; +}; + +} // namespace webrtc +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ROUTE_CHANGE_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.cc new file mode 100644 index 0000000000..0ea700a024 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.cc @@ -0,0 +1,34 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventRtcpPacketIncoming::RtcEventRtcpPacketIncoming( + rtc::ArrayView<const uint8_t> packet) + : packet_(packet.data(), packet.size()) {} + +RtcEventRtcpPacketIncoming::RtcEventRtcpPacketIncoming( + const RtcEventRtcpPacketIncoming& other) + : RtcEvent(other.timestamp_us_), + packet_(other.packet_.data(), other.packet_.size()) {} + +RtcEventRtcpPacketIncoming::~RtcEventRtcpPacketIncoming() = default; + +std::unique_ptr<RtcEventRtcpPacketIncoming> RtcEventRtcpPacketIncoming::Copy() + const { + return absl::WrapUnique<RtcEventRtcpPacketIncoming>( + new RtcEventRtcpPacketIncoming(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h new file mode 100644 index 0000000000..84fe398e08 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_RTCP_PACKET_INCOMING_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_RTCP_PACKET_INCOMING_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "rtc_base/buffer.h" + +namespace webrtc { + +class RtcEventRtcpPacketIncoming final : public RtcEvent { + public: + static constexpr Type kType = Type::RtcpPacketIncoming; + + explicit RtcEventRtcpPacketIncoming(rtc::ArrayView<const uint8_t> packet); + ~RtcEventRtcpPacketIncoming() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventRtcpPacketIncoming> Copy() const; + + const rtc::Buffer& packet() const { return packet_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedRtcpPacketIncoming>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventRtcpPacketIncoming(const RtcEventRtcpPacketIncoming& other); + + rtc::Buffer packet_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_RTCP_PACKET_INCOMING_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.cc new file mode 100644 index 0000000000..b6a41ac034 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.cc @@ -0,0 +1,34 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h" + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventRtcpPacketOutgoing::RtcEventRtcpPacketOutgoing( + rtc::ArrayView<const uint8_t> packet) + : packet_(packet.data(), packet.size()) {} + +RtcEventRtcpPacketOutgoing::RtcEventRtcpPacketOutgoing( + const RtcEventRtcpPacketOutgoing& other) + : RtcEvent(other.timestamp_us_), + packet_(other.packet_.data(), other.packet_.size()) {} + +RtcEventRtcpPacketOutgoing::~RtcEventRtcpPacketOutgoing() = default; + +std::unique_ptr<RtcEventRtcpPacketOutgoing> RtcEventRtcpPacketOutgoing::Copy() + const { + return absl::WrapUnique<RtcEventRtcpPacketOutgoing>( + new RtcEventRtcpPacketOutgoing(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h new file mode 100644 index 0000000000..687bd319b4 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_RTCP_PACKET_OUTGOING_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_RTCP_PACKET_OUTGOING_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "rtc_base/buffer.h" + +namespace webrtc { + +class RtcEventRtcpPacketOutgoing final : public RtcEvent { + public: + static constexpr Type kType = Type::RtcpPacketOutgoing; + + explicit RtcEventRtcpPacketOutgoing(rtc::ArrayView<const uint8_t> packet); + ~RtcEventRtcpPacketOutgoing() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventRtcpPacketOutgoing> Copy() const; + + const rtc::Buffer& packet() const { return packet_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedRtcpPacketOutgoing>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventRtcpPacketOutgoing(const RtcEventRtcpPacketOutgoing& other); + + rtc::Buffer packet_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_RTCP_PACKET_OUTGOING_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.cc new file mode 100644 index 0000000000..4cf33a238f --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.cc @@ -0,0 +1,35 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h" + +#include "absl/memory/memory.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +RtcEventRtpPacketIncoming::RtcEventRtpPacketIncoming( + const RtpPacketReceived& packet) + : packet_(packet) {} + +RtcEventRtpPacketIncoming::RtcEventRtpPacketIncoming( + const RtcEventRtpPacketIncoming& other) + : RtcEvent(other.timestamp_us_), packet_(other.packet_) {} + +RtcEventRtpPacketIncoming::~RtcEventRtpPacketIncoming() = default; + +std::unique_ptr<RtcEventRtpPacketIncoming> RtcEventRtpPacketIncoming::Copy() + const { + return absl::WrapUnique<RtcEventRtpPacketIncoming>( + new RtcEventRtpPacketIncoming(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h new file mode 100644 index 0000000000..66ea167a1c --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h @@ -0,0 +1,93 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_RTP_PACKET_INCOMING_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_RTP_PACKET_INCOMING_H_ + +#include <cstddef> +#include <cstdint> +#include <map> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "modules/rtp_rtcp/source/rtp_packet.h" + +namespace webrtc { + +class RtpPacketReceived; + +class RtcEventRtpPacketIncoming final : public RtcEvent { + public: + static constexpr Type kType = Type::RtpPacketIncoming; + + explicit RtcEventRtpPacketIncoming(const RtpPacketReceived& packet); + ~RtcEventRtpPacketIncoming() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventRtpPacketIncoming> Copy() const; + + size_t packet_length() const { return packet_.size(); } + + rtc::ArrayView<const uint8_t> RawHeader() const { + return rtc::MakeArrayView(packet_.data(), header_length()); + } + uint32_t Ssrc() const { return packet_.Ssrc(); } + uint32_t Timestamp() const { return packet_.Timestamp(); } + uint16_t SequenceNumber() const { return packet_.SequenceNumber(); } + uint8_t PayloadType() const { return packet_.PayloadType(); } + bool Marker() const { return packet_.Marker(); } + template <typename ExtensionTrait, typename... Args> + bool GetExtension(Args&&... args) const { + return packet_.GetExtension<ExtensionTrait>(std::forward<Args>(args)...); + } + template <typename ExtensionTrait> + rtc::ArrayView<const uint8_t> GetRawExtension() const { + return packet_.GetRawExtension<ExtensionTrait>(); + } + template <typename ExtensionTrait> + bool HasExtension() const { + return packet_.HasExtension<ExtensionTrait>(); + } + + size_t payload_length() const { return packet_.payload_size(); } + size_t header_length() const { return packet_.headers_size(); } + size_t padding_length() const { return packet_.padding_size(); } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::map<uint32_t, std::vector<LoggedRtpPacketIncoming>>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventRtpPacketIncoming(const RtcEventRtpPacketIncoming& other); + + const RtpPacket packet_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_RTP_PACKET_INCOMING_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.cc new file mode 100644 index 0000000000..a6a4d99702 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.cc @@ -0,0 +1,38 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h" + +#include "absl/memory/memory.h" +#include "modules/rtp_rtcp/source/rtp_packet_to_send.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +RtcEventRtpPacketOutgoing::RtcEventRtpPacketOutgoing( + const RtpPacketToSend& packet, + int probe_cluster_id) + : packet_(packet), probe_cluster_id_(probe_cluster_id) {} + +RtcEventRtpPacketOutgoing::RtcEventRtpPacketOutgoing( + const RtcEventRtpPacketOutgoing& other) + : RtcEvent(other.timestamp_us_), + packet_(other.packet_), + probe_cluster_id_(other.probe_cluster_id_) {} + +RtcEventRtpPacketOutgoing::~RtcEventRtpPacketOutgoing() = default; + +std::unique_ptr<RtcEventRtpPacketOutgoing> RtcEventRtpPacketOutgoing::Copy() + const { + return absl::WrapUnique<RtcEventRtpPacketOutgoing>( + new RtcEventRtpPacketOutgoing(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h new file mode 100644 index 0000000000..4f4be04445 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h @@ -0,0 +1,97 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_RTP_PACKET_OUTGOING_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_RTP_PACKET_OUTGOING_H_ + +#include <cstddef> +#include <cstdint> +#include <map> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "modules/rtp_rtcp/source/rtp_packet.h" + +namespace webrtc { + +class RtpPacketToSend; + +class RtcEventRtpPacketOutgoing final : public RtcEvent { + public: + static constexpr Type kType = Type::RtpPacketOutgoing; + + RtcEventRtpPacketOutgoing(const RtpPacketToSend& packet, + int probe_cluster_id); + ~RtcEventRtpPacketOutgoing() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return false; } + + std::unique_ptr<RtcEventRtpPacketOutgoing> Copy() const; + + size_t packet_length() const { return packet_.size(); } + + rtc::ArrayView<const uint8_t> RawHeader() const { + return rtc::MakeArrayView(packet_.data(), header_length()); + } + uint32_t Ssrc() const { return packet_.Ssrc(); } + uint32_t Timestamp() const { return packet_.Timestamp(); } + uint16_t SequenceNumber() const { return packet_.SequenceNumber(); } + uint8_t PayloadType() const { return packet_.PayloadType(); } + bool Marker() const { return packet_.Marker(); } + template <typename ExtensionTrait, typename... Args> + bool GetExtension(Args&&... args) const { + return packet_.GetExtension<ExtensionTrait>(std::forward<Args>(args)...); + } + template <typename ExtensionTrait> + rtc::ArrayView<const uint8_t> GetRawExtension() const { + return packet_.GetRawExtension<ExtensionTrait>(); + } + template <typename ExtensionTrait> + bool HasExtension() const { + return packet_.HasExtension<ExtensionTrait>(); + } + + size_t payload_length() const { return packet_.payload_size(); } + size_t header_length() const { return packet_.headers_size(); } + size_t padding_length() const { return packet_.padding_size(); } + int probe_cluster_id() const { return probe_cluster_id_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::map<uint32_t, std::vector<LoggedRtpPacketOutgoing>>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventRtpPacketOutgoing(const RtcEventRtpPacketOutgoing& other); + + const RtpPacket packet_; + // TODO(eladalon): Delete `probe_cluster_id_` along with legacy encoding. + const int probe_cluster_id_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_RTP_PACKET_OUTGOING_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.cc new file mode 100644 index 0000000000..90ab8185a3 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.cc @@ -0,0 +1,39 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h" + +#include <utility> + +#include "absl/memory/memory.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +RtcEventVideoReceiveStreamConfig::RtcEventVideoReceiveStreamConfig( + std::unique_ptr<rtclog::StreamConfig> config) + : config_(std::move(config)) { + RTC_DCHECK(config_); +} + +RtcEventVideoReceiveStreamConfig::RtcEventVideoReceiveStreamConfig( + const RtcEventVideoReceiveStreamConfig& other) + : RtcEvent(other.timestamp_us_), + config_(std::make_unique<rtclog::StreamConfig>(*other.config_)) {} + +RtcEventVideoReceiveStreamConfig::~RtcEventVideoReceiveStreamConfig() = default; + +std::unique_ptr<RtcEventVideoReceiveStreamConfig> +RtcEventVideoReceiveStreamConfig::Copy() const { + return absl::WrapUnique<RtcEventVideoReceiveStreamConfig>( + new RtcEventVideoReceiveStreamConfig(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h new file mode 100644 index 0000000000..0be56c2065 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_VIDEO_RECEIVE_STREAM_CONFIG_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_VIDEO_RECEIVE_STREAM_CONFIG_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/rtc_stream_config.h" + +namespace webrtc { + +struct LoggedVideoRecvConfig { + LoggedVideoRecvConfig() = default; + LoggedVideoRecvConfig(Timestamp timestamp, const rtclog::StreamConfig config) + : timestamp(timestamp), config(config) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtclog::StreamConfig config; +}; + +class RtcEventVideoReceiveStreamConfig final : public RtcEvent { + public: + static constexpr Type kType = Type::VideoReceiveStreamConfig; + + explicit RtcEventVideoReceiveStreamConfig( + std::unique_ptr<rtclog::StreamConfig> config); + ~RtcEventVideoReceiveStreamConfig() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return true; } + + std::unique_ptr<RtcEventVideoReceiveStreamConfig> Copy() const; + + const rtclog::StreamConfig& config() const { return *config_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedVideoRecvConfig>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventVideoReceiveStreamConfig( + const RtcEventVideoReceiveStreamConfig& other); + + const std::unique_ptr<const rtclog::StreamConfig> config_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_VIDEO_RECEIVE_STREAM_CONFIG_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_video_send_stream_config.cc b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_video_send_stream_config.cc new file mode 100644 index 0000000000..c28a476d01 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_video_send_stream_config.cc @@ -0,0 +1,36 @@ +/* + * 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 "logging/rtc_event_log/events/rtc_event_video_send_stream_config.h" + +#include <utility> + +#include "absl/memory/memory.h" + +namespace webrtc { + +RtcEventVideoSendStreamConfig::RtcEventVideoSendStreamConfig( + std::unique_ptr<rtclog::StreamConfig> config) + : config_(std::move(config)) {} + +RtcEventVideoSendStreamConfig::RtcEventVideoSendStreamConfig( + const RtcEventVideoSendStreamConfig& other) + : RtcEvent(other.timestamp_us_), + config_(std::make_unique<rtclog::StreamConfig>(*other.config_)) {} + +RtcEventVideoSendStreamConfig::~RtcEventVideoSendStreamConfig() = default; + +std::unique_ptr<RtcEventVideoSendStreamConfig> +RtcEventVideoSendStreamConfig::Copy() const { + return absl::WrapUnique<RtcEventVideoSendStreamConfig>( + new RtcEventVideoSendStreamConfig(*this)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h new file mode 100644 index 0000000000..f1717b19ea --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_VIDEO_SEND_STREAM_CONFIG_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_VIDEO_SEND_STREAM_CONFIG_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/rtc_stream_config.h" + +namespace webrtc { + +struct LoggedVideoSendConfig { + LoggedVideoSendConfig() = default; + LoggedVideoSendConfig(Timestamp timestamp, const rtclog::StreamConfig config) + : timestamp(timestamp), config(config) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } + + Timestamp timestamp = Timestamp::MinusInfinity(); + rtclog::StreamConfig config; +}; + +class RtcEventVideoSendStreamConfig final : public RtcEvent { + public: + static constexpr Type kType = Type::VideoSendStreamConfig; + + explicit RtcEventVideoSendStreamConfig( + std::unique_ptr<rtclog::StreamConfig> config); + ~RtcEventVideoSendStreamConfig() override; + + Type GetType() const override { return kType; } + bool IsConfigEvent() const override { return true; } + + std::unique_ptr<RtcEventVideoSendStreamConfig> Copy() const; + + const rtclog::StreamConfig& config() const { return *config_; } + + static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) { + // TODO(terelius): Implement + return ""; + } + + static RtcEventLogParseStatus Parse( + absl::string_view encoded_bytes, + bool batched, + std::vector<LoggedVideoSendConfig>& output) { + // TODO(terelius): Implement + return RtcEventLogParseStatus::Error("Not Implemented", __FILE__, __LINE__); + } + + private: + RtcEventVideoSendStreamConfig(const RtcEventVideoSendStreamConfig& other); + + const std::unique_ptr<const rtclog::StreamConfig> config_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_VIDEO_SEND_STREAM_CONFIG_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log.cc b/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log.cc new file mode 100644 index 0000000000..5a44b00694 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log.cc @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 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 "logging/rtc_event_log/fake_rtc_event_log.h" + +#include <map> +#include <memory> + +#include "api/rtc_event_log/rtc_event_log.h" +#include "rtc_base/synchronization/mutex.h" + +namespace webrtc { + +bool FakeRtcEventLog::StartLogging(std::unique_ptr<RtcEventLogOutput> output, + int64_t output_period_ms) { + return true; +} + +void FakeRtcEventLog::StopLogging() {} + +void FakeRtcEventLog::Log(std::unique_ptr<RtcEvent> event) { + MutexLock lock(&mu_); + ++count_[event->GetType()]; +} + +int FakeRtcEventLog::GetEventCount(RtcEvent::Type event_type) { + MutexLock lock(&mu_); + return count_[event_type]; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log.h b/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log.h new file mode 100644 index 0000000000..effa7507f1 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_FAKE_RTC_EVENT_LOG_H_ +#define LOGGING_RTC_EVENT_LOG_FAKE_RTC_EVENT_LOG_H_ + +#include <map> +#include <memory> + +#include "api/rtc_event_log/rtc_event.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class FakeRtcEventLog : public RtcEventLog { + public: + FakeRtcEventLog() = default; + ~FakeRtcEventLog() override = default; + + bool StartLogging(std::unique_ptr<RtcEventLogOutput> output, + int64_t output_period_ms) override; + void StopLogging() override; + void Log(std::unique_ptr<RtcEvent> event) override; + int GetEventCount(RtcEvent::Type event_type); + + private: + Mutex mu_; + std::map<RtcEvent::Type, int> count_ RTC_GUARDED_BY(mu_); +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_FAKE_RTC_EVENT_LOG_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.cc b/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.cc new file mode 100644 index 0000000000..47db40c9f4 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.cc @@ -0,0 +1,33 @@ +/* + * Copyright 2018 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 "logging/rtc_event_log/fake_rtc_event_log_factory.h" + +#include <memory> + +#include "api/rtc_event_log/rtc_event_log.h" +#include "logging/rtc_event_log/fake_rtc_event_log.h" + +namespace webrtc { + +std::unique_ptr<RtcEventLog> FakeRtcEventLogFactory::Create( + RtcEventLog::EncodingType /*encoding_type*/) const { + auto fake_event_log = std::make_unique<FakeRtcEventLog>(); + const_cast<FakeRtcEventLogFactory*>(this)->last_log_created_ = + fake_event_log.get(); + return fake_event_log; +} + +std::unique_ptr<RtcEventLog> FakeRtcEventLogFactory::CreateRtcEventLog( + RtcEventLog::EncodingType encoding_type) { + return Create(encoding_type); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.h b/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.h new file mode 100644 index 0000000000..c7ff33dee4 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/fake_rtc_event_log_factory.h @@ -0,0 +1,40 @@ +/* + * Copyright 2018 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_FAKE_RTC_EVENT_LOG_FACTORY_H_ +#define LOGGING_RTC_EVENT_LOG_FAKE_RTC_EVENT_LOG_FACTORY_H_ + +#include <memory> + +#include "api/rtc_event_log/rtc_event_log_factory_interface.h" +#include "logging/rtc_event_log/fake_rtc_event_log.h" + +namespace webrtc { + +class FakeRtcEventLogFactory : public RtcEventLogFactoryInterface { + public: + FakeRtcEventLogFactory() = default; + ~FakeRtcEventLogFactory() override = default; + + std::unique_ptr<RtcEventLog> Create( + RtcEventLog::EncodingType encoding_type) const override; + + std::unique_ptr<RtcEventLog> CreateRtcEventLog( + RtcEventLog::EncodingType encoding_type) override; + + webrtc::FakeRtcEventLog* last_log_created() { return last_log_created_; } + + private: + webrtc::FakeRtcEventLog* last_log_created_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_FAKE_RTC_EVENT_LOG_FACTORY_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/ice_logger.cc b/third_party/libwebrtc/logging/rtc_event_log/ice_logger.cc new file mode 100644 index 0000000000..390deda953 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/ice_logger.cc @@ -0,0 +1,52 @@ +/* + * 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 "logging/rtc_event_log/ice_logger.h" + +#include <memory> + +#include "api/rtc_event_log/rtc_event_log.h" + +namespace webrtc { + +IceEventLog::IceEventLog() {} +IceEventLog::~IceEventLog() {} + +void IceEventLog::LogCandidatePairConfig( + IceCandidatePairConfigType type, + uint32_t candidate_pair_id, + const IceCandidatePairDescription& candidate_pair_desc) { + if (event_log_ == nullptr) { + return; + } + candidate_pair_desc_by_id_[candidate_pair_id] = candidate_pair_desc; + event_log_->Log(std::make_unique<RtcEventIceCandidatePairConfig>( + type, candidate_pair_id, candidate_pair_desc)); +} + +void IceEventLog::LogCandidatePairEvent(IceCandidatePairEventType type, + uint32_t candidate_pair_id, + uint32_t transaction_id) { + if (event_log_ == nullptr) { + return; + } + event_log_->Log(std::make_unique<RtcEventIceCandidatePair>( + type, candidate_pair_id, transaction_id)); +} + +void IceEventLog::DumpCandidatePairDescriptionToMemoryAsConfigEvents() const { + for (const auto& desc_id_pair : candidate_pair_desc_by_id_) { + event_log_->Log(std::make_unique<RtcEventIceCandidatePairConfig>( + IceCandidatePairConfigType::kUpdated, desc_id_pair.first, + desc_id_pair.second)); + } +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/ice_logger.h b/third_party/libwebrtc/logging/rtc_event_log/ice_logger.h new file mode 100644 index 0000000000..0dea43bf9d --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/ice_logger.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_ICE_LOGGER_H_ +#define LOGGING_RTC_EVENT_LOG_ICE_LOGGER_H_ + +#include <unordered_map> + +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" + +namespace webrtc { + +class RtcEventLog; + +// IceEventLog wraps RtcEventLog and provides structural logging of ICE-specific +// events. The logged events are serialized with other RtcEvent's if protobuf is +// enabled in the build. +class IceEventLog { + public: + IceEventLog(); + ~IceEventLog(); + + void set_event_log(RtcEventLog* event_log) { event_log_ = event_log; } + + void LogCandidatePairConfig( + IceCandidatePairConfigType type, + uint32_t candidate_pair_id, + const IceCandidatePairDescription& candidate_pair_desc); + + void LogCandidatePairEvent(IceCandidatePairEventType type, + uint32_t candidate_pair_id, + uint32_t transaction_id); + + // This method constructs a config event for each candidate pair with their + // description and logs these config events. It is intended to be called when + // logging starts to ensure that we have at least one config for each + // candidate pair id. + void DumpCandidatePairDescriptionToMemoryAsConfigEvents() const; + + private: + RtcEventLog* event_log_ = nullptr; + std::unordered_map<uint32_t, IceCandidatePairDescription> + candidate_pair_desc_by_id_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ICE_LOGGER_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/mock/mock_rtc_event_log.cc b/third_party/libwebrtc/logging/rtc_event_log/mock/mock_rtc_event_log.cc new file mode 100644 index 0000000000..240a4f6693 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/mock/mock_rtc_event_log.cc @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2018 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 "logging/rtc_event_log/mock/mock_rtc_event_log.h" + +namespace webrtc { + +MockRtcEventLog::MockRtcEventLog() = default; +MockRtcEventLog::~MockRtcEventLog() = default; + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/mock/mock_rtc_event_log.h b/third_party/libwebrtc/logging/rtc_event_log/mock/mock_rtc_event_log.h new file mode 100644 index 0000000000..646831de27 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/mock/mock_rtc_event_log.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_MOCK_MOCK_RTC_EVENT_LOG_H_ +#define LOGGING_RTC_EVENT_LOG_MOCK_MOCK_RTC_EVENT_LOG_H_ + +#include <memory> + +#include "api/rtc_event_log/rtc_event_log.h" +#include "test/gmock.h" + +namespace webrtc { + +class MockRtcEventLog : public RtcEventLog { + public: + MockRtcEventLog(); + ~MockRtcEventLog() override; + + MOCK_METHOD(bool, + StartLogging, + (std::unique_ptr<RtcEventLogOutput> output, + int64_t output_period_ms), + (override)); + + MOCK_METHOD(void, StopLogging, (), (override)); + + void Log(std::unique_ptr<RtcEvent> event) override { + return LogProxy(event.get()); + } + MOCK_METHOD(void, LogProxy, (RtcEvent*)); +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_MOCK_MOCK_RTC_EVENT_LOG_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/output/rtc_event_log_output_file.h b/third_party/libwebrtc/logging/rtc_event_log/output/rtc_event_log_output_file.h new file mode 100644 index 0000000000..86be01d884 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/output/rtc_event_log_output_file.h @@ -0,0 +1,19 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_OUTPUT_RTC_EVENT_LOG_OUTPUT_FILE_H_ +#define LOGGING_RTC_EVENT_LOG_OUTPUT_RTC_EVENT_LOG_OUTPUT_FILE_H_ + +// TODO(bugs.webrtc.org/6463): For backwards compatibility; delete as soon as +// downstream dependencies are updated. + +#include "api/rtc_event_log_output_file.h" + +#endif // LOGGING_RTC_EVENT_LOG_OUTPUT_RTC_EVENT_LOG_OUTPUT_FILE_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log.proto b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log.proto new file mode 100644 index 0000000000..12baa493bd --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log.proto @@ -0,0 +1,427 @@ +syntax = "proto2"; +option optimize_for = LITE_RUNTIME; +package webrtc.rtclog; + +enum MediaType { + ANY = 0; + AUDIO = 1; + VIDEO = 2; + DATA = 3; +} + +// This is the main message to dump to a file, it can contain multiple event +// messages, but it is possible to append multiple EventStreams (each with a +// single event) to a file. +// This has the benefit that there's no need to keep all data in memory. +message EventStream { + repeated Event stream = 1; +} + +message Event { + // required - Elapsed wallclock time in us since the start of the log. + optional int64 timestamp_us = 1; + + // The different types of events that can occur, the UNKNOWN_EVENT entry + // is added in case future EventTypes are added, in that case old code will + // receive the new events as UNKNOWN_EVENT. + enum EventType { + UNKNOWN_EVENT = 0; + LOG_START = 1; + LOG_END = 2; + RTP_EVENT = 3; + RTCP_EVENT = 4; + AUDIO_PLAYOUT_EVENT = 5; + LOSS_BASED_BWE_UPDATE = 6; + DELAY_BASED_BWE_UPDATE = 7; + VIDEO_RECEIVER_CONFIG_EVENT = 8; + VIDEO_SENDER_CONFIG_EVENT = 9; + AUDIO_RECEIVER_CONFIG_EVENT = 10; + AUDIO_SENDER_CONFIG_EVENT = 11; + AUDIO_NETWORK_ADAPTATION_EVENT = 16; + BWE_PROBE_CLUSTER_CREATED_EVENT = 17; + BWE_PROBE_RESULT_EVENT = 18; + ALR_STATE_EVENT = 19; + ICE_CANDIDATE_PAIR_CONFIG = 20; + ICE_CANDIDATE_PAIR_EVENT = 21; + REMOTE_ESTIMATE = 22; + } + + // required - Indicates the type of this event + optional EventType type = 2; + + oneof subtype { + // required if type == RTP_EVENT + RtpPacket rtp_packet = 3; + + // required if type == RTCP_EVENT + RtcpPacket rtcp_packet = 4; + + // required if type == AUDIO_PLAYOUT_EVENT + AudioPlayoutEvent audio_playout_event = 5; + + // required if type == LOSS_BASED_BWE_UPDATE + LossBasedBweUpdate loss_based_bwe_update = 6; + + // required if type == DELAY_BASED_BWE_UPDATE + DelayBasedBweUpdate delay_based_bwe_update = 7; + + // required if type == VIDEO_RECEIVER_CONFIG_EVENT + VideoReceiveConfig video_receiver_config = 8; + + // required if type == VIDEO_SENDER_CONFIG_EVENT + VideoSendConfig video_sender_config = 9; + + // required if type == AUDIO_RECEIVER_CONFIG_EVENT + AudioReceiveConfig audio_receiver_config = 10; + + // required if type == AUDIO_SENDER_CONFIG_EVENT + AudioSendConfig audio_sender_config = 11; + + // required if type == AUDIO_NETWORK_ADAPTATION_EVENT + AudioNetworkAdaptation audio_network_adaptation = 16; + + // required if type == BWE_PROBE_CLUSTER_CREATED_EVENT + BweProbeCluster probe_cluster = 17; + + // required if type == BWE_PROBE_RESULT_EVENT + BweProbeResult probe_result = 18; + + // required if type == ALR_STATE_EVENT + AlrState alr_state = 19; + + // required if type == ICE_CANDIDATE_PAIR_CONFIG + IceCandidatePairConfig ice_candidate_pair_config = 20; + + // required if type == ICE_CANDIDATE_PAIR_EVENT + IceCandidatePairEvent ice_candidate_pair_event = 21; + + // required if type == REMOTE_ESTIMATE + RemoteEstimate remote_estimate = 22; + } +} + +message RtpPacket { + // required - True if the packet is incoming w.r.t. the user logging the data + optional bool incoming = 1; + + optional MediaType type = 2 [deprecated = true]; + + // required - The size of the packet including both payload and header. + optional uint32 packet_length = 3; + + // required - The RTP header only. + optional bytes header = 4; + + // optional - The probe cluster id. + optional int32 probe_cluster_id = 5; + + // Do not add code to log user payload data without a privacy review! +} + +message RtcpPacket { + // required - True if the packet is incoming w.r.t. the user logging the data + optional bool incoming = 1; + + optional MediaType type = 2 [deprecated = true]; + + // required - The whole packet including both payload and header. + optional bytes packet_data = 3; +} + +message AudioPlayoutEvent { + // TODO(ivoc): Rename, we currently use the "remote" ssrc, i.e. identifying + // the receive stream, while local_ssrc identifies the send stream, if any. + // required - The SSRC of the audio stream associated with the playout event. + optional uint32 local_ssrc = 2; +} + +message LossBasedBweUpdate { + // required - Bandwidth estimate (in bps) after the update. + optional int32 bitrate_bps = 1; + + // required - Fraction of lost packets since last receiver report + // computed as floor( 256 * (#lost_packets / #total_packets) ). + // The possible values range from 0 to 255. + optional uint32 fraction_loss = 2; + + // TODO(terelius): Is this really needed? Remove or make optional? + // required - Total number of packets that the BWE update is based on. + optional int32 total_packets = 3; +} + +message DelayBasedBweUpdate { + enum DetectorState { + BWE_NORMAL = 0; + BWE_UNDERUSING = 1; + BWE_OVERUSING = 2; + } + + // required - Bandwidth estimate (in bps) after the update. + optional int32 bitrate_bps = 1; + + // required - The state of the overuse detector. + optional DetectorState detector_state = 2; +} + +// TODO(terelius): Video and audio streams could in principle share SSRC, +// so identifying a stream based only on SSRC might not work. +// It might be better to use a combination of SSRC and media type +// or SSRC and port number, but for now we will rely on SSRC only. +message VideoReceiveConfig { + // required - Synchronization source (stream identifier) to be received. + optional uint32 remote_ssrc = 1; + // required - Sender SSRC used for sending RTCP (such as receiver reports). + optional uint32 local_ssrc = 2; + + // Compound mode is described by RFC 4585 and reduced-size + // RTCP mode is described by RFC 5506. + enum RtcpMode { + RTCP_COMPOUND = 1; + RTCP_REDUCEDSIZE = 2; + } + // required - RTCP mode to use. + optional RtcpMode rtcp_mode = 3; + + // required - Receiver estimated maximum bandwidth. + optional bool remb = 4; + + // Map from video RTP payload type -> RTX config. + repeated RtxMap rtx_map = 5; + + // RTP header extensions used for the received stream. + repeated RtpHeaderExtension header_extensions = 6; + + // List of decoders associated with the stream. + repeated DecoderConfig decoders = 7; +} + +// Maps decoder names to payload types. +message DecoderConfig { + // required + optional string name = 1; + + // required + optional int32 payload_type = 2; +} + +// Maps RTP header extension names to numerical IDs. +message RtpHeaderExtension { + // required + optional string name = 1; + + // required + optional int32 id = 2; +} + +// RTX settings for incoming video payloads that may be received. +// RTX is disabled if there's no config present. +message RtxConfig { + // required - SSRC to use for the RTX stream. + optional uint32 rtx_ssrc = 1; + + // required - Payload type to use for the RTX stream. + optional int32 rtx_payload_type = 2; +} + +message RtxMap { + // required + optional int32 payload_type = 1; + + // required + optional RtxConfig config = 2; +} + +message VideoSendConfig { + // Synchronization source (stream identifier) for outgoing stream. + // One stream can have several ssrcs for e.g. simulcast. + // At least one ssrc is required. + repeated uint32 ssrcs = 1; + + // RTP header extensions used for the outgoing stream. + repeated RtpHeaderExtension header_extensions = 2; + + // List of SSRCs for retransmitted packets. + repeated uint32 rtx_ssrcs = 3; + + // required if rtx_ssrcs is used - Payload type for retransmitted packets. + optional int32 rtx_payload_type = 4; + + // required - Encoder associated with the stream. + optional EncoderConfig encoder = 5; +} + +// Maps encoder names to payload types. +message EncoderConfig { + // required + optional string name = 1; + + // required + optional int32 payload_type = 2; +} + +message AudioReceiveConfig { + // required - Synchronization source (stream identifier) to be received. + optional uint32 remote_ssrc = 1; + + // required - Sender SSRC used for sending RTCP (such as receiver reports). + optional uint32 local_ssrc = 2; + + // RTP header extensions used for the received audio stream. + repeated RtpHeaderExtension header_extensions = 3; +} + +message AudioSendConfig { + // required - Synchronization source (stream identifier) for outgoing stream. + optional uint32 ssrc = 1; + + // RTP header extensions used for the outgoing audio stream. + repeated RtpHeaderExtension header_extensions = 2; +} + +message AudioNetworkAdaptation { + // Bit rate that the audio encoder is operating at. + optional int32 bitrate_bps = 1; + + // Frame length that each encoded audio packet consists of. + optional int32 frame_length_ms = 2; + + // Packet loss fraction that the encoder's forward error correction (FEC) is + // optimized for. + optional float uplink_packet_loss_fraction = 3; + + // Whether forward error correction (FEC) is turned on or off. + optional bool enable_fec = 4; + + // Whether discontinuous transmission (DTX) is turned on or off. + optional bool enable_dtx = 5; + + // Number of audio channels that each encoded packet consists of. + optional uint32 num_channels = 6; +} + +message BweProbeCluster { + // required - The id of this probe cluster. + optional int32 id = 1; + + // required - The bitrate in bps that this probe cluster is meant to probe. + optional int32 bitrate_bps = 2; + + // required - The minimum number of packets used to probe the given bitrate. + optional uint32 min_packets = 3; + + // required - The minimum number of bytes used to probe the given bitrate. + optional uint32 min_bytes = 4; +} + +message BweProbeResult { + // required - The id of this probe cluster. + optional int32 id = 1; + + enum ResultType { + SUCCESS = 0; + INVALID_SEND_RECEIVE_INTERVAL = 1; + INVALID_SEND_RECEIVE_RATIO = 2; + TIMEOUT = 3; + } + + // required - The result of this probing attempt. + optional ResultType result = 2; + + // optional - but required if result == SUCCESS. The resulting bitrate in bps. + optional int32 bitrate_bps = 3; +} + +message RemoteEstimate { + // optional - Lower estimate of link capacity. + optional uint32 link_capacity_lower_kbps = 1; + + // optional - Upper estimate of link capacity. + optional uint32 link_capacity_upper_kbps = 2; +} + +message AlrState { + // required - If we are in ALR or not. + optional bool in_alr = 1; +} + +message IceCandidatePairConfig { + enum IceCandidatePairConfigType { + ADDED = 0; + UPDATED = 1; + DESTROYED = 2; + SELECTED = 3; + } + + enum IceCandidateType { + LOCAL = 0; + STUN = 1; + PRFLX = 2; + RELAY = 3; + UNKNOWN_CANDIDATE_TYPE = 4; + } + + enum Protocol { + UDP = 0; + TCP = 1; + SSLTCP = 2; + TLS = 3; + UNKNOWN_PROTOCOL = 4; + } + + enum AddressFamily { + IPV4 = 0; + IPV6 = 1; + UNKNOWN_ADDRESS_FAMILY = 2; + } + + enum NetworkType { + ETHERNET = 0; + LOOPBACK = 1; + WIFI = 2; + VPN = 3; + CELLULAR = 4; + UNKNOWN_NETWORK_TYPE = 5; + } + + // required + optional IceCandidatePairConfigType config_type = 1; + + // required + optional uint32 candidate_pair_id = 2; + + // required + optional IceCandidateType local_candidate_type = 3; + + // required + optional Protocol local_relay_protocol = 4; + + // required + optional NetworkType local_network_type = 5; + + // required + optional AddressFamily local_address_family = 6; + + // required + optional IceCandidateType remote_candidate_type = 7; + + // required + optional AddressFamily remote_address_family = 8; + + // required + optional Protocol candidate_pair_protocol = 9; +} + +message IceCandidatePairEvent { + enum IceCandidatePairEventType { + CHECK_SENT = 0; + CHECK_RECEIVED = 1; + CHECK_RESPONSE_SENT = 2; + CHECK_RESPONSE_RECEIVED = 3; + } + + // required + optional IceCandidatePairEventType event_type = 1; + + // required + optional uint32 candidate_pair_id = 2; +} diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2.proto b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2.proto new file mode 100644 index 0000000000..a417ded706 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2.proto @@ -0,0 +1,777 @@ +// THIS FILE IS EXPERIMENTAL. BREAKING CHANGES MAY BE MADE AT ANY TIME +// WITHOUT PRIOR WARNING. THIS FILE SHOULD NOT BE USED IN PRODUCTION CODE. + +syntax = "proto2"; +option optimize_for = LITE_RUNTIME; +package webrtc.rtclog2; + +// At the top level, a WebRTC event log is just an EventStream object. Note that +// concatenating multiple EventStreams in the same file is equivalent to a +// single EventStream object containing the same events. Hence, it is not +// necessary to wait for the entire log to be complete before beginning to +// write it to a file. +// Note: For all X_deltas fields, we rely on the default value being an +// empty string. +message EventStream { + // Deprecated - Maintained for compatibility with the old event log. + repeated Event stream = 1 [deprecated = true]; + repeated IncomingRtpPackets incoming_rtp_packets = 2; + repeated OutgoingRtpPackets outgoing_rtp_packets = 3; + repeated IncomingRtcpPackets incoming_rtcp_packets = 4; + repeated OutgoingRtcpPackets outgoing_rtcp_packets = 5; + repeated AudioPlayoutEvents audio_playout_events = 6; + repeated FrameDecodedEvents frame_decoded_events = 7; + // The field tags 8-15 are reserved for the most common events. + repeated BeginLogEvent begin_log_events = 16; + repeated EndLogEvent end_log_events = 17; + repeated LossBasedBweUpdates loss_based_bwe_updates = 18; + repeated DelayBasedBweUpdates delay_based_bwe_updates = 19; + repeated AudioNetworkAdaptations audio_network_adaptations = 20; + repeated BweProbeCluster probe_clusters = 21; + repeated BweProbeResultSuccess probe_success = 22; + repeated BweProbeResultFailure probe_failure = 23; + repeated AlrState alr_states = 24; + repeated IceCandidatePairConfig ice_candidate_configs = 25; + repeated IceCandidatePairEvent ice_candidate_events = 26; + repeated DtlsTransportStateEvent dtls_transport_state_events = 27; + repeated DtlsWritableState dtls_writable_states = 28; + repeated GenericPacketSent generic_packets_sent = 29; + repeated GenericPacketReceived generic_packets_received = 30; + repeated GenericAckReceived generic_acks_received = 31; + repeated RouteChange route_changes = 32; + repeated RemoteEstimates remote_estimates = 33; + repeated NetEqSetMinimumDelay neteq_set_minimum_delay = 34; + + repeated AudioRecvStreamConfig audio_recv_stream_configs = 101; + repeated AudioSendStreamConfig audio_send_stream_configs = 102; + repeated VideoRecvStreamConfig video_recv_stream_configs = 103; + repeated VideoSendStreamConfig video_send_stream_configs = 104; +} + +// DEPRECATED. +message Event { + // TODO(terelius): Do we want to preserve the old Event definition here? +} + +message GenericPacketReceived { + // All fields are required. + optional int64 timestamp_ms = 1; + optional int64 packet_number = 2; + // Length of the packet in bytes. + optional int32 packet_length = 3; + + // Provided if there are deltas in the batch. + optional uint32 number_of_deltas = 16; + optional bytes timestamp_ms_deltas = 17; + optional bytes packet_number_deltas = 18; + optional bytes packet_length_deltas = 19; +} + +message GenericPacketSent { + // All fields are required. All lengths in bytes. + optional int64 timestamp_ms = 1; + optional int64 packet_number = 2; + // overhead+payload+padding length = packet_length in bytes. + optional int32 overhead_length = 3; + optional int32 payload_length = 4; + optional int32 padding_length = 5; + + optional uint32 number_of_deltas = 16; + optional bytes timestamp_ms_deltas = 17; + optional bytes packet_number_deltas = 18; + optional bytes overhead_length_deltas = 19; + optional bytes payload_length_deltas = 20; + optional bytes padding_length_deltas = 21; +} + +message GenericAckReceived { + optional int64 timestamp_ms = 1; + + // ID of the received packet. + optional int64 packet_number = 2; + + // ID of the packet that was acked. + optional int64 acked_packet_number = 3; + + // Timestamp in ms when the packet was received by the other side. + optional int64 receive_acked_packet_time_ms = 4; + + optional uint32 number_of_deltas = 16; + optional bytes timestamp_ms_deltas = 17; + optional bytes packet_number_deltas = 18; + optional bytes acked_packet_number_deltas = 19; + optional bytes receive_acked_packet_time_ms_deltas = 20; +} + +message DependencyDescriptorsWireInfo { + // required + // Base and delta encoded B and E bits represented as two bit numbers. + optional uint32 start_end_bit = 1; + optional bytes start_end_bit_deltas = 2; + + // required + // Base and delta encoded template IDs, represented as six bit numbers. + optional uint32 template_id = 3; + optional bytes template_id_deltas = 4; + + // required + // Base and delta compressed frame IDs. + optional uint32 frame_id = 5; + optional bytes frame_id_deltas = 6; + + // optional - set if any DD contains extended information. + // The extended info encoded as optional blobs. + optional bytes extended_infos = 7; +} + +message IncomingRtpPackets { + // required + optional int64 timestamp_ms = 1; + + // required - RTP marker bit, used to label boundaries between video frames. + optional bool marker = 2; + + // required - RTP payload type. + optional uint32 payload_type = 3; + + // required - RTP sequence number. + optional uint32 sequence_number = 4; + + // required - RTP monotonic clock timestamp (not actual time). + optional fixed32 rtp_timestamp = 5; + + // required - Synchronization source of this packet's RTP stream. + optional fixed32 ssrc = 6; + + // TODO(terelius/dinor): Add CSRCs. Field number 7 reserved for this purpose. + + // required - The size (in bytes) of the media payload, not including + // RTP header or padding. The packet size is the sum of payload, header and + // padding. + optional uint32 payload_size = 8; + + // required - The size (in bytes) of the RTP header. + optional uint32 header_size = 9; + + // required - The size (in bytes) of the padding. + optional uint32 padding_size = 10; + + // optional - required if the batch contains delta encoded events. + optional uint32 number_of_deltas = 11; + + // Field numbers 12-14 reserved for future use. + + // Optional header extensions. + optional uint32 transport_sequence_number = 15; + optional int32 transmission_time_offset = 16; + optional uint32 absolute_send_time = 17; + optional uint32 video_rotation = 18; + // `audio_level` and `voice_activity` are always used in conjunction. + optional uint32 audio_level = 19; + optional bool voice_activity = 20; + // Encodes all DD information in the batch, not just the base event. + optional DependencyDescriptorsWireInfo dependency_descriptor = 21; + // TODO(terelius): Add other header extensions like playout delay? + + // Delta encodings. + optional bytes timestamp_ms_deltas = 101; + optional bytes marker_deltas = 102; + optional bytes payload_type_deltas = 103; + optional bytes sequence_number_deltas = 104; + optional bytes rtp_timestamp_deltas = 105; + // Field number 107 reserved for CSRC. + optional bytes ssrc_deltas = 106; + optional bytes payload_size_deltas = 108; + optional bytes header_size_deltas = 109; + optional bytes padding_size_deltas = 110; + // Field number 111-114 reserved for future use. + optional bytes transport_sequence_number_deltas = 115; + optional bytes transmission_time_offset_deltas = 116; + optional bytes absolute_send_time_deltas = 117; + optional bytes video_rotation_deltas = 118; + // `audio_level` and `voice_activity` are always used in conjunction. + optional bytes audio_level_deltas = 119; + optional bytes voice_activity_deltas = 120; +} + +message OutgoingRtpPackets { + // required + optional int64 timestamp_ms = 1; + + // required - RTP marker bit, used to label boundaries between video frames. + optional bool marker = 2; + + // required - RTP payload type. + optional uint32 payload_type = 3; + + // required - RTP sequence number. + optional uint32 sequence_number = 4; + + // required - RTP monotonic clock timestamp (not actual time). + optional fixed32 rtp_timestamp = 5; + + // required - Synchronization source of this packet's RTP stream. + optional fixed32 ssrc = 6; + + // TODO(terelius/dinor): Add CSRCs. Field number 7 reserved for this purpose. + + // required - The size (in bytes) of the media payload, not including + // RTP header or padding. The packet size is the sum of payload, header and + // padding. + optional uint32 payload_size = 8; + + // required - The size (in bytes) of the RTP header. + optional uint32 header_size = 9; + + // required - The size (in bytes) of the padding. + optional uint32 padding_size = 10; + + // optional - required if the batch contains delta encoded events. + optional uint32 number_of_deltas = 11; + + // Field numbers 12-14 reserved for future use. + + // Optional header extensions. + optional uint32 transport_sequence_number = 15; + optional int32 transmission_time_offset = 16; + optional uint32 absolute_send_time = 17; + optional uint32 video_rotation = 18; + // `audio_level` and `voice_activity` are always used in conjunction. + optional uint32 audio_level = 19; + optional bool voice_activity = 20; + // TODO(terelius): Add other header extensions like playout delay? + // Encodes all DD information in the batch, not just the base event. + optional DependencyDescriptorsWireInfo dependency_descriptor = 21; + + // Delta encodings. + optional bytes timestamp_ms_deltas = 101; + optional bytes marker_deltas = 102; + optional bytes payload_type_deltas = 103; + optional bytes sequence_number_deltas = 104; + optional bytes rtp_timestamp_deltas = 105; + optional bytes ssrc_deltas = 106; + // Field number 107 reserved for CSRC. + optional bytes payload_size_deltas = 108; + optional bytes header_size_deltas = 109; + optional bytes padding_size_deltas = 110; + // Field number 111-114 reserved for future use. + optional bytes transport_sequence_number_deltas = 115; + optional bytes transmission_time_offset_deltas = 116; + optional bytes absolute_send_time_deltas = 117; + optional bytes video_rotation_deltas = 118; + // `audio_level` and `voice_activity` are always used in conjunction. + optional bytes audio_level_deltas = 119; + optional bytes voice_activity_deltas = 120; +} + +message IncomingRtcpPackets { + // required + optional int64 timestamp_ms = 1; + + // required - The whole packet including both payload and header. + optional bytes raw_packet = 2; + // TODO(terelius): Feasible to log parsed RTCP instead? + + // optional - required if the batch contains delta encoded events. + optional uint32 number_of_deltas = 3; + + // Delta/blob encodings. + optional bytes timestamp_ms_deltas = 101; + optional bytes raw_packet_blobs = 102; +} + +message OutgoingRtcpPackets { + // required + optional int64 timestamp_ms = 1; + + // required - The whole packet including both payload and header. + optional bytes raw_packet = 2; + // TODO(terelius): Feasible to log parsed RTCP instead? + + // optional - required if the batch contains delta encoded events. + optional uint32 number_of_deltas = 3; + + // Delta/blob encodings. + optional bytes timestamp_ms_deltas = 101; + optional bytes raw_packet_blobs = 102; +} + +message AudioPlayoutEvents { + // required + optional int64 timestamp_ms = 1; + + // required - The SSRC of the audio stream associated with the playout event. + optional uint32 local_ssrc = 2; + + // optional - required if the batch contains delta encoded events. + optional uint32 number_of_deltas = 3; + + // Delta encodings. + optional bytes timestamp_ms_deltas = 101; + optional bytes local_ssrc_deltas = 102; +} + +message NetEqSetMinimumDelay { + // required + optional int64 timestamp_ms = 1; + + // required - The SSRC of the remote stream associated with the MinimumDelay + // event. + optional fixed32 remote_ssrc = 2; + + // required - minimum delay passed to SetBaseMinimumDelay. + optional int32 minimum_delay_ms = 3; + + // optional - required if the batch contains delta encoded events. + optional uint32 number_of_deltas = 4; + + // Delta encodings. + optional bytes timestamp_ms_deltas = 101; + optional bytes remote_ssrc_deltas = 102; + optional bytes minimum_delay_ms_deltas = 103; +} + +message FrameDecodedEvents { + enum Codec { + CODEC_UNKNOWN = 0; + CODEC_GENERIC = 1; + CODEC_VP8 = 2; + CODEC_VP9 = 3; + CODEC_AV1 = 4; + CODEC_H264 = 5; + CODEC_H265 = 6; + } + + // required + optional int64 timestamp_ms = 1; + + // required - The SSRC of the video stream that the frame belongs to. + optional fixed32 ssrc = 2; + + // required - The predicted render time of the frame. + optional int64 render_time_ms = 3; + + // required - The width (in pixels) of the frame. + optional int32 width = 4; + + // required - The height (in pixels) of the frame. + optional int32 height = 5; + + // required - The codec type of the frame. + optional Codec codec = 6; + + // required - The QP (quantization parameter) of the frame. Range [0,255]. + optional uint32 qp = 7; + + // optional - required if the batch contains delta encoded events. + optional uint32 number_of_deltas = 15; + + // Delta encodings. + optional bytes timestamp_ms_deltas = 101; + optional bytes ssrc_deltas = 102; + optional bytes render_time_ms_deltas = 103; + optional bytes width_deltas = 104; + optional bytes height_deltas = 105; + optional bytes codec_deltas = 106; + optional bytes qp_deltas = 107; +} + +message BeginLogEvent { + // required + optional int64 timestamp_ms = 1; + + // required + optional uint32 version = 2; + + // required + optional int64 utc_time_ms = 3; +} + +message EndLogEvent { + // required + optional int64 timestamp_ms = 1; +} + +message LossBasedBweUpdates { + // required + optional int64 timestamp_ms = 1; + + // TODO(terelius): Update log interface to unsigned. + // required - Bandwidth estimate (in bps) after the update. + optional uint32 bitrate_bps = 2; + + // required - Fraction of lost packets since last receiver report + // computed as floor( 256 * (#lost_packets / #total_packets) ). + // The possible values range from 0 to 255. + optional uint32 fraction_loss = 3; + + // TODO(terelius): Is this really needed? Remove or make optional? + // TODO(terelius): Update log interface to unsigned. + // required - Total number of packets that the BWE update is based on. + optional uint32 total_packets = 4; + + // optional - required if the batch contains delta encoded events. + optional uint32 number_of_deltas = 5; + + // Delta encodings. + optional bytes timestamp_ms_deltas = 101; + optional bytes bitrate_bps_deltas = 102; + optional bytes fraction_loss_deltas = 103; + optional bytes total_packets_deltas = 104; +} + +message DelayBasedBweUpdates { + // required + optional int64 timestamp_ms = 1; + + // required - Bandwidth estimate (in bps) after the update. + optional uint32 bitrate_bps = 2; + + enum DetectorState { + BWE_UNKNOWN_STATE = 0; + BWE_NORMAL = 1; + BWE_UNDERUSING = 2; + BWE_OVERUSING = 3; + } + optional DetectorState detector_state = 3; + + // optional - required if the batch contains delta encoded events. + optional uint32 number_of_deltas = 4; + + // Delta encodings. + optional bytes timestamp_ms_deltas = 101; + optional bytes bitrate_bps_deltas = 102; + optional bytes detector_state_deltas = 103; +} + +// Maps RTP header extension names to numerical IDs. +message RtpHeaderExtensionConfig { + // Optional IDs for the header extensions. Each ID is a 4-bit number that is + // only set if that extension is configured. + // TODO: Can we skip audio level? + optional int32 transmission_time_offset_id = 1; + optional int32 absolute_send_time_id = 2; + optional int32 transport_sequence_number_id = 3; + optional int32 video_rotation_id = 4; + optional int32 audio_level_id = 5; + optional int32 dependency_descriptor_id = 6; + // TODO(terelius): Add other header extensions like playout delay? +} + +message VideoRecvStreamConfig { + // required + optional int64 timestamp_ms = 1; + + // required - Synchronization source (stream identifier) to be received. + optional uint32 remote_ssrc = 2; + + // required - Sender SSRC used for sending RTCP (such as receiver reports). + optional uint32 local_ssrc = 3; + + // optional - required if RTX is configured. SSRC for the RTX stream. + optional uint32 rtx_ssrc = 4; + + // IDs for the header extension we care about. Only required if there are + // header extensions configured. + optional RtpHeaderExtensionConfig header_extensions = 5; + + // TODO(terelius): Do we need codec-payload mapping? If so and rtx_ssrc is + // used, we also need a map between RTP payload type and RTX payload type. +} + +message VideoSendStreamConfig { + // required + optional int64 timestamp_ms = 1; + + // required - Synchronization source (stream identifier) for outgoing stream. + // When using simulcast, a separate config should be logged for each stream. + optional uint32 ssrc = 2; + + // optional - required if RTX is configured. SSRC for the RTX stream. + optional uint32 rtx_ssrc = 3; + + // IDs for the header extension we care about. Only required if there are + // header extensions configured. + optional RtpHeaderExtensionConfig header_extensions = 4; + + // TODO(terelius): Do we need codec-payload mapping? If so and rtx_ssrc is + // used, we also need a map between RTP payload type and RTX payload type. +} + +message AudioRecvStreamConfig { + // required + optional int64 timestamp_ms = 1; + + // required - Synchronization source (stream identifier) to be received. + optional uint32 remote_ssrc = 2; + + // required - Sender SSRC used for sending RTCP (such as receiver reports). + optional uint32 local_ssrc = 3; + + // Field number 4 reserved for RTX SSRC. + + // IDs for the header extension we care about. Only required if there are + // header extensions configured. + optional RtpHeaderExtensionConfig header_extensions = 5; + + // TODO(terelius): Do we need codec-payload mapping? If so and rtx_ssrc is + // used, we also need a map between RTP payload type and RTX payload type. +} + +message AudioSendStreamConfig { + // required + optional int64 timestamp_ms = 1; + + // required - Synchronization source (stream identifier) for outgoing stream. + optional uint32 ssrc = 2; + + // Field number 3 reserved for RTX SSRC. + + // IDs for the header extension we care about. Only required if there are + // header extensions configured. + optional RtpHeaderExtensionConfig header_extensions = 4; + + // TODO(terelius): Do we need codec-payload mapping? If so and rtx_ssrc is + // used, we also need a map between RTP payload type and RTX payload type. +} + +message AudioNetworkAdaptations { + // required + optional int64 timestamp_ms = 1; + + // Bit rate that the audio encoder is operating at. + // TODO(terelius): Signed vs unsigned? + optional int32 bitrate_bps = 2; + + // Frame length that each encoded audio packet consists of. + // TODO(terelius): Signed vs unsigned? + optional int32 frame_length_ms = 3; + + // Packet loss fraction that the encoder's forward error correction (FEC) is + // optimized for. + // Instead of encoding a float, we encode a value between 0 and 16383, which + // if divided by 16383, will give a value close to the original float. + // The value 16383 (2^14 - 1) was chosen so that it would give good precision + // on the one hand, and would be encodable with two bytes in varint form + // on the other hand. + optional uint32 uplink_packet_loss_fraction = 4; + + // Whether forward error correction (FEC) is turned on or off. + optional bool enable_fec = 5; + + // Whether discontinuous transmission (DTX) is turned on or off. + optional bool enable_dtx = 6; + + // Number of audio channels that each encoded packet consists of. + optional uint32 num_channels = 7; + + // optional - required if the batch contains delta encoded events. + optional uint32 number_of_deltas = 8; + + // Delta encodings. + optional bytes timestamp_ms_deltas = 101; + optional bytes bitrate_bps_deltas = 102; + optional bytes frame_length_ms_deltas = 103; + optional bytes uplink_packet_loss_fraction_deltas = 104; + optional bytes enable_fec_deltas = 105; + optional bytes enable_dtx_deltas = 106; + optional bytes num_channels_deltas = 107; +} + +message BweProbeCluster { + // required + optional int64 timestamp_ms = 1; + + // required - The id of this probe cluster. + optional uint32 id = 2; + + // required - The bitrate in bps that this probe cluster is meant to probe. + optional uint32 bitrate_bps = 3; + + // required - The minimum number of packets used to probe the given bitrate. + optional uint32 min_packets = 4; + + // required - The minimum number of bytes used to probe the given bitrate. + optional uint32 min_bytes = 5; +} + +message BweProbeResultSuccess { + // required + optional int64 timestamp_ms = 1; + + // required - The id of this probe cluster. + optional uint32 id = 2; + + // required - The resulting bitrate in bps. + optional uint32 bitrate_bps = 3; +} + +message BweProbeResultFailure { + // required + optional int64 timestamp_ms = 1; + + // required - The id of this probe cluster. + optional uint32 id = 2; + + enum FailureReason { + UNKNOWN = 0; + INVALID_SEND_RECEIVE_INTERVAL = 1; + INVALID_SEND_RECEIVE_RATIO = 2; + TIMEOUT = 3; + } + + // required + optional FailureReason failure = 3; +} + +message AlrState { + // required + optional int64 timestamp_ms = 1; + + // required - True if the send rate is application limited. + optional bool in_alr = 2; +} + +message IceCandidatePairConfig { + enum IceCandidatePairConfigType { + UNKNOWN_CONFIG_TYPE = 0; + ADDED = 1; + UPDATED = 2; + DESTROYED = 3; + SELECTED = 4; + } + + enum IceCandidateType { + UNKNOWN_CANDIDATE_TYPE = 0; + LOCAL = 1; + STUN = 2; + PRFLX = 3; + RELAY = 4; + } + + enum Protocol { + UNKNOWN_PROTOCOL = 0; + UDP = 1; + TCP = 2; + SSLTCP = 3; + TLS = 4; + } + + enum AddressFamily { + UNKNOWN_ADDRESS_FAMILY = 0; + IPV4 = 1; + IPV6 = 2; + } + + enum NetworkType { + UNKNOWN_NETWORK_TYPE = 0; + ETHERNET = 1; + WIFI = 2; + CELLULAR = 3; + VPN = 4; + LOOPBACK = 5; + } + + // required + optional int64 timestamp_ms = 1; + + // required + optional IceCandidatePairConfigType config_type = 2; + + // required + optional uint32 candidate_pair_id = 3; + + // required + optional IceCandidateType local_candidate_type = 4; + + // required + optional Protocol local_relay_protocol = 5; + + // required + optional NetworkType local_network_type = 6; + + // required + optional AddressFamily local_address_family = 7; + + // required + optional IceCandidateType remote_candidate_type = 8; + + // required + optional AddressFamily remote_address_family = 9; + + // required + optional Protocol candidate_pair_protocol = 10; +} + +message IceCandidatePairEvent { + enum IceCandidatePairEventType { + UNKNOWN_CHECK_TYPE = 0; + CHECK_SENT = 1; + CHECK_RECEIVED = 2; + CHECK_RESPONSE_SENT = 3; + CHECK_RESPONSE_RECEIVED = 4; + } + + // required + optional int64 timestamp_ms = 1; + + // required + optional IceCandidatePairEventType event_type = 2; + + // required + optional uint32 candidate_pair_id = 3; + + // required + optional uint32 transaction_id = 4; +} + +message DtlsTransportStateEvent { + enum DtlsTransportState { + UNKNOWN_DTLS_TRANSPORT_STATE = 0; + DTLS_TRANSPORT_NEW = 1; + DTLS_TRANSPORT_CONNECTING = 2; + DTLS_TRANSPORT_CONNECTED = 3; + DTLS_TRANSPORT_CLOSED = 4; + DTLS_TRANSPORT_FAILED = 5; + } + + // required + optional int64 timestamp_ms = 1; + + // required + optional DtlsTransportState dtls_transport_state = 2; +} + +message DtlsWritableState { + // required + optional int64 timestamp_ms = 1; + + // required + optional bool writable = 2; +} + +message RouteChange { + // required + optional int64 timestamp_ms = 1; + // required - True if the route is ready for sending packets. + optional bool connected = 2; + // required - The per packet data overhead for this route. + optional uint32 overhead = 3; +} + +message RemoteEstimates { + // required + optional int64 timestamp_ms = 1; + // optional - value used as a safe measure of available capacity. + optional uint32 link_capacity_lower_kbps = 2; + // optional - value used as limit for increasing bitrate. + optional uint32 link_capacity_upper_kbps = 3; + + // optional - required if the batch contains delta encoded events. + optional uint32 number_of_deltas = 4; + + // Delta encodings. + optional bytes timestamp_ms_deltas = 101; + optional bytes link_capacity_lower_kbps_deltas = 102; + optional bytes link_capacity_upper_kbps_deltas = 103; +} diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2_proto_include.h b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2_proto_include.h new file mode 100644 index 0000000000..3e43103897 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2_proto_include.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG2_PROTO_INCLUDE_H_ +#define LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG2_PROTO_INCLUDE_H_ + +#include "rtc_base/ignore_wundef.h" + +// *.pb.h files are generated at build-time by the protobuf compiler. +RTC_PUSH_IGNORING_WUNDEF() +#ifdef WEBRTC_ANDROID_PLATFORM_BUILD +#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log2.pb.h" +#else +#include "logging/rtc_event_log/rtc_event_log2.pb.h" +#endif +RTC_POP_IGNORING_WUNDEF() + +#endif // LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG2_PROTO_INCLUDE_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2rtp_dump.cc b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2rtp_dump.cc new file mode 100644 index 0000000000..a0514259aa --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log2rtp_dump.cc @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2015 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 <stdint.h> +#include <string.h> + +#include <iostream> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "absl/flags/usage.h" +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "api/rtp_headers.h" +#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "logging/rtc_event_log/rtc_event_processor.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet.h" +#include "rtc_base/checks.h" +#include "test/rtp_file_reader.h" +#include "test/rtp_file_writer.h" + +ABSL_FLAG( + bool, + audio, + true, + "Use --noaudio to exclude audio packets from the converted RTPdump file."); +ABSL_FLAG( + bool, + video, + true, + "Use --novideo to exclude video packets from the converted RTPdump file."); +ABSL_FLAG( + bool, + data, + true, + "Use --nodata to exclude data packets from the converted RTPdump file."); +ABSL_FLAG( + bool, + rtp, + true, + "Use --nortp to exclude RTP packets from the converted RTPdump file."); +ABSL_FLAG( + bool, + rtcp, + true, + "Use --nortcp to exclude RTCP packets from the converted RTPdump file."); +ABSL_FLAG(std::string, + ssrc, + "", + "Store only packets with this SSRC (decimal or hex, the latter " + "starting with 0x)."); + +namespace { + +using MediaType = webrtc::ParsedRtcEventLog::MediaType; + +// Parses the input string for a valid SSRC. If a valid SSRC is found, it is +// written to the output variable `ssrc`, and true is returned. Otherwise, +// false is returned. +// The empty string must be validated as true, because it is the default value +// of the command-line flag. In this case, no value is written to the output +// variable. +absl::optional<uint32_t> ParseSsrc(absl::string_view str) { + // If the input string starts with 0x or 0X it indicates a hexadecimal number. + uint32_t ssrc; + auto read_mode = std::dec; + if (str.size() > 2 && + (str.substr(0, 2) == "0x" || str.substr(0, 2) == "0X")) { + read_mode = std::hex; + str = str.substr(2); + } + std::stringstream ss(std::string{str}); + ss >> read_mode >> ssrc; + if (str.empty() || (!ss.fail() && ss.eof())) + return ssrc; + return absl::nullopt; +} + +bool ShouldSkipStream(MediaType media_type, + uint32_t ssrc, + absl::optional<uint32_t> ssrc_filter) { + if (!absl::GetFlag(FLAGS_audio) && media_type == MediaType::AUDIO) + return true; + if (!absl::GetFlag(FLAGS_video) && media_type == MediaType::VIDEO) + return true; + if (!absl::GetFlag(FLAGS_data) && media_type == MediaType::DATA) + return true; + if (ssrc_filter.has_value() && ssrc != *ssrc_filter) + return true; + return false; +} + +// Convert a LoggedRtpPacketIncoming to a test::RtpPacket. Header extension IDs +// are allocated according to the provided extension map. This might not match +// the extension map used in the actual call. +void ConvertRtpPacket( + const webrtc::LoggedRtpPacketIncoming& incoming, + const webrtc::RtpHeaderExtensionMap& default_extension_map, + webrtc::test::RtpPacket* packet) { + webrtc::RtpPacket reconstructed_packet(&default_extension_map); + + reconstructed_packet.SetMarker(incoming.rtp.header.markerBit); + reconstructed_packet.SetPayloadType(incoming.rtp.header.payloadType); + reconstructed_packet.SetSequenceNumber(incoming.rtp.header.sequenceNumber); + reconstructed_packet.SetTimestamp(incoming.rtp.header.timestamp); + reconstructed_packet.SetSsrc(incoming.rtp.header.ssrc); + if (incoming.rtp.header.numCSRCs > 0) { + reconstructed_packet.SetCsrcs(rtc::ArrayView<const uint32_t>( + incoming.rtp.header.arrOfCSRCs, incoming.rtp.header.numCSRCs)); + } + + // Set extensions. + if (incoming.rtp.header.extension.hasTransmissionTimeOffset) + reconstructed_packet.SetExtension<webrtc::TransmissionOffset>( + incoming.rtp.header.extension.transmissionTimeOffset); + if (incoming.rtp.header.extension.hasAbsoluteSendTime) + reconstructed_packet.SetExtension<webrtc::AbsoluteSendTime>( + incoming.rtp.header.extension.absoluteSendTime); + if (incoming.rtp.header.extension.hasTransportSequenceNumber) + reconstructed_packet.SetExtension<webrtc::TransportSequenceNumber>( + incoming.rtp.header.extension.transportSequenceNumber); + if (incoming.rtp.header.extension.hasAudioLevel) + reconstructed_packet.SetExtension<webrtc::AudioLevel>( + incoming.rtp.header.extension.voiceActivity, + incoming.rtp.header.extension.audioLevel); + if (incoming.rtp.header.extension.hasVideoRotation) + reconstructed_packet.SetExtension<webrtc::VideoOrientation>( + incoming.rtp.header.extension.videoRotation); + if (incoming.rtp.header.extension.hasVideoContentType) + reconstructed_packet.SetExtension<webrtc::VideoContentTypeExtension>( + incoming.rtp.header.extension.videoContentType); + if (incoming.rtp.header.extension.has_video_timing) + reconstructed_packet.SetExtension<webrtc::VideoTimingExtension>( + incoming.rtp.header.extension.video_timing); + + RTC_DCHECK_EQ(reconstructed_packet.size(), incoming.rtp.header_length); + RTC_DCHECK_EQ(reconstructed_packet.headers_size(), + incoming.rtp.header_length); + memcpy(packet->data, reconstructed_packet.data(), + reconstructed_packet.headers_size()); + packet->length = reconstructed_packet.headers_size(); + packet->original_length = incoming.rtp.total_length; + packet->time_ms = incoming.log_time_ms(); + // Set padding bit. + if (incoming.rtp.header.paddingLength > 0) + packet->data[0] = packet->data[0] | 0x20; +} + +} // namespace + +// This utility will convert a stored event log to the rtpdump format. +int main(int argc, char* argv[]) { + absl::SetProgramUsageMessage( + "Tool for converting an RtcEventLog file to an " + "RTP dump file.\n" + "Example usage:\n" + "./rtc_event_log2rtp_dump input.rel output.rtp\n"); + std::vector<char*> args = absl::ParseCommandLine(argc, argv); + if (args.size() != 3) { + std::cout << absl::ProgramUsageMessage(); + return 1; + } + + std::string input_file = args[1]; + std::string output_file = args[2]; + + absl::optional<uint32_t> ssrc_filter; + if (!absl::GetFlag(FLAGS_ssrc).empty()) { + ssrc_filter = ParseSsrc(absl::GetFlag(FLAGS_ssrc)); + RTC_CHECK(ssrc_filter.has_value()) << "Failed to read SSRC filter flag."; + } + + webrtc::ParsedRtcEventLog parsed_stream; + auto status = parsed_stream.ParseFile(input_file); + if (!status.ok()) { + std::cerr << "Failed to parse event log " << input_file << ": " + << status.message() << std::endl; + return -1; + } + + std::unique_ptr<webrtc::test::RtpFileWriter> rtp_writer( + webrtc::test::RtpFileWriter::Create( + webrtc::test::RtpFileWriter::FileFormat::kRtpDump, output_file)); + + if (!rtp_writer) { + std::cerr << "Error while opening output file: " << output_file + << std::endl; + return -1; + } + + int rtp_counter = 0, rtcp_counter = 0; + bool header_only = false; + + webrtc::RtpHeaderExtensionMap default_extension_map = + webrtc::ParsedRtcEventLog::GetDefaultHeaderExtensionMap(); + auto handle_rtp = [&default_extension_map, &rtp_writer, &rtp_counter]( + const webrtc::LoggedRtpPacketIncoming& incoming) { + webrtc::test::RtpPacket packet; + ConvertRtpPacket(incoming, default_extension_map, &packet); + + rtp_writer->WritePacket(&packet); + rtp_counter++; + }; + + auto handle_rtcp = [&rtp_writer, &rtcp_counter]( + const webrtc::LoggedRtcpPacketIncoming& incoming) { + webrtc::test::RtpPacket packet; + memcpy(packet.data, incoming.rtcp.raw_data.data(), + incoming.rtcp.raw_data.size()); + packet.length = incoming.rtcp.raw_data.size(); + // For RTCP packets the original_length should be set to 0 in the + // RTPdump format. + packet.original_length = 0; + packet.time_ms = incoming.log_time_ms(); + + rtp_writer->WritePacket(&packet); + rtcp_counter++; + }; + + webrtc::RtcEventProcessor event_processor; + for (const auto& stream : parsed_stream.incoming_rtp_packets_by_ssrc()) { + MediaType media_type = + parsed_stream.GetMediaType(stream.ssrc, webrtc::kIncomingPacket); + if (ShouldSkipStream(media_type, stream.ssrc, ssrc_filter)) + continue; + event_processor.AddEvents(stream.incoming_packets, handle_rtp); + } + // Note that `packet_ssrc` is the sender SSRC. An RTCP message may contain + // report blocks for many streams, thus several SSRCs and they don't + // necessarily have to be of the same media type. We therefore don't + // support filtering of RTCP based on SSRC and media type. + event_processor.AddEvents(parsed_stream.incoming_rtcp_packets(), handle_rtcp); + + event_processor.ProcessEventsInOrder(); + + std::cout << "Wrote " << rtp_counter << (header_only ? " header-only" : "") + << " RTP packets and " << rtcp_counter + << " RTCP packets to the " + "output file." + << std::endl; + return 0; +} diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl.cc b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl.cc new file mode 100644 index 0000000000..f2b3f22d6a --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl.cc @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2015 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 "logging/rtc_event_log/rtc_event_log_impl.h" + +#include <functional> +#include <limits> +#include <memory> +#include <utility> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/task_queue/task_queue_base.h" +#include "api/units/time_delta.h" +#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 "rtc_base/checks.h" +#include "rtc_base/event.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +std::unique_ptr<RtcEventLogEncoder> RtcEventLogImpl::CreateEncoder( + RtcEventLog::EncodingType type) { + switch (type) { + case RtcEventLog::EncodingType::Legacy: + RTC_DLOG(LS_INFO) << "Creating legacy encoder for RTC event log."; + return std::make_unique<RtcEventLogEncoderLegacy>(); + case RtcEventLog::EncodingType::NewFormat: + RTC_DLOG(LS_INFO) << "Creating new format encoder for RTC event log."; + return std::make_unique<RtcEventLogEncoderNewFormat>(); + default: + RTC_LOG(LS_ERROR) << "Unknown RtcEventLog encoder type (" << int(type) + << ")"; + RTC_DCHECK_NOTREACHED(); + return std::unique_ptr<RtcEventLogEncoder>(nullptr); + } +} + +RtcEventLogImpl::RtcEventLogImpl(std::unique_ptr<RtcEventLogEncoder> encoder, + TaskQueueFactory* task_queue_factory, + size_t max_events_in_history, + size_t max_config_events_in_history) + : max_events_in_history_(max_events_in_history), + max_config_events_in_history_(max_config_events_in_history), + event_encoder_(std::move(encoder)), + last_output_ms_(rtc::TimeMillis()), + task_queue_( + std::make_unique<rtc::TaskQueue>(task_queue_factory->CreateTaskQueue( + "rtc_event_log", + TaskQueueFactory::Priority::NORMAL))) {} + +RtcEventLogImpl::~RtcEventLogImpl() { + // If we're logging to the output, this will stop that. Blocking function. + mutex_.Lock(); + bool started = logging_state_started_; + mutex_.Unlock(); + + if (started) { + logging_state_checker_.Detach(); + StopLogging(); + } + + // We want to block on any executing task by invoking ~TaskQueue() before + // we set unique_ptr's internal pointer to null. + rtc::TaskQueue* tq = task_queue_.get(); + delete tq; + task_queue_.release(); +} + +bool RtcEventLogImpl::StartLogging(std::unique_ptr<RtcEventLogOutput> output, + int64_t output_period_ms) { + RTC_DCHECK(output); + RTC_DCHECK(output_period_ms == kImmediateOutput || output_period_ms > 0); + + if (!output->IsActive()) { + // TODO(eladalon): We may want to remove the IsActive method. Otherwise + // we probably want to be consistent and terminate any existing output. + return false; + } + + const int64_t timestamp_us = rtc::TimeMillis() * 1000; + const int64_t utc_time_us = rtc::TimeUTCMillis() * 1000; + RTC_LOG(LS_INFO) << "Starting WebRTC event log. (Timestamp, UTC) = (" + << timestamp_us << ", " << utc_time_us << ")."; + + RTC_DCHECK_RUN_ON(&logging_state_checker_); + MutexLock lock(&mutex_); + logging_state_started_ = true; + immediately_output_mode_ = (output_period_ms == kImmediateOutput); + need_schedule_output_ = (output_period_ms != kImmediateOutput); + + // Binding to `this` is safe because `this` outlives the `task_queue_`. + task_queue_->PostTask([this, output_period_ms, timestamp_us, utc_time_us, + output = std::move(output), + histories = ExtractRecentHistories()]() mutable { + RTC_DCHECK_RUN_ON(task_queue_.get()); + RTC_DCHECK(output); + RTC_DCHECK(output->IsActive()); + output_period_ms_ = output_period_ms; + event_output_ = std::move(output); + + WriteToOutput(event_encoder_->EncodeLogStart(timestamp_us, utc_time_us)); + // Load all configs of previous sessions. + if (!all_config_history_.empty()) { + EventDeque& history = histories.config_history; + history.insert(history.begin(), + std::make_move_iterator(all_config_history_.begin()), + std::make_move_iterator(all_config_history_.end())); + all_config_history_.clear(); + + if (history.size() > max_config_events_in_history_) { + RTC_LOG(LS_WARNING) + << "Dropping config events: " << history.size() + << " exceeds maximum " << max_config_events_in_history_; + history.erase(history.begin(), history.begin() + history.size() - + max_config_events_in_history_); + } + } + LogEventsToOutput(std::move(histories)); + }); + + return true; +} + +void RtcEventLogImpl::StopLogging() { + RTC_DLOG(LS_INFO) << "Stopping WebRTC event log."; + // TODO(bugs.webrtc.org/14449): Do not block current thread waiting on the + // task queue. It might work for now, for current callers, but disallows + // caller to share threads with the `task_queue_`. + rtc::Event output_stopped; + StopLogging([&output_stopped]() { output_stopped.Set(); }); + output_stopped.Wait(rtc::Event::kForever); + + RTC_DLOG(LS_INFO) << "WebRTC event log successfully stopped."; +} + +void RtcEventLogImpl::StopLogging(std::function<void()> callback) { + RTC_DCHECK_RUN_ON(&logging_state_checker_); + MutexLock lock(&mutex_); + logging_state_started_ = false; + task_queue_->PostTask( + [this, callback, histories = ExtractRecentHistories()]() mutable { + RTC_DCHECK_RUN_ON(task_queue_.get()); + if (event_output_) { + RTC_DCHECK(event_output_->IsActive()); + LogEventsToOutput(std::move(histories)); + } + StopLoggingInternal(); + callback(); + }); +} + +RtcEventLogImpl::EventHistories RtcEventLogImpl::ExtractRecentHistories() { + EventHistories histories; + std::swap(histories, recent_); + return histories; +} + +void RtcEventLogImpl::Log(std::unique_ptr<RtcEvent> event) { + RTC_CHECK(event); + MutexLock lock(&mutex_); + + LogToMemory(std::move(event)); + if (logging_state_started_) { + if (ShouldOutputImmediately()) { + // Binding to `this` is safe because `this` outlives the `task_queue_`. + task_queue_->PostTask( + [this, histories = ExtractRecentHistories()]() mutable { + RTC_DCHECK_RUN_ON(task_queue_.get()); + if (event_output_) { + RTC_DCHECK(event_output_->IsActive()); + LogEventsToOutput(std::move(histories)); + } + }); + } else if (need_schedule_output_) { + need_schedule_output_ = false; + // Binding to `this` is safe because `this` outlives the `task_queue_`. + task_queue_->PostTask([this]() mutable { + RTC_DCHECK_RUN_ON(task_queue_.get()); + if (event_output_) { + RTC_DCHECK(event_output_->IsActive()); + ScheduleOutput(); + } + }); + } + } +} + +bool RtcEventLogImpl::ShouldOutputImmediately() { + if (recent_.history.size() >= max_events_in_history_) { + // We have to emergency drain the buffer. We can't wait for the scheduled + // output task because there might be other event incoming before that. + return true; + } + + return immediately_output_mode_; +} + +void RtcEventLogImpl::ScheduleOutput() { + RTC_DCHECK(output_period_ms_ != kImmediateOutput); + // Binding to `this` is safe because `this` outlives the `task_queue_`. + auto output_task = [this]() { + RTC_DCHECK_RUN_ON(task_queue_.get()); + // Allow scheduled output if the `event_output_` is valid. + if (event_output_) { + RTC_DCHECK(event_output_->IsActive()); + mutex_.Lock(); + RTC_DCHECK(!need_schedule_output_); + // Let the next `Log()` to schedule output. + need_schedule_output_ = true; + EventHistories histories = ExtractRecentHistories(); + mutex_.Unlock(); + LogEventsToOutput(std::move(histories)); + } + }; + const int64_t now_ms = rtc::TimeMillis(); + const int64_t time_since_output_ms = now_ms - last_output_ms_; + const int32_t delay = rtc::SafeClamp(output_period_ms_ - time_since_output_ms, + 0, output_period_ms_); + task_queue_->PostDelayedTask(std::move(output_task), + TimeDelta::Millis(delay)); +} + +void RtcEventLogImpl::LogToMemory(std::unique_ptr<RtcEvent> event) { + EventDeque& container = + event->IsConfigEvent() ? recent_.config_history : recent_.history; + const size_t container_max_size = event->IsConfigEvent() + ? max_config_events_in_history_ + : max_events_in_history_; + + // Shouldn't lose events if started. + if (container.size() >= container_max_size && !logging_state_started_) { + container.pop_front(); + } + container.push_back(std::move(event)); +} + +void RtcEventLogImpl::LogEventsToOutput(EventHistories histories) { + last_output_ms_ = rtc::TimeMillis(); + + // Serialize the stream configurations. + std::string encoded_configs = event_encoder_->EncodeBatch( + histories.config_history.begin(), histories.config_history.end()); + + // Serialize the events in the event queue. Note that the write may fail, + // for example if we are writing to a file and have reached the maximum limit. + // We don't get any feedback if this happens, so we still remove the events + // from the event log history. This is normally not a problem, but if another + // log is started immediately after the first one becomes full, then one + // cannot rely on the second log to contain everything that isn't in the first + // log; one batch of events might be missing. + std::string encoded_history = event_encoder_->EncodeBatch( + histories.history.begin(), histories.history.end()); + + WriteConfigsAndHistoryToOutput(encoded_configs, encoded_history); + + // Unlike other events, the configs are retained. If we stop/start logging + // again, these configs are used to interpret other events. + all_config_history_.insert( + all_config_history_.end(), + std::make_move_iterator(histories.config_history.begin()), + std::make_move_iterator(histories.config_history.end())); + if (all_config_history_.size() > max_config_events_in_history_) { + RTC_LOG(LS_WARNING) << "Dropping config events: " + << all_config_history_.size() << " exceeds maximum " + << max_config_events_in_history_; + all_config_history_.erase(all_config_history_.begin(), + all_config_history_.begin() + + all_config_history_.size() - + max_config_events_in_history_); + } +} + +void RtcEventLogImpl::WriteConfigsAndHistoryToOutput( + absl::string_view encoded_configs, + absl::string_view encoded_history) { + // This function is used to merge the strings instead of calling the output + // object twice with small strings. The function also avoids copying any + // strings in the typical case where there are no config events. + if (encoded_configs.empty()) { + WriteToOutput(encoded_history); // Typical case. + } else if (encoded_history.empty()) { + WriteToOutput(encoded_configs); // Very unusual case. + } else { + std::string s; + s.reserve(encoded_configs.size() + encoded_history.size()); + s.append(encoded_configs.data(), encoded_configs.size()); + s.append(encoded_history.data(), encoded_history.size()); + WriteToOutput(s); + } +} + +void RtcEventLogImpl::StopOutput() { + event_output_.reset(); +} + +void RtcEventLogImpl::StopLoggingInternal() { + if (event_output_) { + RTC_DCHECK(event_output_->IsActive()); + const int64_t timestamp_us = rtc::TimeMillis() * 1000; + event_output_->Write(event_encoder_->EncodeLogEnd(timestamp_us)); + } + StopOutput(); +} + +void RtcEventLogImpl::WriteToOutput(absl::string_view output_string) { + if (event_output_) { + RTC_DCHECK(event_output_->IsActive()); + if (!event_output_->Write(output_string)) { + RTC_LOG(LS_ERROR) << "Failed to write RTC event to output."; + // The first failure closes the output. + RTC_DCHECK(!event_output_->IsActive()); + StopOutput(); // Clean-up. + } + } +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl.h b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl.h new file mode 100644 index 0000000000..3187a7fe87 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_IMPL_H_ +#define LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_IMPL_H_ + +#include <cstddef> +#include <cstdint> +#include <deque> +#include <memory> +#include <string> + +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "api/rtc_event_log_output.h" +#include "api/sequence_checker.h" +#include "api/task_queue/task_queue_factory.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/task_queue.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class RtcEventLogImpl final : public RtcEventLog { + public: + // The max number of events that the history can store. + static constexpr size_t kMaxEventsInHistory = 10000; + // The max number of events that the config history can store. + // The config-history is supposed to be unbounded, but needs to have some + // bound to prevent an attack via unreasonable memory use. + static constexpr size_t kMaxEventsInConfigHistory = 1000; + + RtcEventLogImpl( + std::unique_ptr<RtcEventLogEncoder> encoder, + TaskQueueFactory* task_queue_factory, + size_t max_events_in_history = kMaxEventsInHistory, + size_t max_config_events_in_history = kMaxEventsInConfigHistory); + RtcEventLogImpl(const RtcEventLogImpl&) = delete; + RtcEventLogImpl& operator=(const RtcEventLogImpl&) = delete; + + ~RtcEventLogImpl() override; + + static std::unique_ptr<RtcEventLogEncoder> CreateEncoder( + EncodingType encoding_type); + + // TODO(eladalon): We should change these name to reflect that what we're + // actually starting/stopping is the output of the log, not the log itself. + bool StartLogging(std::unique_ptr<RtcEventLogOutput> output, + int64_t output_period_ms) override; + void StopLogging() override; + void StopLogging(std::function<void()> callback) override; + + // Records event into `recent_` on current thread, and schedules the output on + // task queue if the buffers are full or `output_period_ms_` is expired. + void Log(std::unique_ptr<RtcEvent> event) override; + + private: + using EventDeque = std::deque<std::unique_ptr<RtcEvent>>; + + struct EventHistories { + EventDeque config_history; + EventDeque history; + }; + + // Helper to extract and clear `recent_`. + EventHistories ExtractRecentHistories() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void LogToMemory(std::unique_ptr<RtcEvent> event) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void LogEventsToOutput(EventHistories histories) RTC_RUN_ON(task_queue_); + + void StopOutput() RTC_RUN_ON(task_queue_); + + void WriteConfigsAndHistoryToOutput(absl::string_view encoded_configs, + absl::string_view encoded_history) + RTC_RUN_ON(task_queue_); + void WriteToOutput(absl::string_view output_string) RTC_RUN_ON(task_queue_); + + void StopLoggingInternal() RTC_RUN_ON(task_queue_); + + bool ShouldOutputImmediately() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void ScheduleOutput() RTC_RUN_ON(task_queue_); + + // Max size of event history. + const size_t max_events_in_history_; + + // Max size of config event history. + const size_t max_config_events_in_history_; + + // History containing all past configuration events. + EventDeque all_config_history_ RTC_GUARDED_BY(task_queue_); + + // `config_history` containing the most recent configuration events. + // `history` containing the most recent (non-configuration) events (~10s). + EventHistories recent_ RTC_GUARDED_BY(mutex_); + + std::unique_ptr<RtcEventLogEncoder> event_encoder_ + RTC_GUARDED_BY(task_queue_); + std::unique_ptr<RtcEventLogOutput> event_output_ RTC_GUARDED_BY(task_queue_); + + int64_t output_period_ms_ RTC_GUARDED_BY(task_queue_); + int64_t last_output_ms_ RTC_GUARDED_BY(task_queue_); + + RTC_NO_UNIQUE_ADDRESS SequenceChecker logging_state_checker_; + bool logging_state_started_ RTC_GUARDED_BY(mutex_) = false; + bool immediately_output_mode_ RTC_GUARDED_BY(mutex_) = false; + bool need_schedule_output_ RTC_GUARDED_BY(mutex_) = false; + + // Since we are posting tasks bound to `this`, it is critical that the event + // log and its members outlive `task_queue_`. Keep the `task_queue_` + // last to ensure it destructs first, or else tasks living on the queue might + // access other members after they've been torn down. + std::unique_ptr<rtc::TaskQueue> task_queue_; + + Mutex mutex_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_IMPL_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl_unittest.cc new file mode 100644 index 0000000000..1e8799bdd6 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl_unittest.cc @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2023 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 "logging/rtc_event_log/rtc_event_log_impl.h" + +#include <utility> +#include <vector> + +#include "test/gmock.h" +#include "test/gtest.h" +#include "test/time_controller/simulated_time_controller.h" + +namespace webrtc { +namespace { + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Mock; +using ::testing::Property; +using ::testing::Ref; +using ::testing::Return; +using ::testing::StrEq; + +class MockEventEncoder : public RtcEventLogEncoder { + public: + MOCK_METHOD(std::string, + EncodeLogStart, + (int64_t /*timestamp_us*/, int64_t /*utc_time_us*/), + (override)); + MOCK_METHOD(std::string, + EncodeLogEnd, + (int64_t /*timestamp_us*/), + (override)); + MOCK_METHOD(std::string, OnEncode, (const RtcEvent&), ()); + std::string EncodeBatch( + std::deque<std::unique_ptr<RtcEvent>>::const_iterator a, + std::deque<std::unique_ptr<RtcEvent>>::const_iterator b) override { + std::string result; + while (a != b) { + result += OnEncode(**a); + ++a; + } + return result; + } +}; + +class FakeOutput : public RtcEventLogOutput { + public: + explicit FakeOutput(std::string& written_data) + : written_data_(written_data) {} + bool IsActive() const { return is_active_; } + bool Write(absl::string_view data) override { + RTC_DCHECK(is_active_); + if (fails_write_) { + is_active_ = false; + fails_write_ = false; + return false; + } else { + written_data_.append(std::string(data)); + return true; + } + } + void Flush() override {} + + void FailsNextWrite() { fails_write_ = true; } + + private: + std::string& written_data_; + bool is_active_ = true; + bool fails_write_ = false; +}; + +class FakeEvent : public RtcEvent { + public: + Type GetType() const override { return RtcEvent::Type::FakeEvent; } + bool IsConfigEvent() const override { return false; } +}; + +class FakeConfigEvent : public RtcEvent { + public: + Type GetType() const override { return RtcEvent::Type::FakeEvent; } + bool IsConfigEvent() const override { return true; } +}; + +class RtcEventLogImplTest : public ::testing::Test { + public: + static constexpr size_t kMaxEventsInHistory = 2; + static constexpr size_t kMaxEventsInConfigHistory = 5; + static constexpr TimeDelta kOutputPeriod = TimeDelta::Seconds(2); + static constexpr Timestamp kStartTime = Timestamp::Seconds(1); + + GlobalSimulatedTimeController time_controller_{kStartTime}; + std::string written_data_; // This must be destroyed after the event_log_. + std::unique_ptr<MockEventEncoder> encoder_ = + std::make_unique<MockEventEncoder>(); + MockEventEncoder* encoder_ptr_ = encoder_.get(); + std::unique_ptr<FakeOutput> output_ = + std::make_unique<FakeOutput>(written_data_); + FakeOutput* output_ptr_ = output_.get(); + RtcEventLogImpl event_log_{std::move(encoder_), + time_controller_.GetTaskQueueFactory(), + kMaxEventsInHistory, kMaxEventsInConfigHistory}; +}; + +TEST_F(RtcEventLogImplTest, WritesHeaderAndEventsAndTrailer) { + EXPECT_CALL(*encoder_ptr_, EncodeLogStart(kStartTime.us(), kStartTime.us())) + .WillOnce(Return("start")); + EXPECT_CALL(*encoder_ptr_, EncodeLogEnd(kStartTime.us())) + .WillOnce(Return("stop")); + EXPECT_CALL(*encoder_ptr_, OnEncode(Property(&RtcEvent::IsConfigEvent, true))) + .WillOnce(Return("config")); + EXPECT_CALL(*encoder_ptr_, + OnEncode(Property(&RtcEvent::IsConfigEvent, false))) + .WillOnce(Return("event")); + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + event_log_.Log(std::make_unique<FakeEvent>()); + event_log_.Log(std::make_unique<FakeConfigEvent>()); + event_log_.StopLogging(); + Mock::VerifyAndClearExpectations(encoder_ptr_); + EXPECT_TRUE(written_data_ == "startconfigeventstop" || + written_data_ == "starteventconfigstop"); +} + +TEST_F(RtcEventLogImplTest, KeepsMostRecentEventsOnStart) { + auto e2 = std::make_unique<FakeEvent>(); + RtcEvent* e2_ptr = e2.get(); + auto e3 = std::make_unique<FakeEvent>(); + RtcEvent* e3_ptr = e3.get(); + event_log_.Log(std::make_unique<FakeEvent>()); + event_log_.Log(std::move(e2)); + event_log_.Log(std::move(e3)); + InSequence s; + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*e2_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*e3_ptr))); + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + time_controller_.AdvanceTime(TimeDelta::Zero()); + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +TEST_F(RtcEventLogImplTest, KeepsConfigEventsOnStart) { + // The config-history is supposed to be unbounded and never overflow. + auto c1 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c1_ptr = c1.get(); + auto c2 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c2_ptr = c2.get(); + auto c3 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c3_ptr = c3.get(); + event_log_.Log(std::move(c1)); + event_log_.Log(std::move(c2)); + event_log_.Log(std::move(c3)); + InSequence s; + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c1_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c2_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c3_ptr))); + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + time_controller_.AdvanceTime(TimeDelta::Zero()); + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +TEST_F(RtcEventLogImplTest, EncodesEventsOnHistoryFullPostponesLastEncode) { + auto e1 = std::make_unique<FakeEvent>(); + RtcEvent* e1_ptr = e1.get(); + auto e2 = std::make_unique<FakeEvent>(); + RtcEvent* e2_ptr = e2.get(); + auto e3 = std::make_unique<FakeEvent>(); + RtcEvent* e3_ptr = e3.get(); + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + time_controller_.AdvanceTime(TimeDelta::Zero()); + event_log_.Log(std::move(e1)); + event_log_.Log(std::move(e2)); + // Overflows and causes immediate output, and eventual output after the output + // period has passed. + event_log_.Log(std::move(e3)); + InSequence s; + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*e1_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*e2_ptr))); + time_controller_.AdvanceTime(TimeDelta::Zero()); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*e3_ptr))); + time_controller_.AdvanceTime(kOutputPeriod); + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +TEST_F(RtcEventLogImplTest, RewritesAllConfigEventsOnlyOnRestart) { + auto c1 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c1_ptr = c1.get(); + auto c2 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c2_ptr = c2.get(); + auto c3 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c3_ptr = c3.get(); + auto c4 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c4_ptr = c4.get(); + auto c5 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c5_ptr = c5.get(); + // Keep the config event before start. + event_log_.Log(std::move(c1)); + // Start logging. + event_log_.StartLogging(std::make_unique<FakeOutput>(written_data_), + kOutputPeriod.ms()); + event_log_.Log(std::move(c2)); + event_log_.Log(std::move(c3)); + event_log_.StopLogging(); + time_controller_.AdvanceTime(TimeDelta::Zero()); + // Restart logging. + event_log_.StartLogging(std::make_unique<FakeOutput>(written_data_), + kOutputPeriod.ms()); + event_log_.Log(std::move(c4)); + event_log_.Log(std::move(c5)); + event_log_.Log(std::make_unique<FakeEvent>()); + event_log_.StopLogging(); + time_controller_.AdvanceTime(TimeDelta::Zero()); + InSequence s; + // Rewrite all config events in sequence on next restart. + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c1_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c2_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c3_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c4_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c5_ptr))); + EXPECT_CALL(*encoder_ptr_, + OnEncode(Property(&RtcEvent::IsConfigEvent, false))) + .Times(0); + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + time_controller_.AdvanceTime(TimeDelta::Zero()); + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +TEST_F(RtcEventLogImplTest, SchedulesWriteAfterOutputDurationPassed) { + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + event_log_.Log(std::make_unique<FakeConfigEvent>()); + event_log_.Log(std::make_unique<FakeEvent>()); + EXPECT_CALL(*encoder_ptr_, + OnEncode(Property(&RtcEvent::IsConfigEvent, true))); + EXPECT_CALL(*encoder_ptr_, + OnEncode(Property(&RtcEvent::IsConfigEvent, false))); + time_controller_.AdvanceTime(kOutputPeriod); + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +TEST_F(RtcEventLogImplTest, DoNotDropEventsIfHistoryFullAfterStarted) { + constexpr size_t kNumberOfEvents = 10 * kMaxEventsInHistory; + + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + event_log_.Log(std::make_unique<FakeConfigEvent>()); + for (size_t i = 0; i < kNumberOfEvents; i++) { + event_log_.Log(std::make_unique<FakeEvent>()); + } + EXPECT_CALL(*encoder_ptr_, + OnEncode(Property(&RtcEvent::IsConfigEvent, true))); + EXPECT_CALL(*encoder_ptr_, + OnEncode(Property(&RtcEvent::IsConfigEvent, false))) + .Times(kNumberOfEvents); + time_controller_.AdvanceTime(kOutputPeriod); + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +TEST_F(RtcEventLogImplTest, StopOutputOnWriteFailure) { + constexpr size_t kNumberOfEvents = 10; + constexpr size_t kFailsWriteOnEventsCount = 5; + + size_t number_of_encoded_events = 0; + EXPECT_CALL(*encoder_ptr_, OnEncode(_)) + .WillRepeatedly(Invoke([this, &number_of_encoded_events]() { + ++number_of_encoded_events; + if (number_of_encoded_events == kFailsWriteOnEventsCount) { + output_ptr_->FailsNextWrite(); + } + return std::string(); + })); + + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + + // Fails `RtcEventLogOutput` on the last event. + for (size_t i = 0; i < kFailsWriteOnEventsCount; i++) { + event_log_.Log(std::make_unique<FakeEvent>()); + } + time_controller_.AdvanceTime(kOutputPeriod); + // Expect that the remainder events are not encoded. + for (size_t i = kFailsWriteOnEventsCount; i < kNumberOfEvents; i++) { + event_log_.Log(std::make_unique<FakeEvent>()); + } + time_controller_.AdvanceTime(kOutputPeriod); + event_log_.StopLogging(); + + EXPECT_EQ(number_of_encoded_events, kFailsWriteOnEventsCount); + + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +} // namespace +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.cc b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.cc new file mode 100644 index 0000000000..37bb70a69b --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.cc @@ -0,0 +1,3618 @@ +/* + * Copyright (c) 2016 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 "logging/rtc_event_log/rtc_event_log_parser.h" + +#include <stdint.h> +#include <string.h> + +#include <algorithm> +#include <limits> +#include <map> +#include <utility> + +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/network_state_predictor.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "api/rtp_headers.h" +#include "api/rtp_parameters.h" +#include "logging/rtc_event_log/dependency_descriptor_encoder_decoder.h" +#include "logging/rtc_event_log/encoder/blob_encoding.h" +#include "logging/rtc_event_log/encoder/delta_encoding.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" +#include "logging/rtc_event_log/encoder/var_int.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/rtc_event_processor.h" +#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" +#include "modules/rtp_rtcp/include/rtp_cvo.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/byte_io.h" +#include "modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "rtc_base/checks.h" +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/numerics/sequence_number_unwrapper.h" +#include "rtc_base/protobuf_utils.h" +#include "rtc_base/system/file_wrapper.h" + +using webrtc_event_logging::ToSigned; +using webrtc_event_logging::ToUnsigned; + +namespace webrtc { + +namespace { +constexpr int64_t kMaxLogSize = 250000000; + +constexpr size_t kIpv4Overhead = 20; +constexpr size_t kIpv6Overhead = 40; +constexpr size_t kUdpOverhead = 8; +constexpr size_t kSrtpOverhead = 10; +constexpr size_t kStunOverhead = 4; +constexpr uint16_t kDefaultOverhead = + kUdpOverhead + kSrtpOverhead + kIpv4Overhead; + +constexpr char kIncompleteLogError[] = + "Could not parse the entire log. Only the beginning will be used."; + +struct MediaStreamInfo { + MediaStreamInfo() = default; + MediaStreamInfo(LoggedMediaType media_type, bool rtx) + : media_type(media_type), rtx(rtx) {} + LoggedMediaType media_type = LoggedMediaType::kUnknown; + bool rtx = false; + SeqNumUnwrapper<uint32_t> unwrap_capture_ticks; +}; + +template <typename Iterable> +void AddRecvStreamInfos(std::map<uint32_t, MediaStreamInfo>* streams, + const Iterable configs, + LoggedMediaType media_type) { + for (auto& conf : configs) { + streams->insert({conf.config.remote_ssrc, {media_type, false}}); + if (conf.config.rtx_ssrc != 0) + streams->insert({conf.config.rtx_ssrc, {media_type, true}}); + } +} +template <typename Iterable> +void AddSendStreamInfos(std::map<uint32_t, MediaStreamInfo>* streams, + const Iterable configs, + LoggedMediaType media_type) { + for (auto& conf : configs) { + streams->insert({conf.config.local_ssrc, {media_type, false}}); + if (conf.config.rtx_ssrc != 0) + streams->insert({conf.config.rtx_ssrc, {media_type, true}}); + } +} +struct OverheadChangeEvent { + Timestamp timestamp; + uint16_t overhead; +}; +std::vector<OverheadChangeEvent> GetOverheadChangingEvents( + const std::vector<InferredRouteChangeEvent>& route_changes, + PacketDirection direction) { + std::vector<OverheadChangeEvent> overheads; + for (auto& event : route_changes) { + uint16_t new_overhead = direction == PacketDirection::kIncomingPacket + ? event.return_overhead + : event.send_overhead; + if (overheads.empty() || new_overhead != overheads.back().overhead) { + overheads.push_back({event.log_time, new_overhead}); + } + } + return overheads; +} + +bool IdenticalRtcpContents(const std::vector<uint8_t>& last_rtcp, + absl::string_view new_rtcp) { + if (last_rtcp.size() != new_rtcp.size()) + return false; + return memcmp(last_rtcp.data(), new_rtcp.data(), new_rtcp.size()) == 0; +} + +// Conversion functions for legacy wire format. +RtcpMode GetRuntimeRtcpMode(rtclog::VideoReceiveConfig::RtcpMode rtcp_mode) { + switch (rtcp_mode) { + case rtclog::VideoReceiveConfig::RTCP_COMPOUND: + return RtcpMode::kCompound; + case rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE: + return RtcpMode::kReducedSize; + } + RTC_DCHECK_NOTREACHED(); + return RtcpMode::kOff; +} + +BandwidthUsage GetRuntimeDetectorState( + rtclog::DelayBasedBweUpdate::DetectorState detector_state) { + switch (detector_state) { + case rtclog::DelayBasedBweUpdate::BWE_NORMAL: + return BandwidthUsage::kBwNormal; + case rtclog::DelayBasedBweUpdate::BWE_UNDERUSING: + return BandwidthUsage::kBwUnderusing; + case rtclog::DelayBasedBweUpdate::BWE_OVERUSING: + return BandwidthUsage::kBwOverusing; + } + RTC_DCHECK_NOTREACHED(); + return BandwidthUsage::kBwNormal; +} + +IceCandidatePairConfigType GetRuntimeIceCandidatePairConfigType( + rtclog::IceCandidatePairConfig::IceCandidatePairConfigType type) { + switch (type) { + case rtclog::IceCandidatePairConfig::ADDED: + return IceCandidatePairConfigType::kAdded; + case rtclog::IceCandidatePairConfig::UPDATED: + return IceCandidatePairConfigType::kUpdated; + case rtclog::IceCandidatePairConfig::DESTROYED: + return IceCandidatePairConfigType::kDestroyed; + case rtclog::IceCandidatePairConfig::SELECTED: + return IceCandidatePairConfigType::kSelected; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairConfigType::kAdded; +} + +IceCandidateType GetRuntimeIceCandidateType( + rtclog::IceCandidatePairConfig::IceCandidateType type) { + switch (type) { + case rtclog::IceCandidatePairConfig::LOCAL: + return IceCandidateType::kLocal; + case rtclog::IceCandidatePairConfig::STUN: + return IceCandidateType::kStun; + case rtclog::IceCandidatePairConfig::PRFLX: + return IceCandidateType::kPrflx; + case rtclog::IceCandidatePairConfig::RELAY: + return IceCandidateType::kRelay; + case rtclog::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE: + return IceCandidateType::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidateType::kUnknown; +} + +IceCandidatePairProtocol GetRuntimeIceCandidatePairProtocol( + rtclog::IceCandidatePairConfig::Protocol protocol) { + switch (protocol) { + case rtclog::IceCandidatePairConfig::UDP: + return IceCandidatePairProtocol::kUdp; + case rtclog::IceCandidatePairConfig::TCP: + return IceCandidatePairProtocol::kTcp; + case rtclog::IceCandidatePairConfig::SSLTCP: + return IceCandidatePairProtocol::kSsltcp; + case rtclog::IceCandidatePairConfig::TLS: + return IceCandidatePairProtocol::kTls; + case rtclog::IceCandidatePairConfig::UNKNOWN_PROTOCOL: + return IceCandidatePairProtocol::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairProtocol::kUnknown; +} + +IceCandidatePairAddressFamily GetRuntimeIceCandidatePairAddressFamily( + rtclog::IceCandidatePairConfig::AddressFamily address_family) { + switch (address_family) { + case rtclog::IceCandidatePairConfig::IPV4: + return IceCandidatePairAddressFamily::kIpv4; + case rtclog::IceCandidatePairConfig::IPV6: + return IceCandidatePairAddressFamily::kIpv6; + case rtclog::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY: + return IceCandidatePairAddressFamily::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairAddressFamily::kUnknown; +} + +IceCandidateNetworkType GetRuntimeIceCandidateNetworkType( + rtclog::IceCandidatePairConfig::NetworkType network_type) { + switch (network_type) { + case rtclog::IceCandidatePairConfig::ETHERNET: + return IceCandidateNetworkType::kEthernet; + case rtclog::IceCandidatePairConfig::LOOPBACK: + return IceCandidateNetworkType::kLoopback; + case rtclog::IceCandidatePairConfig::WIFI: + return IceCandidateNetworkType::kWifi; + case rtclog::IceCandidatePairConfig::VPN: + return IceCandidateNetworkType::kVpn; + case rtclog::IceCandidatePairConfig::CELLULAR: + return IceCandidateNetworkType::kCellular; + case rtclog::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE: + return IceCandidateNetworkType::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidateNetworkType::kUnknown; +} + +IceCandidatePairEventType GetRuntimeIceCandidatePairEventType( + rtclog::IceCandidatePairEvent::IceCandidatePairEventType type) { + switch (type) { + case rtclog::IceCandidatePairEvent::CHECK_SENT: + return IceCandidatePairEventType::kCheckSent; + case rtclog::IceCandidatePairEvent::CHECK_RECEIVED: + return IceCandidatePairEventType::kCheckReceived; + case rtclog::IceCandidatePairEvent::CHECK_RESPONSE_SENT: + return IceCandidatePairEventType::kCheckResponseSent; + case rtclog::IceCandidatePairEvent::CHECK_RESPONSE_RECEIVED: + return IceCandidatePairEventType::kCheckResponseReceived; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairEventType::kCheckSent; +} + +VideoCodecType GetRuntimeCodecType(rtclog2::FrameDecodedEvents::Codec codec) { + switch (codec) { + case rtclog2::FrameDecodedEvents::CODEC_GENERIC: + return VideoCodecType::kVideoCodecGeneric; + case rtclog2::FrameDecodedEvents::CODEC_VP8: + return VideoCodecType::kVideoCodecVP8; + case rtclog2::FrameDecodedEvents::CODEC_VP9: + return VideoCodecType::kVideoCodecVP9; + case rtclog2::FrameDecodedEvents::CODEC_AV1: + return VideoCodecType::kVideoCodecAV1; + case rtclog2::FrameDecodedEvents::CODEC_H264: + return VideoCodecType::kVideoCodecH264; + case rtclog2::FrameDecodedEvents::CODEC_H265: + return VideoCodecType::kVideoCodecH265; + case rtclog2::FrameDecodedEvents::CODEC_UNKNOWN: + RTC_LOG(LS_ERROR) << "Unknown codec type. Assuming " + "VideoCodecType::kVideoCodecMultiplex"; + return VideoCodecType::kVideoCodecMultiplex; + } + RTC_DCHECK_NOTREACHED(); + return VideoCodecType::kVideoCodecMultiplex; +} + +ParsedRtcEventLog::ParseStatus GetHeaderExtensions( + std::vector<RtpExtension>* header_extensions, + const RepeatedPtrField<rtclog::RtpHeaderExtension>& + proto_header_extensions) { + header_extensions->clear(); + for (auto& p : proto_header_extensions) { + RTC_PARSE_CHECK_OR_RETURN(p.has_name()); + RTC_PARSE_CHECK_OR_RETURN(p.has_id()); + const std::string& name = p.name(); + int id = p.id(); + header_extensions->push_back(RtpExtension(name, id)); + } + return ParsedRtcEventLog::ParseStatus::Success(); +} + +template <typename ProtoType, typename LoggedType> +ParsedRtcEventLog::ParseStatus StoreRtpPackets( + const ProtoType& proto, + std::map<uint32_t, std::vector<LoggedType>>* rtp_packets_map) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_marker()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_payload_type()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_sequence_number()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_rtp_timestamp()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_ssrc()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_payload_size()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_header_size()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_padding_size()); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + const size_t total_packets = number_of_deltas + 1; + + std::vector<std::vector<uint8_t>> dependency_descriptor_wire_format( + total_packets); + if (proto.has_dependency_descriptor()) { + auto status_or_decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode( + proto.dependency_descriptor(), total_packets); + if (!status_or_decoded.ok()) { + return status_or_decoded.status(); + } + dependency_descriptor_wire_format = status_or_decoded.value(); + } + + // Base event + { + RTPHeader header; + header.markerBit = rtc::checked_cast<bool>(proto.marker()); + header.payloadType = rtc::checked_cast<uint8_t>(proto.payload_type()); + header.sequenceNumber = + rtc::checked_cast<uint16_t>(proto.sequence_number()); + header.timestamp = rtc::checked_cast<uint32_t>(proto.rtp_timestamp()); + header.ssrc = rtc::checked_cast<uint32_t>(proto.ssrc()); + header.numCSRCs = 0; // TODO(terelius): Implement CSRC. + header.paddingLength = rtc::checked_cast<size_t>(proto.padding_size()); + header.headerLength = rtc::checked_cast<size_t>(proto.header_size()); + // TODO(terelius): Should we implement payload_type_frequency? + if (proto.has_transport_sequence_number()) { + header.extension.hasTransportSequenceNumber = true; + header.extension.transportSequenceNumber = + rtc::checked_cast<uint16_t>(proto.transport_sequence_number()); + } + if (proto.has_transmission_time_offset()) { + header.extension.hasTransmissionTimeOffset = true; + header.extension.transmissionTimeOffset = + rtc::checked_cast<int32_t>(proto.transmission_time_offset()); + } + if (proto.has_absolute_send_time()) { + header.extension.hasAbsoluteSendTime = true; + header.extension.absoluteSendTime = + rtc::checked_cast<uint32_t>(proto.absolute_send_time()); + } + if (proto.has_video_rotation()) { + header.extension.hasVideoRotation = true; + header.extension.videoRotation = ConvertCVOByteToVideoRotation( + rtc::checked_cast<uint8_t>(proto.video_rotation())); + } + if (proto.has_audio_level()) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_voice_activity()); + header.extension.hasAudioLevel = true; + header.extension.voiceActivity = + rtc::checked_cast<bool>(proto.voice_activity()); + const uint8_t audio_level = + rtc::checked_cast<uint8_t>(proto.audio_level()); + RTC_PARSE_CHECK_OR_RETURN_LE(audio_level, 0x7Fu); + header.extension.audioLevel = audio_level; + } else { + RTC_PARSE_CHECK_OR_RETURN(!proto.has_voice_activity()); + } + LoggedType logged_packet( + Timestamp::Millis(proto.timestamp_ms()), header, proto.header_size(), + proto.payload_size() + header.headerLength + header.paddingLength); + if (!dependency_descriptor_wire_format[0].empty()) { + logged_packet.rtp.dependency_descriptor_wire_format = + dependency_descriptor_wire_format[0]; + } + (*rtp_packets_map)[header.ssrc].push_back(std::move(logged_packet)); + } + + if (number_of_deltas == 0) { + return ParsedRtcEventLog::ParseStatus::Success(); + } + + // timestamp_ms (event) + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // marker (RTP base) + std::vector<absl::optional<uint64_t>> marker_values = + DecodeDeltas(proto.marker_deltas(), proto.marker(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(marker_values.size(), number_of_deltas); + + // payload_type (RTP base) + std::vector<absl::optional<uint64_t>> payload_type_values = DecodeDeltas( + proto.payload_type_deltas(), proto.payload_type(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(payload_type_values.size(), number_of_deltas); + + // sequence_number (RTP base) + std::vector<absl::optional<uint64_t>> sequence_number_values = + DecodeDeltas(proto.sequence_number_deltas(), proto.sequence_number(), + number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(sequence_number_values.size(), number_of_deltas); + + // rtp_timestamp (RTP base) + std::vector<absl::optional<uint64_t>> rtp_timestamp_values = DecodeDeltas( + proto.rtp_timestamp_deltas(), proto.rtp_timestamp(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(rtp_timestamp_values.size(), number_of_deltas); + + // ssrc (RTP base) + std::vector<absl::optional<uint64_t>> ssrc_values = + DecodeDeltas(proto.ssrc_deltas(), proto.ssrc(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(ssrc_values.size(), number_of_deltas); + + // payload_size (RTP base) + std::vector<absl::optional<uint64_t>> payload_size_values = DecodeDeltas( + proto.payload_size_deltas(), proto.payload_size(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(payload_size_values.size(), number_of_deltas); + + // header_size (RTP base) + std::vector<absl::optional<uint64_t>> header_size_values = DecodeDeltas( + proto.header_size_deltas(), proto.header_size(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(header_size_values.size(), number_of_deltas); + + // padding_size (RTP base) + std::vector<absl::optional<uint64_t>> padding_size_values = DecodeDeltas( + proto.padding_size_deltas(), proto.padding_size(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(padding_size_values.size(), number_of_deltas); + + // transport_sequence_number (RTP extension) + std::vector<absl::optional<uint64_t>> transport_sequence_number_values; + { + const absl::optional<uint64_t> base_transport_sequence_number = + proto.has_transport_sequence_number() + ? proto.transport_sequence_number() + : absl::optional<uint64_t>(); + transport_sequence_number_values = + DecodeDeltas(proto.transport_sequence_number_deltas(), + base_transport_sequence_number, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(transport_sequence_number_values.size(), + number_of_deltas); + } + + // transmission_time_offset (RTP extension) + std::vector<absl::optional<uint64_t>> transmission_time_offset_values; + { + const absl::optional<uint64_t> unsigned_base_transmission_time_offset = + proto.has_transmission_time_offset() + ? ToUnsigned(proto.transmission_time_offset()) + : absl::optional<uint64_t>(); + transmission_time_offset_values = + DecodeDeltas(proto.transmission_time_offset_deltas(), + unsigned_base_transmission_time_offset, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(transmission_time_offset_values.size(), + number_of_deltas); + } + + // absolute_send_time (RTP extension) + std::vector<absl::optional<uint64_t>> absolute_send_time_values; + { + const absl::optional<uint64_t> base_absolute_send_time = + proto.has_absolute_send_time() ? proto.absolute_send_time() + : absl::optional<uint64_t>(); + absolute_send_time_values = + DecodeDeltas(proto.absolute_send_time_deltas(), base_absolute_send_time, + number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(absolute_send_time_values.size(), + number_of_deltas); + } + + // video_rotation (RTP extension) + std::vector<absl::optional<uint64_t>> video_rotation_values; + { + const absl::optional<uint64_t> base_video_rotation = + proto.has_video_rotation() ? proto.video_rotation() + : absl::optional<uint64_t>(); + video_rotation_values = DecodeDeltas(proto.video_rotation_deltas(), + base_video_rotation, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(video_rotation_values.size(), + number_of_deltas); + } + + // audio_level (RTP extension) + std::vector<absl::optional<uint64_t>> audio_level_values; + { + const absl::optional<uint64_t> base_audio_level = + proto.has_audio_level() ? proto.audio_level() + : absl::optional<uint64_t>(); + audio_level_values = DecodeDeltas(proto.audio_level_deltas(), + base_audio_level, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(audio_level_values.size(), number_of_deltas); + } + + // voice_activity (RTP extension) + std::vector<absl::optional<uint64_t>> voice_activity_values; + { + const absl::optional<uint64_t> base_voice_activity = + proto.has_voice_activity() ? proto.voice_activity() + : absl::optional<uint64_t>(); + voice_activity_values = DecodeDeltas(proto.voice_activity_deltas(), + base_voice_activity, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(voice_activity_values.size(), + number_of_deltas); + } + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(marker_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(payload_type_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(sequence_number_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(rtp_timestamp_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(ssrc_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(payload_size_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(header_size_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(padding_size_values[i].has_value()); + + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + RTPHeader header; + header.markerBit = rtc::checked_cast<bool>(*marker_values[i]); + header.payloadType = rtc::checked_cast<uint8_t>(*payload_type_values[i]); + header.sequenceNumber = + rtc::checked_cast<uint16_t>(*sequence_number_values[i]); + header.timestamp = rtc::checked_cast<uint32_t>(*rtp_timestamp_values[i]); + header.ssrc = rtc::checked_cast<uint32_t>(*ssrc_values[i]); + header.numCSRCs = 0; // TODO(terelius): Implement CSRC. + header.paddingLength = rtc::checked_cast<size_t>(*padding_size_values[i]); + header.headerLength = rtc::checked_cast<size_t>(*header_size_values[i]); + // TODO(terelius): Should we implement payload_type_frequency? + if (transport_sequence_number_values.size() > i && + transport_sequence_number_values[i].has_value()) { + header.extension.hasTransportSequenceNumber = true; + header.extension.transportSequenceNumber = rtc::checked_cast<uint16_t>( + transport_sequence_number_values[i].value()); + } + if (transmission_time_offset_values.size() > i && + transmission_time_offset_values[i].has_value()) { + header.extension.hasTransmissionTimeOffset = true; + int32_t transmission_time_offset; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(transmission_time_offset_values[i].value(), + &transmission_time_offset)); + header.extension.transmissionTimeOffset = transmission_time_offset; + } + if (absolute_send_time_values.size() > i && + absolute_send_time_values[i].has_value()) { + header.extension.hasAbsoluteSendTime = true; + header.extension.absoluteSendTime = + rtc::checked_cast<uint32_t>(absolute_send_time_values[i].value()); + } + if (video_rotation_values.size() > i && + video_rotation_values[i].has_value()) { + header.extension.hasVideoRotation = true; + header.extension.videoRotation = ConvertCVOByteToVideoRotation( + rtc::checked_cast<uint8_t>(video_rotation_values[i].value())); + } + if (audio_level_values.size() > i && audio_level_values[i].has_value()) { + RTC_PARSE_CHECK_OR_RETURN(voice_activity_values.size() > i && + voice_activity_values[i].has_value()); + header.extension.hasAudioLevel = true; + header.extension.voiceActivity = + rtc::checked_cast<bool>(voice_activity_values[i].value()); + const uint8_t audio_level = + rtc::checked_cast<uint8_t>(audio_level_values[i].value()); + RTC_PARSE_CHECK_OR_RETURN_LE(audio_level, 0x7Fu); + header.extension.audioLevel = audio_level; + } else { + RTC_PARSE_CHECK_OR_RETURN(voice_activity_values.size() <= i || + !voice_activity_values[i].has_value()); + } + LoggedType logged_packet(Timestamp::Millis(timestamp_ms), header, + header.headerLength, + payload_size_values[i].value() + + header.headerLength + header.paddingLength); + if (!dependency_descriptor_wire_format[i + 1].empty()) { + logged_packet.rtp.dependency_descriptor_wire_format = + dependency_descriptor_wire_format[i + 1]; + } + (*rtp_packets_map)[header.ssrc].push_back(std::move(logged_packet)); + } + return ParsedRtcEventLog::ParseStatus::Success(); +} + +template <typename ProtoType, typename LoggedType> +ParsedRtcEventLog::ParseStatus StoreRtcpPackets( + const ProtoType& proto, + std::vector<LoggedType>* rtcp_packets, + bool remove_duplicates) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_raw_packet()); + + // TODO(terelius): Incoming RTCP may be delivered once for audio and once + // for video. As a work around, we remove the duplicated packets since they + // cause problems when analyzing the log or feeding it into the transport + // feedback adapter. + if (!remove_duplicates || rtcp_packets->empty() || + !IdenticalRtcpContents(rtcp_packets->back().rtcp.raw_data, + proto.raw_packet())) { + // Base event + rtcp_packets->emplace_back(Timestamp::Millis(proto.timestamp_ms()), + proto.raw_packet()); + } + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParsedRtcEventLog::ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // raw_packet + RTC_PARSE_CHECK_OR_RETURN(proto.has_raw_packet_blobs()); + std::vector<absl::string_view> raw_packet_values = + DecodeBlobs(proto.raw_packet_blobs(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(raw_packet_values.size(), number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + // TODO(terelius): Incoming RTCP may be delivered once for audio and once + // for video. As a work around, we remove the duplicated packets since they + // cause problems when analyzing the log or feeding it into the transport + // feedback adapter. + if (remove_duplicates && !rtcp_packets->empty() && + IdenticalRtcpContents(rtcp_packets->back().rtcp.raw_data, + raw_packet_values[i])) { + continue; + } + std::string data(raw_packet_values[i]); + rtcp_packets->emplace_back(Timestamp::Millis(timestamp_ms), data); + } + return ParsedRtcEventLog::ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus StoreRtcpBlocks( + int64_t timestamp_us, + const uint8_t* packet_begin, + const uint8_t* packet_end, + std::vector<LoggedRtcpPacketSenderReport>* sr_list, + std::vector<LoggedRtcpPacketReceiverReport>* rr_list, + std::vector<LoggedRtcpPacketExtendedReports>* xr_list, + std::vector<LoggedRtcpPacketRemb>* remb_list, + std::vector<LoggedRtcpPacketNack>* nack_list, + std::vector<LoggedRtcpPacketFir>* fir_list, + std::vector<LoggedRtcpPacketPli>* pli_list, + std::vector<LoggedRtcpPacketBye>* bye_list, + std::vector<LoggedRtcpPacketTransportFeedback>* transport_feedback_list, + std::vector<LoggedRtcpPacketLossNotification>* loss_notification_list) { + Timestamp timestamp = Timestamp::Micros(timestamp_us); + rtcp::CommonHeader header; + for (const uint8_t* block = packet_begin; block < packet_end; + block = header.NextPacket()) { + RTC_PARSE_CHECK_OR_RETURN(header.Parse(block, packet_end - block)); + if (header.type() == rtcp::TransportFeedback::kPacketType && + header.fmt() == rtcp::TransportFeedback::kFeedbackMessageType) { + LoggedRtcpPacketTransportFeedback parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.transport_feedback.Parse(header)); + transport_feedback_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::SenderReport::kPacketType) { + LoggedRtcpPacketSenderReport parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.sr.Parse(header)); + sr_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::ReceiverReport::kPacketType) { + LoggedRtcpPacketReceiverReport parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.rr.Parse(header)); + rr_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::ExtendedReports::kPacketType) { + LoggedRtcpPacketExtendedReports parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.xr.Parse(header)); + xr_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::Fir::kPacketType && + header.fmt() == rtcp::Fir::kFeedbackMessageType) { + LoggedRtcpPacketFir parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.fir.Parse(header)); + fir_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::Pli::kPacketType && + header.fmt() == rtcp::Pli::kFeedbackMessageType) { + LoggedRtcpPacketPli parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.pli.Parse(header)); + pli_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::Bye::kPacketType) { + LoggedRtcpPacketBye parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.bye.Parse(header)); + bye_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::Psfb::kPacketType && + header.fmt() == rtcp::Psfb::kAfbMessageType) { + bool type_found = false; + if (!type_found) { + LoggedRtcpPacketRemb parsed_block; + parsed_block.timestamp = timestamp; + if (parsed_block.remb.Parse(header)) { + remb_list->push_back(std::move(parsed_block)); + type_found = true; + } + } + if (!type_found) { + LoggedRtcpPacketLossNotification parsed_block; + parsed_block.timestamp = timestamp; + if (parsed_block.loss_notification.Parse(header)) { + loss_notification_list->push_back(std::move(parsed_block)); + type_found = true; + } + } + // We ignore other application-layer feedback types. + } else if (header.type() == rtcp::Nack::kPacketType && + header.fmt() == rtcp::Nack::kFeedbackMessageType) { + LoggedRtcpPacketNack parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.nack.Parse(header)); + nack_list->push_back(std::move(parsed_block)); + } + } + return ParsedRtcEventLog::ParseStatus::Success(); +} + +} // namespace + +// Conversion functions for version 2 of the wire format. +BandwidthUsage GetRuntimeDetectorState( + rtclog2::DelayBasedBweUpdates::DetectorState detector_state) { + switch (detector_state) { + case rtclog2::DelayBasedBweUpdates::BWE_NORMAL: + return BandwidthUsage::kBwNormal; + case rtclog2::DelayBasedBweUpdates::BWE_UNDERUSING: + return BandwidthUsage::kBwUnderusing; + case rtclog2::DelayBasedBweUpdates::BWE_OVERUSING: + return BandwidthUsage::kBwOverusing; + case rtclog2::DelayBasedBweUpdates::BWE_UNKNOWN_STATE: + break; + } + RTC_DCHECK_NOTREACHED(); + return BandwidthUsage::kBwNormal; +} + +ProbeFailureReason GetRuntimeProbeFailureReason( + rtclog2::BweProbeResultFailure::FailureReason failure) { + switch (failure) { + case rtclog2::BweProbeResultFailure::INVALID_SEND_RECEIVE_INTERVAL: + return ProbeFailureReason::kInvalidSendReceiveInterval; + case rtclog2::BweProbeResultFailure::INVALID_SEND_RECEIVE_RATIO: + return ProbeFailureReason::kInvalidSendReceiveRatio; + case rtclog2::BweProbeResultFailure::TIMEOUT: + return ProbeFailureReason::kTimeout; + case rtclog2::BweProbeResultFailure::UNKNOWN: + break; + } + RTC_DCHECK_NOTREACHED(); + return ProbeFailureReason::kTimeout; +} + +DtlsTransportState GetRuntimeDtlsTransportState( + rtclog2::DtlsTransportStateEvent::DtlsTransportState state) { + switch (state) { + case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_NEW: + return DtlsTransportState::kNew; + case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CONNECTING: + return DtlsTransportState::kConnecting; + case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CONNECTED: + return DtlsTransportState::kConnected; + case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CLOSED: + return DtlsTransportState::kClosed; + case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_FAILED: + return DtlsTransportState::kFailed; + case rtclog2::DtlsTransportStateEvent::UNKNOWN_DTLS_TRANSPORT_STATE: + RTC_DCHECK_NOTREACHED(); + return DtlsTransportState::kNumValues; + } + RTC_DCHECK_NOTREACHED(); + return DtlsTransportState::kNumValues; +} + +IceCandidatePairConfigType GetRuntimeIceCandidatePairConfigType( + rtclog2::IceCandidatePairConfig::IceCandidatePairConfigType type) { + switch (type) { + case rtclog2::IceCandidatePairConfig::ADDED: + return IceCandidatePairConfigType::kAdded; + case rtclog2::IceCandidatePairConfig::UPDATED: + return IceCandidatePairConfigType::kUpdated; + case rtclog2::IceCandidatePairConfig::DESTROYED: + return IceCandidatePairConfigType::kDestroyed; + case rtclog2::IceCandidatePairConfig::SELECTED: + return IceCandidatePairConfigType::kSelected; + case rtclog2::IceCandidatePairConfig::UNKNOWN_CONFIG_TYPE: + break; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairConfigType::kAdded; +} + +IceCandidateType GetRuntimeIceCandidateType( + rtclog2::IceCandidatePairConfig::IceCandidateType type) { + switch (type) { + case rtclog2::IceCandidatePairConfig::LOCAL: + return IceCandidateType::kLocal; + case rtclog2::IceCandidatePairConfig::STUN: + return IceCandidateType::kStun; + case rtclog2::IceCandidatePairConfig::PRFLX: + return IceCandidateType::kPrflx; + case rtclog2::IceCandidatePairConfig::RELAY: + return IceCandidateType::kRelay; + case rtclog2::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE: + return IceCandidateType::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidateType::kUnknown; +} + +IceCandidatePairProtocol GetRuntimeIceCandidatePairProtocol( + rtclog2::IceCandidatePairConfig::Protocol protocol) { + switch (protocol) { + case rtclog2::IceCandidatePairConfig::UDP: + return IceCandidatePairProtocol::kUdp; + case rtclog2::IceCandidatePairConfig::TCP: + return IceCandidatePairProtocol::kTcp; + case rtclog2::IceCandidatePairConfig::SSLTCP: + return IceCandidatePairProtocol::kSsltcp; + case rtclog2::IceCandidatePairConfig::TLS: + return IceCandidatePairProtocol::kTls; + case rtclog2::IceCandidatePairConfig::UNKNOWN_PROTOCOL: + return IceCandidatePairProtocol::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairProtocol::kUnknown; +} + +IceCandidatePairAddressFamily GetRuntimeIceCandidatePairAddressFamily( + rtclog2::IceCandidatePairConfig::AddressFamily address_family) { + switch (address_family) { + case rtclog2::IceCandidatePairConfig::IPV4: + return IceCandidatePairAddressFamily::kIpv4; + case rtclog2::IceCandidatePairConfig::IPV6: + return IceCandidatePairAddressFamily::kIpv6; + case rtclog2::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY: + return IceCandidatePairAddressFamily::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairAddressFamily::kUnknown; +} + +IceCandidateNetworkType GetRuntimeIceCandidateNetworkType( + rtclog2::IceCandidatePairConfig::NetworkType network_type) { + switch (network_type) { + case rtclog2::IceCandidatePairConfig::ETHERNET: + return IceCandidateNetworkType::kEthernet; + case rtclog2::IceCandidatePairConfig::LOOPBACK: + return IceCandidateNetworkType::kLoopback; + case rtclog2::IceCandidatePairConfig::WIFI: + return IceCandidateNetworkType::kWifi; + case rtclog2::IceCandidatePairConfig::VPN: + return IceCandidateNetworkType::kVpn; + case rtclog2::IceCandidatePairConfig::CELLULAR: + return IceCandidateNetworkType::kCellular; + case rtclog2::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE: + return IceCandidateNetworkType::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidateNetworkType::kUnknown; +} + +IceCandidatePairEventType GetRuntimeIceCandidatePairEventType( + rtclog2::IceCandidatePairEvent::IceCandidatePairEventType type) { + switch (type) { + case rtclog2::IceCandidatePairEvent::CHECK_SENT: + return IceCandidatePairEventType::kCheckSent; + case rtclog2::IceCandidatePairEvent::CHECK_RECEIVED: + return IceCandidatePairEventType::kCheckReceived; + case rtclog2::IceCandidatePairEvent::CHECK_RESPONSE_SENT: + return IceCandidatePairEventType::kCheckResponseSent; + case rtclog2::IceCandidatePairEvent::CHECK_RESPONSE_RECEIVED: + return IceCandidatePairEventType::kCheckResponseReceived; + case rtclog2::IceCandidatePairEvent::UNKNOWN_CHECK_TYPE: + break; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairEventType::kCheckSent; +} + +std::vector<RtpExtension> GetRuntimeRtpHeaderExtensionConfig( + const rtclog2::RtpHeaderExtensionConfig& proto_header_extensions) { + std::vector<RtpExtension> rtp_extensions; + if (proto_header_extensions.has_transmission_time_offset_id()) { + rtp_extensions.emplace_back( + RtpExtension::kTimestampOffsetUri, + proto_header_extensions.transmission_time_offset_id()); + } + if (proto_header_extensions.has_absolute_send_time_id()) { + rtp_extensions.emplace_back( + RtpExtension::kAbsSendTimeUri, + proto_header_extensions.absolute_send_time_id()); + } + if (proto_header_extensions.has_transport_sequence_number_id()) { + rtp_extensions.emplace_back( + RtpExtension::kTransportSequenceNumberUri, + proto_header_extensions.transport_sequence_number_id()); + } + if (proto_header_extensions.has_audio_level_id()) { + rtp_extensions.emplace_back(RtpExtension::kAudioLevelUri, + proto_header_extensions.audio_level_id()); + } + if (proto_header_extensions.has_video_rotation_id()) { + rtp_extensions.emplace_back(RtpExtension::kVideoRotationUri, + proto_header_extensions.video_rotation_id()); + } + if (proto_header_extensions.has_dependency_descriptor_id()) { + rtp_extensions.emplace_back( + RtpExtension::kDependencyDescriptorUri, + proto_header_extensions.dependency_descriptor_id()); + } + return rtp_extensions; +} +// End of conversion functions. + +LoggedPacketInfo::LoggedPacketInfo(const LoggedRtpPacket& rtp, + LoggedMediaType media_type, + bool rtx, + Timestamp capture_time) + : ssrc(rtp.header.ssrc), + stream_seq_no(rtp.header.sequenceNumber), + size(static_cast<uint16_t>(rtp.total_length)), + payload_size(static_cast<uint16_t>(rtp.total_length - + rtp.header.paddingLength - + rtp.header.headerLength)), + padding_size(static_cast<uint16_t>(rtp.header.paddingLength)), + payload_type(rtp.header.payloadType), + media_type(media_type), + rtx(rtx), + marker_bit(rtp.header.markerBit), + has_transport_seq_no(rtp.header.extension.hasTransportSequenceNumber), + transport_seq_no(static_cast<uint16_t>( + has_transport_seq_no ? rtp.header.extension.transportSequenceNumber + : 0)), + capture_time(capture_time), + log_packet_time(Timestamp::Micros(rtp.log_time_us())), + reported_send_time(rtp.header.extension.hasAbsoluteSendTime + ? rtp.header.extension.GetAbsoluteSendTimestamp() + : Timestamp::MinusInfinity()) {} + +LoggedPacketInfo::LoggedPacketInfo(const LoggedPacketInfo&) = default; + +LoggedPacketInfo::~LoggedPacketInfo() {} + +ParsedRtcEventLog::~ParsedRtcEventLog() = default; + +ParsedRtcEventLog::LoggedRtpStreamIncoming::LoggedRtpStreamIncoming() = default; +ParsedRtcEventLog::LoggedRtpStreamIncoming::LoggedRtpStreamIncoming( + const LoggedRtpStreamIncoming& rhs) = default; +ParsedRtcEventLog::LoggedRtpStreamIncoming::~LoggedRtpStreamIncoming() = + default; + +ParsedRtcEventLog::LoggedRtpStreamOutgoing::LoggedRtpStreamOutgoing() = default; +ParsedRtcEventLog::LoggedRtpStreamOutgoing::LoggedRtpStreamOutgoing( + const LoggedRtpStreamOutgoing& rhs) = default; +ParsedRtcEventLog::LoggedRtpStreamOutgoing::~LoggedRtpStreamOutgoing() = + default; + +ParsedRtcEventLog::LoggedRtpStreamView::LoggedRtpStreamView( + uint32_t ssrc, + const std::vector<LoggedRtpPacketIncoming>& packets) + : ssrc(ssrc), packet_view() { + for (const LoggedRtpPacketIncoming& packet : packets) { + packet_view.push_back(&(packet.rtp)); + } +} + +ParsedRtcEventLog::LoggedRtpStreamView::LoggedRtpStreamView( + uint32_t ssrc, + const std::vector<LoggedRtpPacketOutgoing>& packets) + : ssrc(ssrc), packet_view() { + for (const LoggedRtpPacketOutgoing& packet : packets) { + packet_view.push_back(&(packet.rtp)); + } +} + +ParsedRtcEventLog::LoggedRtpStreamView::LoggedRtpStreamView( + const LoggedRtpStreamView&) = default; + +// Return default values for header extensions, to use on streams without stored +// mapping data. Currently this only applies to audio streams, since the mapping +// is not stored in the event log. +// TODO(ivoc): Remove this once this mapping is stored in the event log for +// audio streams. Tracking bug: webrtc:6399 +webrtc::RtpHeaderExtensionMap +ParsedRtcEventLog::GetDefaultHeaderExtensionMap() { + // Values from before the default RTP header extension IDs were removed. + constexpr int kAudioLevelDefaultId = 1; + constexpr int kTimestampOffsetDefaultId = 2; + constexpr int kAbsSendTimeDefaultId = 3; + constexpr int kVideoRotationDefaultId = 4; + constexpr int kTransportSequenceNumberDefaultId = 5; + constexpr int kPlayoutDelayDefaultId = 6; + constexpr int kVideoContentTypeDefaultId = 7; + constexpr int kVideoTimingDefaultId = 8; + constexpr int kDependencyDescriptorDefaultId = 9; + + webrtc::RtpHeaderExtensionMap default_map(/*extmap_allow_mixed=*/true); + default_map.Register<AudioLevel>(kAudioLevelDefaultId); + default_map.Register<TransmissionOffset>(kTimestampOffsetDefaultId); + default_map.Register<AbsoluteSendTime>(kAbsSendTimeDefaultId); + default_map.Register<VideoOrientation>(kVideoRotationDefaultId); + default_map.Register<TransportSequenceNumber>( + kTransportSequenceNumberDefaultId); + default_map.Register<PlayoutDelayLimits>(kPlayoutDelayDefaultId); + default_map.Register<VideoContentTypeExtension>(kVideoContentTypeDefaultId); + default_map.Register<VideoTimingExtension>(kVideoTimingDefaultId); + default_map.Register<RtpDependencyDescriptorExtension>( + kDependencyDescriptorDefaultId); + return default_map; +} + +ParsedRtcEventLog::ParsedRtcEventLog( + UnconfiguredHeaderExtensions parse_unconfigured_header_extensions, + bool allow_incomplete_logs) + : parse_unconfigured_header_extensions_( + parse_unconfigured_header_extensions), + allow_incomplete_logs_(allow_incomplete_logs) { + Clear(); +} + +void ParsedRtcEventLog::Clear() { + default_extension_map_ = GetDefaultHeaderExtensionMap(); + + incoming_rtx_ssrcs_.clear(); + incoming_video_ssrcs_.clear(); + incoming_audio_ssrcs_.clear(); + outgoing_rtx_ssrcs_.clear(); + outgoing_video_ssrcs_.clear(); + outgoing_audio_ssrcs_.clear(); + + incoming_rtp_packets_map_.clear(); + outgoing_rtp_packets_map_.clear(); + incoming_rtp_packets_by_ssrc_.clear(); + outgoing_rtp_packets_by_ssrc_.clear(); + incoming_rtp_packet_views_by_ssrc_.clear(); + outgoing_rtp_packet_views_by_ssrc_.clear(); + + incoming_rtcp_packets_.clear(); + outgoing_rtcp_packets_.clear(); + + incoming_rr_.clear(); + outgoing_rr_.clear(); + incoming_sr_.clear(); + outgoing_sr_.clear(); + incoming_nack_.clear(); + outgoing_nack_.clear(); + incoming_remb_.clear(); + outgoing_remb_.clear(); + incoming_transport_feedback_.clear(); + outgoing_transport_feedback_.clear(); + incoming_loss_notification_.clear(); + outgoing_loss_notification_.clear(); + + start_log_events_.clear(); + stop_log_events_.clear(); + audio_playout_events_.clear(); + neteq_set_minimum_delay_events_.clear(); + audio_network_adaptation_events_.clear(); + bwe_probe_cluster_created_events_.clear(); + bwe_probe_failure_events_.clear(); + bwe_probe_success_events_.clear(); + bwe_delay_updates_.clear(); + bwe_loss_updates_.clear(); + dtls_transport_states_.clear(); + dtls_writable_states_.clear(); + decoded_frames_.clear(); + alr_state_events_.clear(); + ice_candidate_pair_configs_.clear(); + ice_candidate_pair_events_.clear(); + audio_recv_configs_.clear(); + audio_send_configs_.clear(); + video_recv_configs_.clear(); + video_send_configs_.clear(); + + last_incoming_rtcp_packet_.clear(); + + first_timestamp_ = Timestamp::PlusInfinity(); + last_timestamp_ = Timestamp::MinusInfinity(); + first_log_segment_ = LogSegment(0, std::numeric_limits<int64_t>::max()); + + incoming_rtp_extensions_maps_.clear(); + outgoing_rtp_extensions_maps_.clear(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseFile( + absl::string_view filename) { + FileWrapper file = FileWrapper::OpenReadOnly(filename); + if (!file.is_open()) { + RTC_LOG(LS_WARNING) << "Could not open file " << filename + << " for reading."; + RTC_PARSE_CHECK_OR_RETURN(file.is_open()); + } + + // Compute file size. + long signed_filesize = file.FileSize(); // NOLINT(runtime/int) + RTC_PARSE_CHECK_OR_RETURN_GE(signed_filesize, 0); + RTC_PARSE_CHECK_OR_RETURN_LE(signed_filesize, kMaxLogSize); + size_t filesize = rtc::checked_cast<size_t>(signed_filesize); + + // Read file into memory. + std::string buffer(filesize, '\0'); + size_t bytes_read = file.Read(&buffer[0], buffer.size()); + if (bytes_read != filesize) { + RTC_LOG(LS_WARNING) << "Failed to read file " << filename; + RTC_PARSE_CHECK_OR_RETURN_EQ(bytes_read, filesize); + } + + return ParseStream(buffer); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseString( + absl::string_view s) { + return ParseStream(s); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStream( + absl::string_view s) { + Clear(); + ParseStatus status = ParseStreamInternal(s); + + // Cache the configured SSRCs. + for (const auto& video_recv_config : video_recv_configs()) { + incoming_video_ssrcs_.insert(video_recv_config.config.remote_ssrc); + incoming_video_ssrcs_.insert(video_recv_config.config.rtx_ssrc); + incoming_rtx_ssrcs_.insert(video_recv_config.config.rtx_ssrc); + } + for (const auto& video_send_config : video_send_configs()) { + outgoing_video_ssrcs_.insert(video_send_config.config.local_ssrc); + outgoing_video_ssrcs_.insert(video_send_config.config.rtx_ssrc); + outgoing_rtx_ssrcs_.insert(video_send_config.config.rtx_ssrc); + } + for (const auto& audio_recv_config : audio_recv_configs()) { + incoming_audio_ssrcs_.insert(audio_recv_config.config.remote_ssrc); + } + for (const auto& audio_send_config : audio_send_configs()) { + outgoing_audio_ssrcs_.insert(audio_send_config.config.local_ssrc); + } + + // ParseStreamInternal stores the RTP packets in a map indexed by SSRC. + // Since we dont need rapid lookup based on SSRC after parsing, we move the + // packets_streams from map to vector. + incoming_rtp_packets_by_ssrc_.reserve(incoming_rtp_packets_map_.size()); + for (auto& kv : incoming_rtp_packets_map_) { + incoming_rtp_packets_by_ssrc_.emplace_back(LoggedRtpStreamIncoming()); + incoming_rtp_packets_by_ssrc_.back().ssrc = kv.first; + incoming_rtp_packets_by_ssrc_.back().incoming_packets = + std::move(kv.second); + } + incoming_rtp_packets_map_.clear(); + outgoing_rtp_packets_by_ssrc_.reserve(outgoing_rtp_packets_map_.size()); + for (auto& kv : outgoing_rtp_packets_map_) { + outgoing_rtp_packets_by_ssrc_.emplace_back(LoggedRtpStreamOutgoing()); + outgoing_rtp_packets_by_ssrc_.back().ssrc = kv.first; + outgoing_rtp_packets_by_ssrc_.back().outgoing_packets = + std::move(kv.second); + } + outgoing_rtp_packets_map_.clear(); + + // Build PacketViews for easier iteration over RTP packets. + for (const auto& stream : incoming_rtp_packets_by_ssrc_) { + incoming_rtp_packet_views_by_ssrc_.emplace_back( + LoggedRtpStreamView(stream.ssrc, stream.incoming_packets)); + } + for (const auto& stream : outgoing_rtp_packets_by_ssrc_) { + outgoing_rtp_packet_views_by_ssrc_.emplace_back( + LoggedRtpStreamView(stream.ssrc, stream.outgoing_packets)); + } + + // Set up convenience wrappers around the most commonly used RTCP types. + for (const auto& incoming : incoming_rtcp_packets_) { + const int64_t timestamp_us = incoming.rtcp.timestamp.us(); + const uint8_t* packet_begin = incoming.rtcp.raw_data.data(); + const uint8_t* packet_end = packet_begin + incoming.rtcp.raw_data.size(); + auto store_rtcp_status = StoreRtcpBlocks( + timestamp_us, packet_begin, packet_end, &incoming_sr_, &incoming_rr_, + &incoming_xr_, &incoming_remb_, &incoming_nack_, &incoming_fir_, + &incoming_pli_, &incoming_bye_, &incoming_transport_feedback_, + &incoming_loss_notification_); + RTC_RETURN_IF_ERROR(store_rtcp_status); + } + + for (const auto& outgoing : outgoing_rtcp_packets_) { + const int64_t timestamp_us = outgoing.rtcp.timestamp.us(); + const uint8_t* packet_begin = outgoing.rtcp.raw_data.data(); + const uint8_t* packet_end = packet_begin + outgoing.rtcp.raw_data.size(); + auto store_rtcp_status = StoreRtcpBlocks( + timestamp_us, packet_begin, packet_end, &outgoing_sr_, &outgoing_rr_, + &outgoing_xr_, &outgoing_remb_, &outgoing_nack_, &outgoing_fir_, + &outgoing_pli_, &outgoing_bye_, &outgoing_transport_feedback_, + &outgoing_loss_notification_); + RTC_RETURN_IF_ERROR(store_rtcp_status); + } + + // Store first and last timestamp events that might happen before the call is + // connected or after the call is disconnected. Typical examples are + // stream configurations and starting/stopping the log. + // TODO(terelius): Figure out if we actually need to find the first and last + // timestamp in the parser. It seems like this could be done by the caller. + first_timestamp_ = Timestamp::PlusInfinity(); + last_timestamp_ = Timestamp::MinusInfinity(); + StoreFirstAndLastTimestamp(alr_state_events()); + StoreFirstAndLastTimestamp(route_change_events()); + for (const auto& audio_stream : audio_playout_events()) { + // Audio playout events are grouped by SSRC. + StoreFirstAndLastTimestamp(audio_stream.second); + } + for (const auto& set_minimum_delay : neteq_set_minimum_delay_events()) { + // NetEq SetMinimumDelay grouped by SSRC. + StoreFirstAndLastTimestamp(set_minimum_delay.second); + } + StoreFirstAndLastTimestamp(audio_network_adaptation_events()); + StoreFirstAndLastTimestamp(bwe_probe_cluster_created_events()); + StoreFirstAndLastTimestamp(bwe_probe_failure_events()); + StoreFirstAndLastTimestamp(bwe_probe_success_events()); + StoreFirstAndLastTimestamp(bwe_delay_updates()); + StoreFirstAndLastTimestamp(bwe_loss_updates()); + for (const auto& frame_stream : decoded_frames()) { + StoreFirstAndLastTimestamp(frame_stream.second); + } + StoreFirstAndLastTimestamp(dtls_transport_states()); + StoreFirstAndLastTimestamp(dtls_writable_states()); + StoreFirstAndLastTimestamp(ice_candidate_pair_configs()); + StoreFirstAndLastTimestamp(ice_candidate_pair_events()); + for (const auto& rtp_stream : incoming_rtp_packets_by_ssrc()) { + StoreFirstAndLastTimestamp(rtp_stream.incoming_packets); + } + for (const auto& rtp_stream : outgoing_rtp_packets_by_ssrc()) { + StoreFirstAndLastTimestamp(rtp_stream.outgoing_packets); + } + StoreFirstAndLastTimestamp(incoming_rtcp_packets()); + StoreFirstAndLastTimestamp(outgoing_rtcp_packets()); + StoreFirstAndLastTimestamp(generic_packets_sent_); + StoreFirstAndLastTimestamp(generic_packets_received_); + StoreFirstAndLastTimestamp(generic_acks_received_); + StoreFirstAndLastTimestamp(remote_estimate_events_); + + // Stop events could be missing due to file size limits. If so, use the + // last event, or the next start timestamp if available. + // TODO(terelius): This could be improved. Instead of using the next start + // event, we could use the timestamp of the the last previous regular event. + auto start_iter = start_log_events().begin(); + auto stop_iter = stop_log_events().begin(); + int64_t start_us = + first_timestamp().us_or(std::numeric_limits<int64_t>::max()); + int64_t next_start_us = std::numeric_limits<int64_t>::max(); + int64_t stop_us = std::numeric_limits<int64_t>::max(); + if (start_iter != start_log_events().end()) { + start_us = std::min(start_us, start_iter->log_time_us()); + ++start_iter; + if (start_iter != start_log_events().end()) + next_start_us = start_iter->log_time_us(); + } + if (stop_iter != stop_log_events().end()) { + stop_us = stop_iter->log_time_us(); + } + stop_us = std::min(stop_us, next_start_us); + if (stop_us == std::numeric_limits<int64_t>::max() && + !last_timestamp().IsMinusInfinity()) { + stop_us = last_timestamp().us(); + } + RTC_PARSE_CHECK_OR_RETURN_LE(start_us, stop_us); + first_log_segment_ = LogSegment(start_us, stop_us); + + if (first_timestamp_ > last_timestamp_) { + first_timestamp_ = last_timestamp_ = Timestamp::Zero(); + } + + return status; +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStreamInternal( + absl::string_view s) { + constexpr uint64_t kMaxEventSize = 10000000; // Sanity check. + // Protobuf defines the message tag as + // (field_number << 3) | wire_type. In the legacy encoding, the field number + // is supposed to be 1 and the wire type for a length-delimited field is 2. + // In the new encoding we still expect the wire type to be 2, but the field + // number will be greater than 1. + constexpr uint64_t kExpectedV1Tag = (1 << 3) | 2; + bool success = false; + + // "Peek" at the first varint. + absl::string_view event_start = s; + uint64_t tag = 0; + std::tie(success, std::ignore) = DecodeVarInt(s, &tag); + if (!success) { + RTC_LOG(LS_WARNING) << "Failed to read varint from beginning of event log."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + return ParseStatus::Error("Failed to read field tag varint", __FILE__, + __LINE__); + } + s = event_start; + + if (tag >> 1 == static_cast<uint64_t>(RtcEvent::Type::BeginV3Log)) { + return ParseStreamInternalV3(s); + } + + while (!s.empty()) { + // If not, "reset" event_start and read the field tag for the next event. + event_start = s; + std::tie(success, s) = DecodeVarInt(s, &tag); + if (!success) { + RTC_LOG(LS_WARNING) + << "Failed to read field tag from beginning of protobuf event."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + return ParseStatus::Error("Failed to read field tag varint", __FILE__, + __LINE__); + } + + constexpr uint64_t kWireTypeMask = 0x07; + const uint64_t wire_type = tag & kWireTypeMask; + if (wire_type != 2) { + RTC_LOG(LS_WARNING) << "Expected field tag with wire type 2 (length " + "delimited message). Found wire type " + << wire_type; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + RTC_PARSE_CHECK_OR_RETURN_EQ(wire_type, 2); + } + + // Read the length field. + uint64_t message_length = 0; + std::tie(success, s) = DecodeVarInt(s, &message_length); + if (!success) { + RTC_LOG(LS_WARNING) << "Missing message length after protobuf field tag."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + return ParseStatus::Error("Failed to read message length varint", + __FILE__, __LINE__); + } + + if (message_length > s.size()) { + RTC_LOG(LS_WARNING) << "Protobuf message length is larger than the " + "remaining bytes in the proto."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + return ParseStatus::Error( + "Incomplete message: the length of the next message is larger than " + "the remaining bytes in the proto", + __FILE__, __LINE__); + } + + RTC_PARSE_CHECK_OR_RETURN_LE(message_length, kMaxEventSize); + // Skip forward to the start of the next event. + s = s.substr(message_length); + size_t total_event_size = event_start.size() - s.size(); + RTC_CHECK_LE(total_event_size, event_start.size()); + + if (tag == kExpectedV1Tag) { + // Parse the protobuf event from the buffer. + rtclog::EventStream event_stream; + if (!event_stream.ParseFromArray(event_start.data(), total_event_size)) { + RTC_LOG(LS_WARNING) + << "Failed to parse legacy-format protobuf message."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + RTC_PARSE_CHECK_OR_RETURN(false); + } + + RTC_PARSE_CHECK_OR_RETURN_EQ(event_stream.stream_size(), 1); + auto status = StoreParsedLegacyEvent(event_stream.stream(0)); + RTC_RETURN_IF_ERROR(status); + } else { + // Parse the protobuf event from the buffer. + rtclog2::EventStream event_stream; + if (!event_stream.ParseFromArray(event_start.data(), total_event_size)) { + RTC_LOG(LS_WARNING) << "Failed to parse new-format protobuf message."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + RTC_PARSE_CHECK_OR_RETURN(false); + } + auto status = StoreParsedNewFormatEvent(event_stream); + RTC_RETURN_IF_ERROR(status); + } + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStreamInternalV3( + absl::string_view s) { + constexpr uint64_t kMaxEventSize = 10000000; // Sanity check. + bool expect_begin_log_event = true; + bool success = false; + + while (!s.empty()) { + // Read event type. + uint64_t event_tag = 0; + std::tie(success, s) = DecodeVarInt(s, &event_tag); + RTC_PARSE_CHECK_OR_RETURN_MESSAGE(success, "Failed to read event type."); + bool batched = event_tag & 1; + uint64_t event_type = event_tag >> 1; + + // Read event size + uint64_t event_size_bytes = 0; + std::tie(success, s) = DecodeVarInt(s, &event_size_bytes); + RTC_PARSE_CHECK_OR_RETURN_MESSAGE(success, "Failed to read event size."); + if (event_size_bytes > kMaxEventSize || event_size_bytes > s.size()) { + RTC_LOG(LS_WARNING) << "Event size is too large."; + RTC_PARSE_CHECK_OR_RETURN_LE(event_size_bytes, kMaxEventSize); + RTC_PARSE_CHECK_OR_RETURN_LE(event_size_bytes, s.size()); + } + + // Read remaining event fields into a buffer. + absl::string_view event_fields = s.substr(0, event_size_bytes); + s = s.substr(event_size_bytes); + + if (expect_begin_log_event) { + RTC_PARSE_CHECK_OR_RETURN_EQ( + event_type, static_cast<uint32_t>(RtcEvent::Type::BeginV3Log)); + expect_begin_log_event = false; + } + + switch (event_type) { + case static_cast<uint32_t>(RtcEvent::Type::BeginV3Log): + RtcEventBeginLog::Parse(event_fields, batched, start_log_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::EndV3Log): + RtcEventEndLog::Parse(event_fields, batched, stop_log_events_); + expect_begin_log_event = true; + break; + case static_cast<uint32_t>(RtcEvent::Type::AlrStateEvent): + RtcEventAlrState::Parse(event_fields, batched, alr_state_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::AudioPlayout): + RtcEventAudioPlayout::Parse(event_fields, batched, + audio_playout_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::BweUpdateDelayBased): + RtcEventBweUpdateDelayBased::Parse(event_fields, batched, + bwe_delay_updates_); + break; + case static_cast<uint32_t>(RtcEvent::Type::AudioNetworkAdaptation): + RtcEventAudioNetworkAdaptation::Parse(event_fields, batched, + audio_network_adaptation_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::AudioReceiveStreamConfig): + RtcEventAudioReceiveStreamConfig::Parse(event_fields, batched, + audio_recv_configs_); + break; + case static_cast<uint32_t>(RtcEvent::Type::AudioSendStreamConfig): + RtcEventAudioSendStreamConfig::Parse(event_fields, batched, + audio_send_configs_); + break; + case static_cast<uint32_t>(RtcEvent::Type::BweUpdateLossBased): + RtcEventBweUpdateLossBased::Parse(event_fields, batched, + bwe_loss_updates_); + break; + case static_cast<uint32_t>(RtcEvent::Type::DtlsTransportState): + RtcEventDtlsTransportState::Parse(event_fields, batched, + dtls_transport_states_); + break; + case static_cast<uint32_t>(RtcEvent::Type::DtlsWritableState): + RtcEventDtlsWritableState::Parse(event_fields, batched, + dtls_writable_states_); + break; + case static_cast<uint32_t>(RtcEvent::Type::FrameDecoded): + RtcEventFrameDecoded::Parse(event_fields, batched, decoded_frames_); + break; + case static_cast<uint32_t>(RtcEvent::Type::GenericAckReceived): + RtcEventGenericAckReceived::Parse(event_fields, batched, + generic_acks_received_); + break; + case static_cast<uint32_t>(RtcEvent::Type::GenericPacketReceived): + RtcEventGenericPacketReceived::Parse(event_fields, batched, + generic_packets_received_); + break; + case static_cast<uint32_t>(RtcEvent::Type::GenericPacketSent): + RtcEventGenericPacketSent::Parse(event_fields, batched, + generic_packets_sent_); + break; + case static_cast<uint32_t>(RtcEvent::Type::IceCandidatePairConfig): + RtcEventIceCandidatePairConfig::Parse(event_fields, batched, + ice_candidate_pair_configs_); + break; + case static_cast<uint32_t>(RtcEvent::Type::IceCandidatePairEvent): + RtcEventIceCandidatePair::Parse(event_fields, batched, + ice_candidate_pair_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::ProbeClusterCreated): + RtcEventProbeClusterCreated::Parse(event_fields, batched, + bwe_probe_cluster_created_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::ProbeResultFailure): + RtcEventProbeResultFailure::Parse(event_fields, batched, + bwe_probe_failure_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::ProbeResultSuccess): + RtcEventProbeResultSuccess::Parse(event_fields, batched, + bwe_probe_success_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::RemoteEstimateEvent): + RtcEventRemoteEstimate::Parse(event_fields, batched, + remote_estimate_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::RouteChangeEvent): + RtcEventRouteChange::Parse(event_fields, batched, route_change_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::RtcpPacketIncoming): + RtcEventRtcpPacketIncoming::Parse(event_fields, batched, + incoming_rtcp_packets_); + break; + case static_cast<uint32_t>(RtcEvent::Type::RtcpPacketOutgoing): + RtcEventRtcpPacketOutgoing::Parse(event_fields, batched, + outgoing_rtcp_packets_); + break; + case static_cast<uint32_t>(RtcEvent::Type::RtpPacketIncoming): + RtcEventRtpPacketIncoming::Parse(event_fields, batched, + incoming_rtp_packets_map_); + break; + case static_cast<uint32_t>(RtcEvent::Type::RtpPacketOutgoing): + RtcEventRtpPacketOutgoing::Parse(event_fields, batched, + outgoing_rtp_packets_map_); + break; + case static_cast<uint32_t>(RtcEvent::Type::VideoReceiveStreamConfig): + RtcEventVideoReceiveStreamConfig::Parse(event_fields, batched, + video_recv_configs_); + break; + case static_cast<uint32_t>(RtcEvent::Type::VideoSendStreamConfig): + RtcEventVideoSendStreamConfig::Parse(event_fields, batched, + video_send_configs_); + break; + } + } + + return ParseStatus::Success(); +} + +template <typename T> +void ParsedRtcEventLog::StoreFirstAndLastTimestamp(const std::vector<T>& v) { + if (v.empty()) + return; + first_timestamp_ = std::min(first_timestamp_, v.front().log_time()); + last_timestamp_ = std::max(last_timestamp_, v.back().log_time()); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreParsedLegacyEvent( + const rtclog::Event& event) { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + switch (event.type()) { + case rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT: { + auto config = GetVideoReceiveConfig(event); + if (!config.ok()) + return config.status(); + + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + video_recv_configs_.emplace_back(Timestamp::Micros(timestamp_us), + config.value()); + incoming_rtp_extensions_maps_[config.value().remote_ssrc] = + RtpHeaderExtensionMap(config.value().rtp_extensions); + incoming_rtp_extensions_maps_[config.value().rtx_ssrc] = + RtpHeaderExtensionMap(config.value().rtp_extensions); + break; + } + case rtclog::Event::VIDEO_SENDER_CONFIG_EVENT: { + auto config = GetVideoSendConfig(event); + if (!config.ok()) + return config.status(); + + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + video_send_configs_.emplace_back(Timestamp::Micros(timestamp_us), + config.value()); + outgoing_rtp_extensions_maps_[config.value().local_ssrc] = + RtpHeaderExtensionMap(config.value().rtp_extensions); + outgoing_rtp_extensions_maps_[config.value().rtx_ssrc] = + RtpHeaderExtensionMap(config.value().rtp_extensions); + break; + } + case rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT: { + auto config = GetAudioReceiveConfig(event); + if (!config.ok()) + return config.status(); + + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + audio_recv_configs_.emplace_back(Timestamp::Micros(timestamp_us), + config.value()); + incoming_rtp_extensions_maps_[config.value().remote_ssrc] = + RtpHeaderExtensionMap(config.value().rtp_extensions); + break; + } + case rtclog::Event::AUDIO_SENDER_CONFIG_EVENT: { + auto config = GetAudioSendConfig(event); + if (!config.ok()) + return config.status(); + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + audio_send_configs_.emplace_back(Timestamp::Micros(timestamp_us), + config.value()); + outgoing_rtp_extensions_maps_[config.value().local_ssrc] = + RtpHeaderExtensionMap(config.value().rtp_extensions); + break; + } + case rtclog::Event::RTP_EVENT: { + RTC_PARSE_CHECK_OR_RETURN(event.has_rtp_packet()); + const rtclog::RtpPacket& rtp_packet = event.rtp_packet(); + RTC_PARSE_CHECK_OR_RETURN(rtp_packet.has_header()); + RTC_PARSE_CHECK_OR_RETURN(rtp_packet.has_incoming()); + RTC_PARSE_CHECK_OR_RETURN(rtp_packet.has_packet_length()); + size_t total_length = rtp_packet.packet_length(); + + // Use RtpPacketReceived instead of more generic RtpPacket because former + // has a buildin convertion to RTPHeader. + RtpPacketReceived rtp_header; + RTC_PARSE_CHECK_OR_RETURN( + rtp_header.Parse(rtc::CopyOnWriteBuffer(rtp_packet.header()))); + + if (const RtpHeaderExtensionMap* extension_map = GetRtpHeaderExtensionMap( + rtp_packet.incoming(), rtp_header.Ssrc())) { + rtp_header.IdentifyExtensions(*extension_map); + } + + RTPHeader parsed_header; + rtp_header.GetHeader(&parsed_header); + + // Since we give the parser only a header, there is no way for it to know + // the padding length. The best solution would be to log the padding + // length in RTC event log. In absence of it, we assume the RTP packet to + // contain only padding, if the padding bit is set. + // TODO(webrtc:9730): Use a generic way to obtain padding length. + if (rtp_header.has_padding()) + parsed_header.paddingLength = total_length - rtp_header.size(); + + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + if (rtp_packet.incoming()) { + incoming_rtp_packets_map_[parsed_header.ssrc].push_back( + LoggedRtpPacketIncoming(Timestamp::Micros(timestamp_us), + parsed_header, rtp_header.size(), + total_length)); + } else { + outgoing_rtp_packets_map_[parsed_header.ssrc].push_back( + LoggedRtpPacketOutgoing(Timestamp::Micros(timestamp_us), + parsed_header, rtp_header.size(), + total_length)); + } + break; + } + case rtclog::Event::RTCP_EVENT: { + PacketDirection direction; + std::vector<uint8_t> packet; + auto status = GetRtcpPacket(event, &direction, &packet); + RTC_RETURN_IF_ERROR(status); + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + if (direction == kIncomingPacket) { + // Currently incoming RTCP packets are logged twice, both for audio and + // video. Only act on one of them. Compare against the previous parsed + // incoming RTCP packet. + if (packet == last_incoming_rtcp_packet_) + break; + incoming_rtcp_packets_.push_back( + LoggedRtcpPacketIncoming(Timestamp::Micros(timestamp_us), packet)); + last_incoming_rtcp_packet_ = packet; + } else { + outgoing_rtcp_packets_.push_back( + LoggedRtcpPacketOutgoing(Timestamp::Micros(timestamp_us), packet)); + } + break; + } + case rtclog::Event::LOG_START: { + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + start_log_events_.push_back( + LoggedStartEvent(Timestamp::Micros(timestamp_us))); + break; + } + case rtclog::Event::LOG_END: { + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + stop_log_events_.push_back( + LoggedStopEvent(Timestamp::Micros(timestamp_us))); + break; + } + case rtclog::Event::AUDIO_PLAYOUT_EVENT: { + auto status_or_value = GetAudioPlayout(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + LoggedAudioPlayoutEvent playout_event = status_or_value.value(); + audio_playout_events_[playout_event.ssrc].push_back(playout_event); + break; + } + case rtclog::Event::LOSS_BASED_BWE_UPDATE: { + auto status_or_value = GetLossBasedBweUpdate(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + bwe_loss_updates_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::DELAY_BASED_BWE_UPDATE: { + auto status_or_value = GetDelayBasedBweUpdate(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + bwe_delay_updates_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT: { + auto status_or_value = GetAudioNetworkAdaptation(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + LoggedAudioNetworkAdaptationEvent ana_event = status_or_value.value(); + audio_network_adaptation_events_.push_back(ana_event); + break; + } + case rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT: { + auto status_or_value = GetBweProbeClusterCreated(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + bwe_probe_cluster_created_events_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::BWE_PROBE_RESULT_EVENT: { + // Probe successes and failures are currently stored in the same proto + // message, we are moving towards separate messages. Probe results + // therefore need special treatment in the parser. + RTC_PARSE_CHECK_OR_RETURN(event.has_probe_result()); + RTC_PARSE_CHECK_OR_RETURN(event.probe_result().has_result()); + if (event.probe_result().result() == rtclog::BweProbeResult::SUCCESS) { + auto status_or_value = GetBweProbeSuccess(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + bwe_probe_success_events_.push_back(status_or_value.value()); + } else { + auto status_or_value = GetBweProbeFailure(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + bwe_probe_failure_events_.push_back(status_or_value.value()); + } + break; + } + case rtclog::Event::ALR_STATE_EVENT: { + auto status_or_value = GetAlrState(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + alr_state_events_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::ICE_CANDIDATE_PAIR_CONFIG: { + auto status_or_value = GetIceCandidatePairConfig(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + ice_candidate_pair_configs_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::ICE_CANDIDATE_PAIR_EVENT: { + auto status_or_value = GetIceCandidatePairEvent(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + ice_candidate_pair_events_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::REMOTE_ESTIMATE: { + auto status_or_value = GetRemoteEstimateEvent(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + remote_estimate_events_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::UNKNOWN_EVENT: { + break; + } + } + return ParseStatus::Success(); +} + +const RtpHeaderExtensionMap* ParsedRtcEventLog::GetRtpHeaderExtensionMap( + bool incoming, + uint32_t ssrc) { + auto& extensions_maps = + incoming ? incoming_rtp_extensions_maps_ : outgoing_rtp_extensions_maps_; + auto it = extensions_maps.find(ssrc); + if (it != extensions_maps.end()) { + return &(it->second); + } + if (parse_unconfigured_header_extensions_ == + UnconfiguredHeaderExtensions::kAttemptWebrtcDefaultConfig) { + RTC_DLOG(LS_WARNING) << "Using default header extension map for SSRC " + << ssrc; + extensions_maps.insert(std::make_pair(ssrc, default_extension_map_)); + return &default_extension_map_; + } + RTC_DLOG(LS_WARNING) << "Not parsing header extensions for SSRC " << ssrc + << ". No header extension map found."; + return nullptr; +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::GetRtcpPacket( + const rtclog::Event& event, + PacketDirection* incoming, + std::vector<uint8_t>* packet) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), rtclog::Event::RTCP_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_rtcp_packet()); + const rtclog::RtcpPacket& rtcp_packet = event.rtcp_packet(); + // Get direction of packet. + RTC_PARSE_CHECK_OR_RETURN(rtcp_packet.has_incoming()); + if (incoming != nullptr) { + *incoming = rtcp_packet.incoming() ? kIncomingPacket : kOutgoingPacket; + } + // Get packet contents. + RTC_PARSE_CHECK_OR_RETURN(rtcp_packet.has_packet_data()); + if (packet != nullptr) { + packet->resize(rtcp_packet.packet_data().size()); + memcpy(packet->data(), rtcp_packet.packet_data().data(), + rtcp_packet.packet_data().size()); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatusOr<rtclog::StreamConfig> +ParsedRtcEventLog::GetVideoReceiveConfig(const rtclog::Event& event) const { + rtclog::StreamConfig config; + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_video_receiver_config()); + const rtclog::VideoReceiveConfig& receiver_config = + event.video_receiver_config(); + // Get SSRCs. + RTC_PARSE_CHECK_OR_RETURN(receiver_config.has_remote_ssrc()); + config.remote_ssrc = receiver_config.remote_ssrc(); + RTC_PARSE_CHECK_OR_RETURN(receiver_config.has_local_ssrc()); + config.local_ssrc = receiver_config.local_ssrc(); + config.rtx_ssrc = 0; + // Get RTCP settings. + RTC_PARSE_CHECK_OR_RETURN(receiver_config.has_rtcp_mode()); + config.rtcp_mode = GetRuntimeRtcpMode(receiver_config.rtcp_mode()); + RTC_PARSE_CHECK_OR_RETURN(receiver_config.has_remb()); + config.remb = receiver_config.remb(); + + // Get RTX map. + std::map<uint32_t, const rtclog::RtxConfig> rtx_map; + for (int i = 0; i < receiver_config.rtx_map_size(); i++) { + const rtclog::RtxMap& map = receiver_config.rtx_map(i); + RTC_PARSE_CHECK_OR_RETURN(map.has_payload_type()); + RTC_PARSE_CHECK_OR_RETURN(map.has_config()); + RTC_PARSE_CHECK_OR_RETURN(map.config().has_rtx_ssrc()); + RTC_PARSE_CHECK_OR_RETURN(map.config().has_rtx_payload_type()); + rtx_map.insert(std::make_pair(map.payload_type(), map.config())); + } + + // Get header extensions. + auto status = GetHeaderExtensions(&config.rtp_extensions, + receiver_config.header_extensions()); + RTC_RETURN_IF_ERROR(status); + + // Get decoders. + config.codecs.clear(); + for (int i = 0; i < receiver_config.decoders_size(); i++) { + RTC_PARSE_CHECK_OR_RETURN(receiver_config.decoders(i).has_name()); + RTC_PARSE_CHECK_OR_RETURN(receiver_config.decoders(i).has_payload_type()); + int rtx_payload_type = 0; + auto rtx_it = rtx_map.find(receiver_config.decoders(i).payload_type()); + if (rtx_it != rtx_map.end()) { + rtx_payload_type = rtx_it->second.rtx_payload_type(); + if (config.rtx_ssrc != 0 && + config.rtx_ssrc != rtx_it->second.rtx_ssrc()) { + RTC_LOG(LS_WARNING) + << "RtcEventLog protobuf contained different SSRCs for " + "different received RTX payload types. Will only use " + "rtx_ssrc = " + << config.rtx_ssrc << "."; + } else { + config.rtx_ssrc = rtx_it->second.rtx_ssrc(); + } + } + config.codecs.emplace_back(receiver_config.decoders(i).name(), + receiver_config.decoders(i).payload_type(), + rtx_payload_type); + } + return config; +} + +ParsedRtcEventLog::ParseStatusOr<rtclog::StreamConfig> +ParsedRtcEventLog::GetVideoSendConfig(const rtclog::Event& event) const { + rtclog::StreamConfig config; + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::VIDEO_SENDER_CONFIG_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_video_sender_config()); + const rtclog::VideoSendConfig& sender_config = event.video_sender_config(); + + // Get SSRCs. + // VideoSendStreamConfig no longer stores multiple SSRCs. If you are + // analyzing a very old log, try building the parser from the same + // WebRTC version. + RTC_PARSE_CHECK_OR_RETURN_EQ(sender_config.ssrcs_size(), 1); + config.local_ssrc = sender_config.ssrcs(0); + RTC_PARSE_CHECK_OR_RETURN_LE(sender_config.rtx_ssrcs_size(), 1); + if (sender_config.rtx_ssrcs_size() == 1) { + config.rtx_ssrc = sender_config.rtx_ssrcs(0); + } + + // Get header extensions. + auto status = GetHeaderExtensions(&config.rtp_extensions, + sender_config.header_extensions()); + RTC_RETURN_IF_ERROR(status); + + // Get the codec. + RTC_PARSE_CHECK_OR_RETURN(sender_config.has_encoder()); + RTC_PARSE_CHECK_OR_RETURN(sender_config.encoder().has_name()); + RTC_PARSE_CHECK_OR_RETURN(sender_config.encoder().has_payload_type()); + config.codecs.emplace_back( + sender_config.encoder().name(), sender_config.encoder().payload_type(), + sender_config.has_rtx_payload_type() ? sender_config.rtx_payload_type() + : 0); + return config; +} + +ParsedRtcEventLog::ParseStatusOr<rtclog::StreamConfig> +ParsedRtcEventLog::GetAudioReceiveConfig(const rtclog::Event& event) const { + rtclog::StreamConfig config; + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_audio_receiver_config()); + const rtclog::AudioReceiveConfig& receiver_config = + event.audio_receiver_config(); + // Get SSRCs. + RTC_PARSE_CHECK_OR_RETURN(receiver_config.has_remote_ssrc()); + config.remote_ssrc = receiver_config.remote_ssrc(); + RTC_PARSE_CHECK_OR_RETURN(receiver_config.has_local_ssrc()); + config.local_ssrc = receiver_config.local_ssrc(); + // Get header extensions. + auto status = GetHeaderExtensions(&config.rtp_extensions, + receiver_config.header_extensions()); + RTC_RETURN_IF_ERROR(status); + + return config; +} + +ParsedRtcEventLog::ParseStatusOr<rtclog::StreamConfig> +ParsedRtcEventLog::GetAudioSendConfig(const rtclog::Event& event) const { + rtclog::StreamConfig config; + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::AUDIO_SENDER_CONFIG_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_audio_sender_config()); + const rtclog::AudioSendConfig& sender_config = event.audio_sender_config(); + // Get SSRCs. + RTC_PARSE_CHECK_OR_RETURN(sender_config.has_ssrc()); + config.local_ssrc = sender_config.ssrc(); + // Get header extensions. + auto status = GetHeaderExtensions(&config.rtp_extensions, + sender_config.header_extensions()); + RTC_RETURN_IF_ERROR(status); + + return config; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedAudioPlayoutEvent> +ParsedRtcEventLog::GetAudioPlayout(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::AUDIO_PLAYOUT_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_audio_playout_event()); + const rtclog::AudioPlayoutEvent& playout_event = event.audio_playout_event(); + LoggedAudioPlayoutEvent res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(playout_event.has_local_ssrc()); + res.ssrc = playout_event.local_ssrc(); + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedBweLossBasedUpdate> +ParsedRtcEventLog::GetLossBasedBweUpdate(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::LOSS_BASED_BWE_UPDATE); + RTC_PARSE_CHECK_OR_RETURN(event.has_loss_based_bwe_update()); + const rtclog::LossBasedBweUpdate& loss_event = event.loss_based_bwe_update(); + + LoggedBweLossBasedUpdate bwe_update; + RTC_CHECK(event.has_timestamp_us()); + bwe_update.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(loss_event.has_bitrate_bps()); + bwe_update.bitrate_bps = loss_event.bitrate_bps(); + RTC_PARSE_CHECK_OR_RETURN(loss_event.has_fraction_loss()); + bwe_update.fraction_lost = loss_event.fraction_loss(); + RTC_PARSE_CHECK_OR_RETURN(loss_event.has_total_packets()); + bwe_update.expected_packets = loss_event.total_packets(); + return bwe_update; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedBweDelayBasedUpdate> +ParsedRtcEventLog::GetDelayBasedBweUpdate(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::DELAY_BASED_BWE_UPDATE); + RTC_PARSE_CHECK_OR_RETURN(event.has_delay_based_bwe_update()); + const rtclog::DelayBasedBweUpdate& delay_event = + event.delay_based_bwe_update(); + + LoggedBweDelayBasedUpdate res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(delay_event.has_bitrate_bps()); + res.bitrate_bps = delay_event.bitrate_bps(); + RTC_PARSE_CHECK_OR_RETURN(delay_event.has_detector_state()); + res.detector_state = GetRuntimeDetectorState(delay_event.detector_state()); + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedAudioNetworkAdaptationEvent> +ParsedRtcEventLog::GetAudioNetworkAdaptation(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_audio_network_adaptation()); + const rtclog::AudioNetworkAdaptation& ana_event = + event.audio_network_adaptation(); + + LoggedAudioNetworkAdaptationEvent res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + if (ana_event.has_bitrate_bps()) + res.config.bitrate_bps = ana_event.bitrate_bps(); + if (ana_event.has_enable_fec()) + res.config.enable_fec = ana_event.enable_fec(); + if (ana_event.has_enable_dtx()) + res.config.enable_dtx = ana_event.enable_dtx(); + if (ana_event.has_frame_length_ms()) + res.config.frame_length_ms = ana_event.frame_length_ms(); + if (ana_event.has_num_channels()) + res.config.num_channels = ana_event.num_channels(); + if (ana_event.has_uplink_packet_loss_fraction()) + res.config.uplink_packet_loss_fraction = + ana_event.uplink_packet_loss_fraction(); + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedBweProbeClusterCreatedEvent> +ParsedRtcEventLog::GetBweProbeClusterCreated(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_probe_cluster()); + const rtclog::BweProbeCluster& pcc_event = event.probe_cluster(); + LoggedBweProbeClusterCreatedEvent res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(pcc_event.has_id()); + res.id = pcc_event.id(); + RTC_PARSE_CHECK_OR_RETURN(pcc_event.has_bitrate_bps()); + res.bitrate_bps = pcc_event.bitrate_bps(); + RTC_PARSE_CHECK_OR_RETURN(pcc_event.has_min_packets()); + res.min_packets = pcc_event.min_packets(); + RTC_PARSE_CHECK_OR_RETURN(pcc_event.has_min_bytes()); + res.min_bytes = pcc_event.min_bytes(); + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedBweProbeFailureEvent> +ParsedRtcEventLog::GetBweProbeFailure(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::BWE_PROBE_RESULT_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_probe_result()); + const rtclog::BweProbeResult& pr_event = event.probe_result(); + RTC_PARSE_CHECK_OR_RETURN(pr_event.has_result()); + RTC_PARSE_CHECK_OR_RETURN_NE(pr_event.result(), + rtclog::BweProbeResult::SUCCESS); + + LoggedBweProbeFailureEvent res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(pr_event.has_id()); + res.id = pr_event.id(); + RTC_PARSE_CHECK_OR_RETURN(pr_event.has_result()); + if (pr_event.result() == + rtclog::BweProbeResult::INVALID_SEND_RECEIVE_INTERVAL) { + res.failure_reason = ProbeFailureReason::kInvalidSendReceiveInterval; + } else if (pr_event.result() == + rtclog::BweProbeResult::INVALID_SEND_RECEIVE_RATIO) { + res.failure_reason = ProbeFailureReason::kInvalidSendReceiveRatio; + } else if (pr_event.result() == rtclog::BweProbeResult::TIMEOUT) { + res.failure_reason = ProbeFailureReason::kTimeout; + } else { + RTC_DCHECK_NOTREACHED(); + } + RTC_PARSE_CHECK_OR_RETURN(!pr_event.has_bitrate_bps()); + + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedBweProbeSuccessEvent> +ParsedRtcEventLog::GetBweProbeSuccess(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::BWE_PROBE_RESULT_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_probe_result()); + const rtclog::BweProbeResult& pr_event = event.probe_result(); + RTC_PARSE_CHECK_OR_RETURN(pr_event.has_result()); + RTC_PARSE_CHECK_OR_RETURN_EQ(pr_event.result(), + rtclog::BweProbeResult::SUCCESS); + + LoggedBweProbeSuccessEvent res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(pr_event.has_id()); + res.id = pr_event.id(); + RTC_PARSE_CHECK_OR_RETURN(pr_event.has_bitrate_bps()); + res.bitrate_bps = pr_event.bitrate_bps(); + + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedAlrStateEvent> +ParsedRtcEventLog::GetAlrState(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), rtclog::Event::ALR_STATE_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_alr_state()); + const rtclog::AlrState& alr_event = event.alr_state(); + LoggedAlrStateEvent res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(alr_event.has_in_alr()); + res.in_alr = alr_event.in_alr(); + + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedIceCandidatePairConfig> +ParsedRtcEventLog::GetIceCandidatePairConfig( + const rtclog::Event& rtc_event) const { + RTC_PARSE_CHECK_OR_RETURN(rtc_event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(rtc_event.type(), + rtclog::Event::ICE_CANDIDATE_PAIR_CONFIG); + LoggedIceCandidatePairConfig res; + const rtclog::IceCandidatePairConfig& config = + rtc_event.ice_candidate_pair_config(); + RTC_PARSE_CHECK_OR_RETURN(rtc_event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(rtc_event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(config.has_config_type()); + res.type = GetRuntimeIceCandidatePairConfigType(config.config_type()); + RTC_PARSE_CHECK_OR_RETURN(config.has_candidate_pair_id()); + res.candidate_pair_id = config.candidate_pair_id(); + RTC_PARSE_CHECK_OR_RETURN(config.has_local_candidate_type()); + res.local_candidate_type = + GetRuntimeIceCandidateType(config.local_candidate_type()); + RTC_PARSE_CHECK_OR_RETURN(config.has_local_relay_protocol()); + res.local_relay_protocol = + GetRuntimeIceCandidatePairProtocol(config.local_relay_protocol()); + RTC_PARSE_CHECK_OR_RETURN(config.has_local_network_type()); + res.local_network_type = + GetRuntimeIceCandidateNetworkType(config.local_network_type()); + RTC_PARSE_CHECK_OR_RETURN(config.has_local_address_family()); + res.local_address_family = + GetRuntimeIceCandidatePairAddressFamily(config.local_address_family()); + RTC_PARSE_CHECK_OR_RETURN(config.has_remote_candidate_type()); + res.remote_candidate_type = + GetRuntimeIceCandidateType(config.remote_candidate_type()); + RTC_PARSE_CHECK_OR_RETURN(config.has_remote_address_family()); + res.remote_address_family = + GetRuntimeIceCandidatePairAddressFamily(config.remote_address_family()); + RTC_PARSE_CHECK_OR_RETURN(config.has_candidate_pair_protocol()); + res.candidate_pair_protocol = + GetRuntimeIceCandidatePairProtocol(config.candidate_pair_protocol()); + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedIceCandidatePairEvent> +ParsedRtcEventLog::GetIceCandidatePairEvent( + const rtclog::Event& rtc_event) const { + RTC_PARSE_CHECK_OR_RETURN(rtc_event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(rtc_event.type(), + rtclog::Event::ICE_CANDIDATE_PAIR_EVENT); + LoggedIceCandidatePairEvent res; + const rtclog::IceCandidatePairEvent& event = + rtc_event.ice_candidate_pair_event(); + RTC_PARSE_CHECK_OR_RETURN(rtc_event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(rtc_event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(event.has_event_type()); + res.type = GetRuntimeIceCandidatePairEventType(event.event_type()); + RTC_PARSE_CHECK_OR_RETURN(event.has_candidate_pair_id()); + res.candidate_pair_id = event.candidate_pair_id(); + // transaction_id is not supported by rtclog::Event + res.transaction_id = 0; + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedRemoteEstimateEvent> +ParsedRtcEventLog::GetRemoteEstimateEvent(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), rtclog::Event::REMOTE_ESTIMATE); + LoggedRemoteEstimateEvent res; + const rtclog::RemoteEstimate& remote_estimate_event = event.remote_estimate(); + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + if (remote_estimate_event.has_link_capacity_lower_kbps()) + res.link_capacity_lower = DataRate::KilobitsPerSec( + remote_estimate_event.link_capacity_lower_kbps()); + if (remote_estimate_event.has_link_capacity_upper_kbps()) + res.link_capacity_upper = DataRate::KilobitsPerSec( + remote_estimate_event.link_capacity_upper_kbps()); + return res; +} + +// Returns the MediaType for registered SSRCs. Search from the end to use last +// registered types first. +ParsedRtcEventLog::MediaType ParsedRtcEventLog::GetMediaType( + uint32_t ssrc, + PacketDirection direction) const { + if (direction == kIncomingPacket) { + if (std::find(incoming_video_ssrcs_.begin(), incoming_video_ssrcs_.end(), + ssrc) != incoming_video_ssrcs_.end()) { + return MediaType::VIDEO; + } + if (std::find(incoming_audio_ssrcs_.begin(), incoming_audio_ssrcs_.end(), + ssrc) != incoming_audio_ssrcs_.end()) { + return MediaType::AUDIO; + } + } else { + if (std::find(outgoing_video_ssrcs_.begin(), outgoing_video_ssrcs_.end(), + ssrc) != outgoing_video_ssrcs_.end()) { + return MediaType::VIDEO; + } + if (std::find(outgoing_audio_ssrcs_.begin(), outgoing_audio_ssrcs_.end(), + ssrc) != outgoing_audio_ssrcs_.end()) { + return MediaType::AUDIO; + } + } + return MediaType::ANY; +} + +std::vector<InferredRouteChangeEvent> ParsedRtcEventLog::GetRouteChanges() + const { + std::vector<InferredRouteChangeEvent> route_changes; + for (auto& candidate : ice_candidate_pair_configs()) { + if (candidate.type == IceCandidatePairConfigType::kSelected) { + InferredRouteChangeEvent route; + route.route_id = candidate.candidate_pair_id; + route.log_time = Timestamp::Millis(candidate.log_time_ms()); + + route.send_overhead = kUdpOverhead + kSrtpOverhead + kIpv4Overhead; + if (candidate.remote_address_family == + IceCandidatePairAddressFamily::kIpv6) + route.send_overhead += kIpv6Overhead - kIpv4Overhead; + if (candidate.remote_candidate_type != IceCandidateType::kLocal) + route.send_overhead += kStunOverhead; + route.return_overhead = kUdpOverhead + kSrtpOverhead + kIpv4Overhead; + if (candidate.remote_address_family == + IceCandidatePairAddressFamily::kIpv6) + route.return_overhead += kIpv6Overhead - kIpv4Overhead; + if (candidate.remote_candidate_type != IceCandidateType::kLocal) + route.return_overhead += kStunOverhead; + route_changes.push_back(route); + } + } + return route_changes; +} + +std::vector<LoggedPacketInfo> ParsedRtcEventLog::GetPacketInfos( + PacketDirection direction) const { + std::map<uint32_t, MediaStreamInfo> streams; + if (direction == PacketDirection::kIncomingPacket) { + AddRecvStreamInfos(&streams, audio_recv_configs(), LoggedMediaType::kAudio); + AddRecvStreamInfos(&streams, video_recv_configs(), LoggedMediaType::kVideo); + } else if (direction == PacketDirection::kOutgoingPacket) { + AddSendStreamInfos(&streams, audio_send_configs(), LoggedMediaType::kAudio); + AddSendStreamInfos(&streams, video_send_configs(), LoggedMediaType::kVideo); + } + + std::vector<OverheadChangeEvent> overheads = + GetOverheadChangingEvents(GetRouteChanges(), direction); + auto overhead_iter = overheads.begin(); + std::vector<LoggedPacketInfo> packets; + std::map<int64_t, size_t> indices; + uint16_t current_overhead = kDefaultOverhead; + Timestamp last_log_time = Timestamp::Zero(); + RtpSequenceNumberUnwrapper seq_num_unwrapper; + + auto advance_time = [&](Timestamp new_log_time) { + if (overhead_iter != overheads.end() && + new_log_time >= overhead_iter->timestamp) { + current_overhead = overhead_iter->overhead; + ++overhead_iter; + } + // If we have a large time delta, it can be caused by a gap in logging, + // therefore we don't want to match up sequence numbers as we might have had + // a wraparound. + if (new_log_time - last_log_time > TimeDelta::Seconds(30)) { + seq_num_unwrapper.Reset(); + indices.clear(); + } + RTC_DCHECK_GE(new_log_time, last_log_time); + last_log_time = new_log_time; + }; + + auto rtp_handler = [&](const LoggedRtpPacket& rtp) { + advance_time(rtp.log_time()); + MediaStreamInfo* stream = &streams[rtp.header.ssrc]; + Timestamp capture_time = Timestamp::MinusInfinity(); + if (!stream->rtx) { + // RTX copy the timestamp of the retransmitted packets. This means that + // RTX streams don't have a unique clock offset and frequency, so + // the RTP timstamps can't be unwrapped. + + // Add an offset to avoid `capture_ticks` to become negative in the case + // of reordering. + constexpr int64_t kStartingCaptureTimeTicks = 90 * 48 * 10000; + int64_t capture_ticks = + kStartingCaptureTimeTicks + + stream->unwrap_capture_ticks.Unwrap(rtp.header.timestamp); + // TODO(srte): Use logged sample rate when it is added to the format. + capture_time = Timestamp::Seconds( + capture_ticks / + (stream->media_type == LoggedMediaType::kAudio ? 48000.0 : 90000.0)); + } + LoggedPacketInfo logged(rtp, stream->media_type, stream->rtx, capture_time); + logged.overhead = current_overhead; + if (logged.has_transport_seq_no) { + logged.log_feedback_time = Timestamp::PlusInfinity(); + int64_t unwrapped_seq_num = + seq_num_unwrapper.Unwrap(logged.transport_seq_no); + if (indices.find(unwrapped_seq_num) != indices.end()) { + auto prev = packets[indices[unwrapped_seq_num]]; + RTC_LOG(LS_WARNING) + << "Repeated sent packet sequence number: " << unwrapped_seq_num + << " Packet time:" << prev.log_packet_time.seconds() << "s vs " + << logged.log_packet_time.seconds() + << "s at:" << rtp.log_time_ms() / 1000; + } + indices[unwrapped_seq_num] = packets.size(); + } + packets.push_back(logged); + }; + + Timestamp feedback_base_time = Timestamp::MinusInfinity(); + Timestamp last_feedback_base_time = Timestamp::MinusInfinity(); + + auto feedback_handler = + [&](const LoggedRtcpPacketTransportFeedback& logged_rtcp) { + auto log_feedback_time = logged_rtcp.log_time(); + advance_time(log_feedback_time); + const auto& feedback = logged_rtcp.transport_feedback; + // Add timestamp deltas to a local time base selected on first packet + // arrival. This won't be the true time base, but makes it easier to + // manually inspect time stamps. + if (!last_feedback_base_time.IsFinite()) { + feedback_base_time = log_feedback_time; + } else { + feedback_base_time += feedback.GetBaseDelta(last_feedback_base_time); + } + last_feedback_base_time = feedback.BaseTime(); + + std::vector<LoggedPacketInfo*> packet_feedbacks; + packet_feedbacks.reserve(feedback.GetPacketStatusCount()); + std::vector<int64_t> unknown_seq_nums; + feedback.ForAllPackets([&](uint16_t sequence_number, + TimeDelta delta_since_base) { + int64_t unwrapped_seq_num = seq_num_unwrapper.Unwrap(sequence_number); + auto it = indices.find(unwrapped_seq_num); + if (it == indices.end()) { + unknown_seq_nums.push_back(unwrapped_seq_num); + return; + } + LoggedPacketInfo* sent = &packets[it->second]; + if (log_feedback_time - sent->log_packet_time > + TimeDelta::Seconds(60)) { + RTC_LOG(LS_WARNING) + << "Received very late feedback, possibly due to wraparound."; + return; + } + if (delta_since_base.IsFinite()) { + if (sent->reported_recv_time.IsInfinite()) { + sent->reported_recv_time = feedback_base_time + delta_since_base; + sent->log_feedback_time = log_feedback_time; + } + } else { + if (sent->reported_recv_time.IsInfinite() && + sent->log_feedback_time.IsInfinite()) { + sent->reported_recv_time = Timestamp::PlusInfinity(); + sent->log_feedback_time = log_feedback_time; + } + } + packet_feedbacks.push_back(sent); + }); + if (!unknown_seq_nums.empty()) { + RTC_LOG(LS_WARNING) + << "Received feedback for unknown packets: " + << unknown_seq_nums.front() << " - " << unknown_seq_nums.back(); + } + if (packet_feedbacks.empty()) + return; + LoggedPacketInfo* last = packet_feedbacks.back(); + last->last_in_feedback = true; + for (LoggedPacketInfo* fb : packet_feedbacks) { + if (direction == PacketDirection::kOutgoingPacket) { + if (last->reported_recv_time.IsFinite() && + fb->reported_recv_time.IsFinite()) { + fb->feedback_hold_duration = + last->reported_recv_time - fb->reported_recv_time; + } + } else { + fb->feedback_hold_duration = + log_feedback_time - fb->log_packet_time; + } + } + }; + + RtcEventProcessor process; + for (const auto& rtp_packets : rtp_packets_by_ssrc(direction)) { + process.AddEvents(rtp_packets.packet_view, rtp_handler, direction); + } + if (direction == PacketDirection::kOutgoingPacket) { + process.AddEvents(incoming_transport_feedback_, feedback_handler, + PacketDirection::kIncomingPacket); + } else { + process.AddEvents(outgoing_transport_feedback_, feedback_handler, + PacketDirection::kOutgoingPacket); + } + process.ProcessEventsInOrder(); + return packets; +} + +std::vector<LoggedIceCandidatePairConfig> ParsedRtcEventLog::GetIceCandidates() + const { + std::vector<LoggedIceCandidatePairConfig> candidates; + std::set<uint32_t> added; + for (auto& candidate : ice_candidate_pair_configs()) { + if (added.find(candidate.candidate_pair_id) == added.end()) { + candidates.push_back(candidate); + added.insert(candidate.candidate_pair_id); + } + } + return candidates; +} + +std::vector<LoggedIceEvent> ParsedRtcEventLog::GetIceEvents() const { + using CheckType = IceCandidatePairEventType; + using ConfigType = IceCandidatePairConfigType; + using Combined = LoggedIceEventType; + std::map<CheckType, Combined> check_map( + {{CheckType::kCheckSent, Combined::kCheckSent}, + {CheckType::kCheckReceived, Combined::kCheckReceived}, + {CheckType::kCheckResponseSent, Combined::kCheckResponseSent}, + {CheckType::kCheckResponseReceived, Combined::kCheckResponseReceived}}); + std::map<ConfigType, Combined> config_map( + {{ConfigType::kAdded, Combined::kAdded}, + {ConfigType::kUpdated, Combined::kUpdated}, + {ConfigType::kDestroyed, Combined::kDestroyed}, + {ConfigType::kSelected, Combined::kSelected}}); + std::vector<LoggedIceEvent> log_events; + auto handle_check = [&](const LoggedIceCandidatePairEvent& check) { + log_events.push_back(LoggedIceEvent{check.candidate_pair_id, + Timestamp::Millis(check.log_time_ms()), + check_map[check.type]}); + }; + auto handle_config = [&](const LoggedIceCandidatePairConfig& conf) { + log_events.push_back(LoggedIceEvent{conf.candidate_pair_id, + Timestamp::Millis(conf.log_time_ms()), + config_map[conf.type]}); + }; + RtcEventProcessor process; + process.AddEvents(ice_candidate_pair_events(), handle_check); + process.AddEvents(ice_candidate_pair_configs(), handle_config); + process.ProcessEventsInOrder(); + return log_events; +} + +const std::vector<MatchedSendArrivalTimes> GetNetworkTrace( + const ParsedRtcEventLog& parsed_log) { + std::vector<MatchedSendArrivalTimes> rtp_rtcp_matched; + for (auto& packet : + parsed_log.GetPacketInfos(PacketDirection::kOutgoingPacket)) { + if (packet.log_feedback_time.IsFinite()) { + rtp_rtcp_matched.emplace_back(packet.log_feedback_time.ms(), + packet.log_packet_time.ms(), + packet.reported_recv_time.ms_or( + MatchedSendArrivalTimes::kNotReceived), + packet.size); + } + } + return rtp_rtcp_matched; +} + +// Helper functions for new format start here +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreParsedNewFormatEvent( + const rtclog2::EventStream& stream) { + RTC_DCHECK_EQ(stream.stream_size(), 0); // No legacy format event. + + RTC_DCHECK_EQ( + stream.incoming_rtp_packets_size() + stream.outgoing_rtp_packets_size() + + stream.incoming_rtcp_packets_size() + + stream.outgoing_rtcp_packets_size() + + stream.audio_playout_events_size() + stream.begin_log_events_size() + + stream.end_log_events_size() + stream.loss_based_bwe_updates_size() + + stream.delay_based_bwe_updates_size() + + stream.dtls_transport_state_events_size() + + stream.dtls_writable_states_size() + + stream.audio_network_adaptations_size() + + stream.probe_clusters_size() + stream.probe_success_size() + + stream.probe_failure_size() + stream.alr_states_size() + + stream.route_changes_size() + stream.remote_estimates_size() + + stream.ice_candidate_configs_size() + + stream.ice_candidate_events_size() + + stream.audio_recv_stream_configs_size() + + stream.audio_send_stream_configs_size() + + stream.video_recv_stream_configs_size() + + stream.video_send_stream_configs_size() + + stream.generic_packets_sent_size() + + stream.generic_packets_received_size() + + stream.generic_acks_received_size() + + stream.frame_decoded_events_size() + + stream.neteq_set_minimum_delay_size(), + 1u); + + if (stream.incoming_rtp_packets_size() == 1) { + return StoreIncomingRtpPackets(stream.incoming_rtp_packets(0)); + } else if (stream.outgoing_rtp_packets_size() == 1) { + return StoreOutgoingRtpPackets(stream.outgoing_rtp_packets(0)); + } else if (stream.incoming_rtcp_packets_size() == 1) { + return StoreIncomingRtcpPackets(stream.incoming_rtcp_packets(0)); + } else if (stream.outgoing_rtcp_packets_size() == 1) { + return StoreOutgoingRtcpPackets(stream.outgoing_rtcp_packets(0)); + } else if (stream.audio_playout_events_size() == 1) { + return StoreAudioPlayoutEvent(stream.audio_playout_events(0)); + } else if (stream.begin_log_events_size() == 1) { + return StoreStartEvent(stream.begin_log_events(0)); + } else if (stream.end_log_events_size() == 1) { + return StoreStopEvent(stream.end_log_events(0)); + } else if (stream.loss_based_bwe_updates_size() == 1) { + return StoreBweLossBasedUpdate(stream.loss_based_bwe_updates(0)); + } else if (stream.delay_based_bwe_updates_size() == 1) { + return StoreBweDelayBasedUpdate(stream.delay_based_bwe_updates(0)); + } else if (stream.dtls_transport_state_events_size() == 1) { + return StoreDtlsTransportState(stream.dtls_transport_state_events(0)); + } else if (stream.dtls_writable_states_size() == 1) { + return StoreDtlsWritableState(stream.dtls_writable_states(0)); + } else if (stream.audio_network_adaptations_size() == 1) { + return StoreAudioNetworkAdaptationEvent( + stream.audio_network_adaptations(0)); + } else if (stream.probe_clusters_size() == 1) { + return StoreBweProbeClusterCreated(stream.probe_clusters(0)); + } else if (stream.probe_success_size() == 1) { + return StoreBweProbeSuccessEvent(stream.probe_success(0)); + } else if (stream.probe_failure_size() == 1) { + return StoreBweProbeFailureEvent(stream.probe_failure(0)); + } else if (stream.alr_states_size() == 1) { + return StoreAlrStateEvent(stream.alr_states(0)); + } else if (stream.route_changes_size() == 1) { + return StoreRouteChangeEvent(stream.route_changes(0)); + } else if (stream.remote_estimates_size() == 1) { + return StoreRemoteEstimateEvent(stream.remote_estimates(0)); + } else if (stream.ice_candidate_configs_size() == 1) { + return StoreIceCandidatePairConfig(stream.ice_candidate_configs(0)); + } else if (stream.ice_candidate_events_size() == 1) { + return StoreIceCandidateEvent(stream.ice_candidate_events(0)); + } else if (stream.audio_recv_stream_configs_size() == 1) { + return StoreAudioRecvConfig(stream.audio_recv_stream_configs(0)); + } else if (stream.audio_send_stream_configs_size() == 1) { + return StoreAudioSendConfig(stream.audio_send_stream_configs(0)); + } else if (stream.video_recv_stream_configs_size() == 1) { + return StoreVideoRecvConfig(stream.video_recv_stream_configs(0)); + } else if (stream.video_send_stream_configs_size() == 1) { + return StoreVideoSendConfig(stream.video_send_stream_configs(0)); + } else if (stream.generic_packets_received_size() == 1) { + return StoreGenericPacketReceivedEvent(stream.generic_packets_received(0)); + } else if (stream.generic_packets_sent_size() == 1) { + return StoreGenericPacketSentEvent(stream.generic_packets_sent(0)); + } else if (stream.generic_acks_received_size() == 1) { + return StoreGenericAckReceivedEvent(stream.generic_acks_received(0)); + } else if (stream.frame_decoded_events_size() == 1) { + return StoreFrameDecodedEvents(stream.frame_decoded_events(0)); + } else if (stream.neteq_set_minimum_delay_size() == 1) { + return StoreNetEqSetMinimumDelay(stream.neteq_set_minimum_delay(0)); + } else { + RTC_DCHECK_NOTREACHED(); + return ParseStatus::Success(); + } +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreAlrStateEvent( + const rtclog2::AlrState& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_in_alr()); + LoggedAlrStateEvent alr_event; + alr_event.timestamp = Timestamp::Millis(proto.timestamp_ms()); + alr_event.in_alr = proto.in_alr(); + + alr_state_events_.push_back(alr_event); + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreRouteChangeEvent( + const rtclog2::RouteChange& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_connected()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_overhead()); + LoggedRouteChangeEvent route_event; + route_event.timestamp = Timestamp::Millis(proto.timestamp_ms()); + route_event.connected = proto.connected(); + route_event.overhead = proto.overhead(); + + route_change_events_.push_back(route_event); + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreRemoteEstimateEvent( + const rtclog2::RemoteEstimates& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + // Base event + LoggedRemoteEstimateEvent base_event; + base_event.timestamp = Timestamp::Millis(proto.timestamp_ms()); + + absl::optional<uint64_t> base_link_capacity_lower_kbps; + if (proto.has_link_capacity_lower_kbps()) { + base_link_capacity_lower_kbps = proto.link_capacity_lower_kbps(); + base_event.link_capacity_lower = + DataRate::KilobitsPerSec(proto.link_capacity_lower_kbps()); + } + + absl::optional<uint64_t> base_link_capacity_upper_kbps; + if (proto.has_link_capacity_upper_kbps()) { + base_link_capacity_upper_kbps = proto.link_capacity_upper_kbps(); + base_event.link_capacity_upper = + DataRate::KilobitsPerSec(proto.link_capacity_upper_kbps()); + } + + remote_estimate_events_.push_back(base_event); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + auto timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // link_capacity_lower_kbps + auto link_capacity_lower_kbps_values = + DecodeDeltas(proto.link_capacity_lower_kbps_deltas(), + base_link_capacity_lower_kbps, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(link_capacity_lower_kbps_values.size(), + number_of_deltas); + + // link_capacity_upper_kbps + auto link_capacity_upper_kbps_values = + DecodeDeltas(proto.link_capacity_upper_kbps_deltas(), + base_link_capacity_upper_kbps, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(link_capacity_upper_kbps_values.size(), + number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + LoggedRemoteEstimateEvent event; + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + event.timestamp = Timestamp::Millis(*timestamp_ms_values[i]); + if (link_capacity_lower_kbps_values[i]) + event.link_capacity_lower = + DataRate::KilobitsPerSec(*link_capacity_lower_kbps_values[i]); + if (link_capacity_upper_kbps_values[i]) + event.link_capacity_upper = + DataRate::KilobitsPerSec(*link_capacity_upper_kbps_values[i]); + remote_estimate_events_.push_back(event); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreAudioPlayoutEvent( + const rtclog2::AudioPlayoutEvents& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_ssrc()); + + // Base event + audio_playout_events_[proto.local_ssrc()].emplace_back( + Timestamp::Millis(proto.timestamp_ms()), proto.local_ssrc()); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // local_ssrc + std::vector<absl::optional<uint64_t>> local_ssrc_values = DecodeDeltas( + proto.local_ssrc_deltas(), proto.local_ssrc(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(local_ssrc_values.size(), number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(local_ssrc_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(local_ssrc_values[i].value(), + std::numeric_limits<uint32_t>::max()); + + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + const uint32_t local_ssrc = + static_cast<uint32_t>(local_ssrc_values[i].value()); + audio_playout_events_[local_ssrc].emplace_back( + Timestamp::Millis(timestamp_ms), local_ssrc); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreNetEqSetMinimumDelay( + const rtclog2::NetEqSetMinimumDelay& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_remote_ssrc()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_minimum_delay_ms()); + + // Base event + neteq_set_minimum_delay_events_[proto.remote_ssrc()].emplace_back( + Timestamp::Millis(proto.timestamp_ms()), proto.remote_ssrc(), + static_cast<int>(proto.minimum_delay_ms())); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // remote_ssrc + std::vector<absl::optional<uint64_t>> remote_ssrc_values = DecodeDeltas( + proto.remote_ssrc_deltas(), proto.remote_ssrc(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(remote_ssrc_values.size(), number_of_deltas); + + // minimum_delay_ms + std::vector<absl::optional<uint64_t>> minimum_delay_ms_values = + DecodeDeltas(proto.minimum_delay_ms_deltas(), + ToUnsigned(proto.minimum_delay_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(minimum_delay_ms_values.size(), + number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(remote_ssrc_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(remote_ssrc_values[i].value(), + std::numeric_limits<uint32_t>::max()); + RTC_PARSE_CHECK_OR_RETURN(minimum_delay_ms_values[i].has_value()); + + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + const uint32_t remote_ssrc = + static_cast<uint32_t>(remote_ssrc_values[i].value()); + int minimum_delay_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(minimum_delay_ms_values[i].value(), &minimum_delay_ms)); + neteq_set_minimum_delay_events_[remote_ssrc].emplace_back( + Timestamp::Millis(timestamp_ms), remote_ssrc, minimum_delay_ms); + } + + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreIncomingRtpPackets( + const rtclog2::IncomingRtpPackets& proto) { + return StoreRtpPackets(proto, &incoming_rtp_packets_map_); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreOutgoingRtpPackets( + const rtclog2::OutgoingRtpPackets& proto) { + return StoreRtpPackets(proto, &outgoing_rtp_packets_map_); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreIncomingRtcpPackets( + const rtclog2::IncomingRtcpPackets& proto) { + return StoreRtcpPackets(proto, &incoming_rtcp_packets_, + /*remove_duplicates=*/true); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreOutgoingRtcpPackets( + const rtclog2::OutgoingRtcpPackets& proto) { + return StoreRtcpPackets(proto, &outgoing_rtcp_packets_, + /*remove_duplicates=*/false); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreStartEvent( + const rtclog2::BeginLogEvent& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_version()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_utc_time_ms()); + RTC_PARSE_CHECK_OR_RETURN_EQ(proto.version(), 2); + LoggedStartEvent start_event(Timestamp::Millis(proto.timestamp_ms()), + Timestamp::Millis(proto.utc_time_ms())); + + start_log_events_.push_back(start_event); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreStopEvent( + const rtclog2::EndLogEvent& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + LoggedStopEvent stop_event(Timestamp::Millis(proto.timestamp_ms())); + + stop_log_events_.push_back(stop_event); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreBweLossBasedUpdate( + const rtclog2::LossBasedBweUpdates& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_bitrate_bps()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_fraction_loss()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_total_packets()); + + // Base event + bwe_loss_updates_.emplace_back(Timestamp::Millis(proto.timestamp_ms()), + proto.bitrate_bps(), proto.fraction_loss(), + proto.total_packets()); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // bitrate_bps + std::vector<absl::optional<uint64_t>> bitrate_bps_values = DecodeDeltas( + proto.bitrate_bps_deltas(), proto.bitrate_bps(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(bitrate_bps_values.size(), number_of_deltas); + + // fraction_loss + std::vector<absl::optional<uint64_t>> fraction_loss_values = DecodeDeltas( + proto.fraction_loss_deltas(), proto.fraction_loss(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(fraction_loss_values.size(), number_of_deltas); + + // total_packets + std::vector<absl::optional<uint64_t>> total_packets_values = DecodeDeltas( + proto.total_packets_deltas(), proto.total_packets(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(total_packets_values.size(), number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + RTC_PARSE_CHECK_OR_RETURN(bitrate_bps_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(bitrate_bps_values[i].value(), + std::numeric_limits<uint32_t>::max()); + const uint32_t bitrate_bps = + static_cast<uint32_t>(bitrate_bps_values[i].value()); + + RTC_PARSE_CHECK_OR_RETURN(fraction_loss_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(fraction_loss_values[i].value(), + std::numeric_limits<uint32_t>::max()); + const uint32_t fraction_loss = + static_cast<uint32_t>(fraction_loss_values[i].value()); + + RTC_PARSE_CHECK_OR_RETURN(total_packets_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(total_packets_values[i].value(), + std::numeric_limits<uint32_t>::max()); + const uint32_t total_packets = + static_cast<uint32_t>(total_packets_values[i].value()); + + bwe_loss_updates_.emplace_back(Timestamp::Millis(timestamp_ms), bitrate_bps, + fraction_loss, total_packets); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreBweDelayBasedUpdate( + const rtclog2::DelayBasedBweUpdates& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_bitrate_bps()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_detector_state()); + + // Base event + const BandwidthUsage base_detector_state = + GetRuntimeDetectorState(proto.detector_state()); + bwe_delay_updates_.emplace_back(Timestamp::Millis(proto.timestamp_ms()), + proto.bitrate_bps(), base_detector_state); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // bitrate_bps + std::vector<absl::optional<uint64_t>> bitrate_bps_values = DecodeDeltas( + proto.bitrate_bps_deltas(), proto.bitrate_bps(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(bitrate_bps_values.size(), number_of_deltas); + + // detector_state + std::vector<absl::optional<uint64_t>> detector_state_values = DecodeDeltas( + proto.detector_state_deltas(), + static_cast<uint64_t>(proto.detector_state()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(detector_state_values.size(), number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + RTC_PARSE_CHECK_OR_RETURN(bitrate_bps_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(bitrate_bps_values[i].value(), + std::numeric_limits<uint32_t>::max()); + const uint32_t bitrate_bps = + static_cast<uint32_t>(bitrate_bps_values[i].value()); + + RTC_PARSE_CHECK_OR_RETURN(detector_state_values[i].has_value()); + const auto detector_state = + static_cast<rtclog2::DelayBasedBweUpdates::DetectorState>( + detector_state_values[i].value()); + + bwe_delay_updates_.emplace_back(Timestamp::Millis(timestamp_ms), + bitrate_bps, + GetRuntimeDetectorState(detector_state)); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreBweProbeClusterCreated( + const rtclog2::BweProbeCluster& proto) { + LoggedBweProbeClusterCreatedEvent probe_cluster; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + probe_cluster.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_id()); + probe_cluster.id = proto.id(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_bitrate_bps()); + probe_cluster.bitrate_bps = proto.bitrate_bps(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_min_packets()); + probe_cluster.min_packets = proto.min_packets(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_min_bytes()); + probe_cluster.min_bytes = proto.min_bytes(); + + bwe_probe_cluster_created_events_.push_back(probe_cluster); + + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreBweProbeSuccessEvent( + const rtclog2::BweProbeResultSuccess& proto) { + LoggedBweProbeSuccessEvent probe_result; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + probe_result.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_id()); + probe_result.id = proto.id(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_bitrate_bps()); + probe_result.bitrate_bps = proto.bitrate_bps(); + + bwe_probe_success_events_.push_back(probe_result); + + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreBweProbeFailureEvent( + const rtclog2::BweProbeResultFailure& proto) { + LoggedBweProbeFailureEvent probe_result; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + probe_result.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_id()); + probe_result.id = proto.id(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_failure()); + probe_result.failure_reason = GetRuntimeProbeFailureReason(proto.failure()); + + bwe_probe_failure_events_.push_back(probe_result); + + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreFrameDecodedEvents( + const rtclog2::FrameDecodedEvents& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_ssrc()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_render_time_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_width()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_height()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_codec()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_qp()); + + LoggedFrameDecoded base_frame; + base_frame.timestamp = Timestamp::Millis(proto.timestamp_ms()); + base_frame.ssrc = proto.ssrc(); + base_frame.render_time_ms = proto.render_time_ms(); + base_frame.width = proto.width(); + base_frame.height = proto.height(); + base_frame.codec = GetRuntimeCodecType(proto.codec()); + RTC_PARSE_CHECK_OR_RETURN_GE(proto.qp(), 0); + RTC_PARSE_CHECK_OR_RETURN_LE(proto.qp(), 255); + base_frame.qp = static_cast<uint8_t>(proto.qp()); + + decoded_frames_[base_frame.ssrc].push_back(base_frame); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // SSRC + std::vector<absl::optional<uint64_t>> ssrc_values = + DecodeDeltas(proto.ssrc_deltas(), proto.ssrc(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(ssrc_values.size(), number_of_deltas); + + // render_time_ms + std::vector<absl::optional<uint64_t>> render_time_ms_values = + DecodeDeltas(proto.render_time_ms_deltas(), + ToUnsigned(proto.render_time_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(render_time_ms_values.size(), number_of_deltas); + + // width + std::vector<absl::optional<uint64_t>> width_values = DecodeDeltas( + proto.width_deltas(), ToUnsigned(proto.width()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(width_values.size(), number_of_deltas); + + // height + std::vector<absl::optional<uint64_t>> height_values = DecodeDeltas( + proto.height_deltas(), ToUnsigned(proto.height()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(height_values.size(), number_of_deltas); + + // codec + std::vector<absl::optional<uint64_t>> codec_values = + DecodeDeltas(proto.codec_deltas(), static_cast<uint64_t>(proto.codec()), + number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(codec_values.size(), number_of_deltas); + + // qp + std::vector<absl::optional<uint64_t>> qp_values = + DecodeDeltas(proto.qp_deltas(), proto.qp(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(qp_values.size(), number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + LoggedFrameDecoded frame; + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + frame.timestamp = Timestamp::Millis(timestamp_ms); + + RTC_PARSE_CHECK_OR_RETURN(ssrc_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(ssrc_values[i].value(), + std::numeric_limits<uint32_t>::max()); + frame.ssrc = static_cast<uint32_t>(ssrc_values[i].value()); + + RTC_PARSE_CHECK_OR_RETURN(render_time_ms_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(render_time_ms_values[i].value(), &frame.render_time_ms)); + + RTC_PARSE_CHECK_OR_RETURN(width_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(ToSigned(width_values[i].value(), &frame.width)); + + RTC_PARSE_CHECK_OR_RETURN(height_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(height_values[i].value(), &frame.height)); + + RTC_PARSE_CHECK_OR_RETURN(codec_values[i].has_value()); + frame.codec = + GetRuntimeCodecType(static_cast<rtclog2::FrameDecodedEvents::Codec>( + codec_values[i].value())); + + RTC_PARSE_CHECK_OR_RETURN(qp_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(qp_values[i].value(), + std::numeric_limits<uint8_t>::max()); + frame.qp = static_cast<uint8_t>(qp_values[i].value()); + + decoded_frames_[frame.ssrc].push_back(frame); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreGenericAckReceivedEvent( + const rtclog2::GenericAckReceived& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_packet_number()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_acked_packet_number()); + // receive_acked_packet_time_ms is optional. + + absl::optional<int64_t> base_receive_acked_packet_time_ms; + if (proto.has_receive_acked_packet_time_ms()) { + base_receive_acked_packet_time_ms = proto.receive_acked_packet_time_ms(); + } + generic_acks_received_.push_back( + {Timestamp::Millis(proto.timestamp_ms()), proto.packet_number(), + proto.acked_packet_number(), base_receive_acked_packet_time_ms}); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // packet_number + std::vector<absl::optional<uint64_t>> packet_number_values = + DecodeDeltas(proto.packet_number_deltas(), + ToUnsigned(proto.packet_number()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(packet_number_values.size(), number_of_deltas); + + // acked_packet_number + std::vector<absl::optional<uint64_t>> acked_packet_number_values = + DecodeDeltas(proto.acked_packet_number_deltas(), + ToUnsigned(proto.acked_packet_number()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(acked_packet_number_values.size(), + number_of_deltas); + + // optional receive_acked_packet_time_ms + const absl::optional<uint64_t> unsigned_receive_acked_packet_time_ms_base = + proto.has_receive_acked_packet_time_ms() + ? absl::optional<uint64_t>( + ToUnsigned(proto.receive_acked_packet_time_ms())) + : absl::optional<uint64_t>(); + std::vector<absl::optional<uint64_t>> receive_acked_packet_time_ms_values = + DecodeDeltas(proto.receive_acked_packet_time_ms_deltas(), + unsigned_receive_acked_packet_time_ms_base, + number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(receive_acked_packet_time_ms_values.size(), + number_of_deltas); + + for (size_t i = 0; i < number_of_deltas; i++) { + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + int64_t packet_number; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(packet_number_values[i].value(), &packet_number)); + int64_t acked_packet_number; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(acked_packet_number_values[i].value(), &acked_packet_number)); + absl::optional<int64_t> receive_acked_packet_time_ms; + + if (receive_acked_packet_time_ms_values[i].has_value()) { + int64_t value; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(receive_acked_packet_time_ms_values[i].value(), &value)); + receive_acked_packet_time_ms = value; + } + generic_acks_received_.push_back({Timestamp::Millis(timestamp_ms), + packet_number, acked_packet_number, + receive_acked_packet_time_ms}); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreGenericPacketSentEvent( + const rtclog2::GenericPacketSent& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + + // Base event + RTC_PARSE_CHECK_OR_RETURN(proto.has_packet_number()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_overhead_length()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_payload_length()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_padding_length()); + + generic_packets_sent_.push_back( + {Timestamp::Millis(proto.timestamp_ms()), proto.packet_number(), + static_cast<size_t>(proto.overhead_length()), + static_cast<size_t>(proto.payload_length()), + static_cast<size_t>(proto.padding_length())}); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // packet_number + std::vector<absl::optional<uint64_t>> packet_number_values = + DecodeDeltas(proto.packet_number_deltas(), + ToUnsigned(proto.packet_number()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(packet_number_values.size(), number_of_deltas); + + std::vector<absl::optional<uint64_t>> overhead_length_values = + DecodeDeltas(proto.overhead_length_deltas(), proto.overhead_length(), + number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(overhead_length_values.size(), number_of_deltas); + + std::vector<absl::optional<uint64_t>> payload_length_values = DecodeDeltas( + proto.payload_length_deltas(), proto.payload_length(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(payload_length_values.size(), number_of_deltas); + + std::vector<absl::optional<uint64_t>> padding_length_values = DecodeDeltas( + proto.padding_length_deltas(), proto.padding_length(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(padding_length_values.size(), number_of_deltas); + + for (size_t i = 0; i < number_of_deltas; i++) { + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + int64_t packet_number; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(packet_number_values[i].value(), &packet_number)); + RTC_PARSE_CHECK_OR_RETURN(overhead_length_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(payload_length_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(padding_length_values[i].has_value()); + generic_packets_sent_.push_back( + {Timestamp::Millis(timestamp_ms), packet_number, + static_cast<size_t>(overhead_length_values[i].value()), + static_cast<size_t>(payload_length_values[i].value()), + static_cast<size_t>(padding_length_values[i].value())}); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus +ParsedRtcEventLog::StoreGenericPacketReceivedEvent( + const rtclog2::GenericPacketReceived& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + + // Base event + RTC_PARSE_CHECK_OR_RETURN(proto.has_packet_number()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_packet_length()); + + generic_packets_received_.push_back({Timestamp::Millis(proto.timestamp_ms()), + proto.packet_number(), + proto.packet_length()}); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // packet_number + std::vector<absl::optional<uint64_t>> packet_number_values = + DecodeDeltas(proto.packet_number_deltas(), + ToUnsigned(proto.packet_number()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(packet_number_values.size(), number_of_deltas); + + std::vector<absl::optional<uint64_t>> packet_length_values = DecodeDeltas( + proto.packet_length_deltas(), proto.packet_length(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(packet_length_values.size(), number_of_deltas); + + for (size_t i = 0; i < number_of_deltas; i++) { + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + int64_t packet_number; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(packet_number_values[i].value(), &packet_number)); + RTC_PARSE_CHECK_OR_RETURN_LE(packet_length_values[i].value(), + std::numeric_limits<int32_t>::max()); + int32_t packet_length = + static_cast<int32_t>(packet_length_values[i].value()); + generic_packets_received_.push_back( + {Timestamp::Millis(timestamp_ms), packet_number, packet_length}); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus +ParsedRtcEventLog::StoreAudioNetworkAdaptationEvent( + const rtclog2::AudioNetworkAdaptations& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + + // Base event + { + AudioEncoderRuntimeConfig runtime_config; + if (proto.has_bitrate_bps()) { + runtime_config.bitrate_bps = proto.bitrate_bps(); + } + if (proto.has_frame_length_ms()) { + runtime_config.frame_length_ms = proto.frame_length_ms(); + } + if (proto.has_uplink_packet_loss_fraction()) { + float uplink_packet_loss_fraction; + RTC_PARSE_CHECK_OR_RETURN(ParsePacketLossFractionFromProtoFormat( + proto.uplink_packet_loss_fraction(), &uplink_packet_loss_fraction)); + runtime_config.uplink_packet_loss_fraction = uplink_packet_loss_fraction; + } + if (proto.has_enable_fec()) { + runtime_config.enable_fec = proto.enable_fec(); + } + if (proto.has_enable_dtx()) { + runtime_config.enable_dtx = proto.enable_dtx(); + } + if (proto.has_num_channels()) { + // Note: Encoding N as N-1 only done for `num_channels_deltas`. + runtime_config.num_channels = proto.num_channels(); + } + audio_network_adaptation_events_.emplace_back( + Timestamp::Millis(proto.timestamp_ms()), runtime_config); + } + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // bitrate_bps + const absl::optional<uint64_t> unsigned_base_bitrate_bps = + proto.has_bitrate_bps() + ? absl::optional<uint64_t>(ToUnsigned(proto.bitrate_bps())) + : absl::optional<uint64_t>(); + std::vector<absl::optional<uint64_t>> bitrate_bps_values = DecodeDeltas( + proto.bitrate_bps_deltas(), unsigned_base_bitrate_bps, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(bitrate_bps_values.size(), number_of_deltas); + + // frame_length_ms + const absl::optional<uint64_t> unsigned_base_frame_length_ms = + proto.has_frame_length_ms() + ? absl::optional<uint64_t>(ToUnsigned(proto.frame_length_ms())) + : absl::optional<uint64_t>(); + std::vector<absl::optional<uint64_t>> frame_length_ms_values = + DecodeDeltas(proto.frame_length_ms_deltas(), + unsigned_base_frame_length_ms, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(frame_length_ms_values.size(), number_of_deltas); + + // uplink_packet_loss_fraction + const absl::optional<uint64_t> uplink_packet_loss_fraction = + proto.has_uplink_packet_loss_fraction() + ? absl::optional<uint64_t>(proto.uplink_packet_loss_fraction()) + : absl::optional<uint64_t>(); + std::vector<absl::optional<uint64_t>> uplink_packet_loss_fraction_values = + DecodeDeltas(proto.uplink_packet_loss_fraction_deltas(), + uplink_packet_loss_fraction, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(uplink_packet_loss_fraction_values.size(), + number_of_deltas); + + // enable_fec + const absl::optional<uint64_t> enable_fec = + proto.has_enable_fec() ? absl::optional<uint64_t>(proto.enable_fec()) + : absl::optional<uint64_t>(); + std::vector<absl::optional<uint64_t>> enable_fec_values = + DecodeDeltas(proto.enable_fec_deltas(), enable_fec, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(enable_fec_values.size(), number_of_deltas); + + // enable_dtx + const absl::optional<uint64_t> enable_dtx = + proto.has_enable_dtx() ? absl::optional<uint64_t>(proto.enable_dtx()) + : absl::optional<uint64_t>(); + std::vector<absl::optional<uint64_t>> enable_dtx_values = + DecodeDeltas(proto.enable_dtx_deltas(), enable_dtx, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(enable_dtx_values.size(), number_of_deltas); + + // num_channels + // Note: For delta encoding, all num_channel values, including the base, + // were shifted down by one, but in the base event, they were not. + // We likewise shift the base event down by one, to get the same base as + // encoding had, but then shift all of the values (except the base) back up + // to their original value. + absl::optional<uint64_t> shifted_base_num_channels; + if (proto.has_num_channels()) { + shifted_base_num_channels = + absl::optional<uint64_t>(proto.num_channels() - 1); + } + std::vector<absl::optional<uint64_t>> num_channels_values = DecodeDeltas( + proto.num_channels_deltas(), shifted_base_num_channels, number_of_deltas); + for (size_t i = 0; i < num_channels_values.size(); ++i) { + if (num_channels_values[i].has_value()) { + num_channels_values[i] = num_channels_values[i].value() + 1; + } + } + RTC_PARSE_CHECK_OR_RETURN_EQ(num_channels_values.size(), number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + AudioEncoderRuntimeConfig runtime_config; + if (bitrate_bps_values[i].has_value()) { + int signed_bitrate_bps; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(bitrate_bps_values[i].value(), &signed_bitrate_bps)); + runtime_config.bitrate_bps = signed_bitrate_bps; + } + if (frame_length_ms_values[i].has_value()) { + int signed_frame_length_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(frame_length_ms_values[i].value(), &signed_frame_length_ms)); + runtime_config.frame_length_ms = signed_frame_length_ms; + } + if (uplink_packet_loss_fraction_values[i].has_value()) { + float uplink_packet_loss_fraction2; + RTC_PARSE_CHECK_OR_RETURN(ParsePacketLossFractionFromProtoFormat( + rtc::checked_cast<uint32_t>( + uplink_packet_loss_fraction_values[i].value()), + &uplink_packet_loss_fraction2)); + runtime_config.uplink_packet_loss_fraction = uplink_packet_loss_fraction2; + } + if (enable_fec_values[i].has_value()) { + runtime_config.enable_fec = + rtc::checked_cast<bool>(enable_fec_values[i].value()); + } + if (enable_dtx_values[i].has_value()) { + runtime_config.enable_dtx = + rtc::checked_cast<bool>(enable_dtx_values[i].value()); + } + if (num_channels_values[i].has_value()) { + runtime_config.num_channels = + rtc::checked_cast<size_t>(num_channels_values[i].value()); + } + audio_network_adaptation_events_.emplace_back( + Timestamp::Millis(timestamp_ms), runtime_config); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreDtlsTransportState( + const rtclog2::DtlsTransportStateEvent& proto) { + LoggedDtlsTransportState dtls_state; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + dtls_state.timestamp = Timestamp::Millis(proto.timestamp_ms()); + + RTC_PARSE_CHECK_OR_RETURN(proto.has_dtls_transport_state()); + dtls_state.dtls_transport_state = + GetRuntimeDtlsTransportState(proto.dtls_transport_state()); + + dtls_transport_states_.push_back(dtls_state); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreDtlsWritableState( + const rtclog2::DtlsWritableState& proto) { + LoggedDtlsWritableState dtls_writable_state; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + dtls_writable_state.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_writable()); + dtls_writable_state.writable = proto.writable(); + + dtls_writable_states_.push_back(dtls_writable_state); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreIceCandidatePairConfig( + const rtclog2::IceCandidatePairConfig& proto) { + LoggedIceCandidatePairConfig ice_config; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + ice_config.timestamp = Timestamp::Millis(proto.timestamp_ms()); + + RTC_PARSE_CHECK_OR_RETURN(proto.has_config_type()); + ice_config.type = GetRuntimeIceCandidatePairConfigType(proto.config_type()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_candidate_pair_id()); + ice_config.candidate_pair_id = proto.candidate_pair_id(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_candidate_type()); + ice_config.local_candidate_type = + GetRuntimeIceCandidateType(proto.local_candidate_type()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_relay_protocol()); + ice_config.local_relay_protocol = + GetRuntimeIceCandidatePairProtocol(proto.local_relay_protocol()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_network_type()); + ice_config.local_network_type = + GetRuntimeIceCandidateNetworkType(proto.local_network_type()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_address_family()); + ice_config.local_address_family = + GetRuntimeIceCandidatePairAddressFamily(proto.local_address_family()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_remote_candidate_type()); + ice_config.remote_candidate_type = + GetRuntimeIceCandidateType(proto.remote_candidate_type()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_remote_address_family()); + ice_config.remote_address_family = + GetRuntimeIceCandidatePairAddressFamily(proto.remote_address_family()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_candidate_pair_protocol()); + ice_config.candidate_pair_protocol = + GetRuntimeIceCandidatePairProtocol(proto.candidate_pair_protocol()); + + ice_candidate_pair_configs_.push_back(ice_config); + + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreIceCandidateEvent( + const rtclog2::IceCandidatePairEvent& proto) { + LoggedIceCandidatePairEvent ice_event; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + ice_event.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_event_type()); + ice_event.type = GetRuntimeIceCandidatePairEventType(proto.event_type()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_candidate_pair_id()); + ice_event.candidate_pair_id = proto.candidate_pair_id(); + // TODO(zstein): Make the transaction_id field required once all old versions + // of the log (which don't have the field) are obsolete. + ice_event.transaction_id = + proto.has_transaction_id() ? proto.transaction_id() : 0; + + ice_candidate_pair_events_.push_back(ice_event); + + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreVideoRecvConfig( + const rtclog2::VideoRecvStreamConfig& proto) { + LoggedVideoRecvConfig stream; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + stream.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_remote_ssrc()); + stream.config.remote_ssrc = proto.remote_ssrc(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_ssrc()); + stream.config.local_ssrc = proto.local_ssrc(); + if (proto.has_rtx_ssrc()) { + stream.config.rtx_ssrc = proto.rtx_ssrc(); + } + if (proto.has_header_extensions()) { + stream.config.rtp_extensions = + GetRuntimeRtpHeaderExtensionConfig(proto.header_extensions()); + } + video_recv_configs_.push_back(stream); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreVideoSendConfig( + const rtclog2::VideoSendStreamConfig& proto) { + LoggedVideoSendConfig stream; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + stream.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_ssrc()); + stream.config.local_ssrc = proto.ssrc(); + if (proto.has_rtx_ssrc()) { + stream.config.rtx_ssrc = proto.rtx_ssrc(); + } + if (proto.has_header_extensions()) { + stream.config.rtp_extensions = + GetRuntimeRtpHeaderExtensionConfig(proto.header_extensions()); + } + video_send_configs_.push_back(stream); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreAudioRecvConfig( + const rtclog2::AudioRecvStreamConfig& proto) { + LoggedAudioRecvConfig stream; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + stream.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_remote_ssrc()); + stream.config.remote_ssrc = proto.remote_ssrc(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_ssrc()); + stream.config.local_ssrc = proto.local_ssrc(); + if (proto.has_header_extensions()) { + stream.config.rtp_extensions = + GetRuntimeRtpHeaderExtensionConfig(proto.header_extensions()); + } + audio_recv_configs_.push_back(stream); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreAudioSendConfig( + const rtclog2::AudioSendStreamConfig& proto) { + LoggedAudioSendConfig stream; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + stream.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_ssrc()); + stream.config.local_ssrc = proto.ssrc(); + if (proto.has_header_extensions()) { + stream.config.rtp_extensions = + GetRuntimeRtpHeaderExtensionConfig(proto.header_extensions()); + } + audio_send_configs_.push_back(stream); + return ParseStatus::Success(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.h b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.h new file mode 100644 index 0000000000..9ad96274d3 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.h @@ -0,0 +1,941 @@ +/* + * Copyright 2019 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. + */ +#ifndef LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_PARSER_H_ +#define LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_PARSER_H_ + +#include <iterator> +#include <limits> +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "absl/base/attributes.h" +#include "absl/strings/string_view.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "call/video_receive_stream.h" +#include "call/video_send_stream.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.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_begin_log.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_dtls_transport_state.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" +#include "logging/rtc_event_log/events/rtc_event_end_log.h" +#include "logging/rtc_event_log/events/rtc_event_frame_decoded.h" +#include "logging/rtc_event_log/events/rtc_event_generic_ack_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_sent.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" +#include "logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.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_remote_estimate.h" +#include "logging/rtc_event_log/events/rtc_event_route_change.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 "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" +#include "rtc_base/ignore_wundef.h" + +// Files generated at build-time by the protobuf compiler. +RTC_PUSH_IGNORING_WUNDEF() +#ifdef WEBRTC_ANDROID_PLATFORM_BUILD +#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log.pb.h" +#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log2.pb.h" +#else +#include "logging/rtc_event_log/rtc_event_log.pb.h" +#include "logging/rtc_event_log/rtc_event_log2.pb.h" +#endif +RTC_POP_IGNORING_WUNDEF() + +namespace webrtc { + +enum PacketDirection { kIncomingPacket = 0, kOutgoingPacket }; + +enum class LoggedMediaType : uint8_t { kUnknown, kAudio, kVideo }; + +struct LoggedPacketInfo { + LoggedPacketInfo(const LoggedRtpPacket& rtp, + LoggedMediaType media_type, + bool rtx, + Timestamp capture_time); + LoggedPacketInfo(const LoggedPacketInfo&); + ~LoggedPacketInfo(); + int64_t log_time_ms() const { return log_packet_time.ms(); } + int64_t log_time_us() const { return log_packet_time.us(); } + uint32_t ssrc; + uint16_t stream_seq_no; + uint16_t size; + uint16_t payload_size; + uint16_t padding_size; + uint16_t overhead = 0; + uint8_t payload_type; + LoggedMediaType media_type = LoggedMediaType::kUnknown; + bool rtx = false; + bool marker_bit = false; + bool has_transport_seq_no = false; + bool last_in_feedback = false; + uint16_t transport_seq_no = 0; + // The RTP header timestamp unwrapped and converted from tick count to seconds + // based timestamp. + Timestamp capture_time; + // The time the packet was logged. This is the receive time for incoming + // packets and send time for outgoing. + Timestamp log_packet_time; + // Send time as reported by abs-send-time extension, For outgoing packets this + // corresponds to log_packet_time, but might be measured using another clock. + Timestamp reported_send_time; + // The receive time that was reported in feedback. For incoming packets this + // corresponds to log_packet_time, but might be measured using another clock. + // PlusInfinity indicates that the packet was lost. + Timestamp reported_recv_time = Timestamp::MinusInfinity(); + // The time feedback message was logged. This is the feedback send time for + // incoming packets and feedback receive time for outgoing. + // PlusInfinity indicates that feedback was expected but not received. + Timestamp log_feedback_time = Timestamp::MinusInfinity(); + // The delay betweeen receiving an RTP packet and sending feedback for + // incoming packets. For outgoing packets we don't know the feedback send + // time, and this is instead calculated as the difference in reported receive + // time between this packet and the last packet in the same feedback message. + TimeDelta feedback_hold_duration = TimeDelta::MinusInfinity(); +}; + +struct InferredRouteChangeEvent { + int64_t log_time_ms() const { return log_time.ms(); } + int64_t log_time_us() const { return log_time.us(); } + uint32_t route_id; + Timestamp log_time = Timestamp::MinusInfinity(); + uint16_t send_overhead; + uint16_t return_overhead; +}; + +enum class LoggedIceEventType { + kAdded, + kUpdated, + kDestroyed, + kSelected, + kCheckSent, + kCheckReceived, + kCheckResponseSent, + kCheckResponseReceived, +}; + +struct LoggedIceEvent { + uint32_t candidate_pair_id; + Timestamp log_time; + LoggedIceEventType event_type; +}; + +// This class is used to process lists of LoggedRtpPacketIncoming +// and LoggedRtpPacketOutgoing without duplicating the code. +// TODO(terelius): Remove this class. Instead use e.g. a vector of pointers +// to LoggedRtpPacket or templatize the surrounding code. +template <typename T> +class DereferencingVector { + public: + template <bool IsConst> + class DereferencingIterator { + public: + // Standard iterator traits. + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = typename std::conditional_t<IsConst, const T*, T*>; + using reference = typename std::conditional_t<IsConst, const T&, T&>; + using iterator_category = std::bidirectional_iterator_tag; + + using representation = + typename std::conditional_t<IsConst, const T* const*, T**>; + + explicit DereferencingIterator(representation ptr) : ptr_(ptr) {} + + DereferencingIterator(const DereferencingIterator& other) + : ptr_(other.ptr_) {} + DereferencingIterator(const DereferencingIterator&& other) + : ptr_(other.ptr_) {} + ~DereferencingIterator() = default; + + DereferencingIterator& operator=(const DereferencingIterator& other) { + ptr_ = other.ptr_; + return *this; + } + DereferencingIterator& operator=(const DereferencingIterator&& other) { + ptr_ = other.ptr_; + return *this; + } + + bool operator==(const DereferencingIterator& other) const { + return ptr_ == other.ptr_; + } + bool operator!=(const DereferencingIterator& other) const { + return ptr_ != other.ptr_; + } + + DereferencingIterator& operator++() { + ++ptr_; + return *this; + } + DereferencingIterator& operator--() { + --ptr_; + return *this; + } + DereferencingIterator operator++(int) { + DereferencingIterator iter_copy(ptr_); + ++ptr_; + return iter_copy; + } + DereferencingIterator operator--(int) { + DereferencingIterator iter_copy(ptr_); + --ptr_; + return iter_copy; + } + + template <bool _IsConst = IsConst> + std::enable_if_t<!_IsConst, reference> operator*() { + return **ptr_; + } + + template <bool _IsConst = IsConst> + std::enable_if_t<_IsConst, reference> operator*() const { + return **ptr_; + } + + template <bool _IsConst = IsConst> + std::enable_if_t<!_IsConst, pointer> operator->() { + return *ptr_; + } + + template <bool _IsConst = IsConst> + std::enable_if_t<_IsConst, pointer> operator->() const { + return *ptr_; + } + + private: + representation ptr_; + }; + + using value_type = T; + using reference = value_type&; + using const_reference = const value_type&; + + using iterator = DereferencingIterator<false>; + using const_iterator = DereferencingIterator<true>; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + iterator begin() { return iterator(elems_.data()); } + iterator end() { return iterator(elems_.data() + elems_.size()); } + + const_iterator begin() const { return const_iterator(elems_.data()); } + const_iterator end() const { + return const_iterator(elems_.data() + elems_.size()); + } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + size_t size() const { return elems_.size(); } + + bool empty() const { return elems_.empty(); } + + T& operator[](size_t i) { + RTC_DCHECK_LT(i, elems_.size()); + return *elems_[i]; + } + + const T& operator[](size_t i) const { + RTC_DCHECK_LT(i, elems_.size()); + return *elems_[i]; + } + + void push_back(T* elem) { + RTC_DCHECK(elem != nullptr); + elems_.push_back(elem); + } + + private: + std::vector<T*> elems_; +}; + +// Conversion functions for version 2 of the wire format. +BandwidthUsage GetRuntimeDetectorState( + rtclog2::DelayBasedBweUpdates::DetectorState detector_state); + +ProbeFailureReason GetRuntimeProbeFailureReason( + rtclog2::BweProbeResultFailure::FailureReason failure); + +DtlsTransportState GetRuntimeDtlsTransportState( + rtclog2::DtlsTransportStateEvent::DtlsTransportState state); + +IceCandidatePairConfigType GetRuntimeIceCandidatePairConfigType( + rtclog2::IceCandidatePairConfig::IceCandidatePairConfigType type); + +IceCandidateType GetRuntimeIceCandidateType( + rtclog2::IceCandidatePairConfig::IceCandidateType type); + +IceCandidatePairProtocol GetRuntimeIceCandidatePairProtocol( + rtclog2::IceCandidatePairConfig::Protocol protocol); + +IceCandidatePairAddressFamily GetRuntimeIceCandidatePairAddressFamily( + rtclog2::IceCandidatePairConfig::AddressFamily address_family); + +IceCandidateNetworkType GetRuntimeIceCandidateNetworkType( + rtclog2::IceCandidatePairConfig::NetworkType network_type); + +IceCandidatePairEventType GetRuntimeIceCandidatePairEventType( + rtclog2::IceCandidatePairEvent::IceCandidatePairEventType type); + +std::vector<RtpExtension> GetRuntimeRtpHeaderExtensionConfig( + const rtclog2::RtpHeaderExtensionConfig& proto_header_extensions); +// End of conversion functions. + +class ParsedRtcEventLog { + public: + enum class MediaType { ANY, AUDIO, VIDEO, DATA }; + enum class UnconfiguredHeaderExtensions { + kDontParse, + kAttemptWebrtcDefaultConfig + }; + + using ParseStatus = RtcEventLogParseStatus; + + template <typename T> + using ParseStatusOr = RtcEventLogParseStatusOr<T>; + + struct LoggedRtpStreamIncoming { + LoggedRtpStreamIncoming(); + LoggedRtpStreamIncoming(const LoggedRtpStreamIncoming&); + ~LoggedRtpStreamIncoming(); + uint32_t ssrc; + std::vector<LoggedRtpPacketIncoming> incoming_packets; + }; + + struct LoggedRtpStreamOutgoing { + LoggedRtpStreamOutgoing(); + LoggedRtpStreamOutgoing(const LoggedRtpStreamOutgoing&); + ~LoggedRtpStreamOutgoing(); + uint32_t ssrc; + std::vector<LoggedRtpPacketOutgoing> outgoing_packets; + }; + + struct LoggedRtpStreamView { + LoggedRtpStreamView(uint32_t ssrc, + const std::vector<LoggedRtpPacketIncoming>& packets); + LoggedRtpStreamView(uint32_t ssrc, + const std::vector<LoggedRtpPacketOutgoing>& packets); + LoggedRtpStreamView(const LoggedRtpStreamView&); + uint32_t ssrc; + DereferencingVector<const LoggedRtpPacket> packet_view; + }; + + class LogSegment { + public: + LogSegment(int64_t start_time_us, int64_t stop_time_us) + : start_time_us_(start_time_us), stop_time_us_(stop_time_us) {} + int64_t start_time_ms() const { return start_time_us_ / 1000; } + int64_t start_time_us() const { return start_time_us_; } + int64_t stop_time_ms() const { return stop_time_us_ / 1000; } + int64_t stop_time_us() const { return stop_time_us_; } + + private: + int64_t start_time_us_; + int64_t stop_time_us_; + }; + + static webrtc::RtpHeaderExtensionMap GetDefaultHeaderExtensionMap(); + + explicit ParsedRtcEventLog( + UnconfiguredHeaderExtensions parse_unconfigured_header_extensions = + UnconfiguredHeaderExtensions::kDontParse, + bool allow_incomplete_log = false); + + ~ParsedRtcEventLog(); + + // Clears previously parsed events and resets the ParsedRtcEventLogNew to an + // empty state. + void Clear(); + + // Reads an RtcEventLog file and returns success if parsing was successful. + ParseStatus ParseFile(absl::string_view file_name); + + // Reads an RtcEventLog from a string and returns success if successful. + ParseStatus ParseString(absl::string_view s); + + // Reads an RtcEventLog from an string and returns success if successful. + ParseStatus ParseStream(absl::string_view s); + + MediaType GetMediaType(uint32_t ssrc, PacketDirection direction) const; + + // Configured SSRCs. + const std::set<uint32_t>& incoming_rtx_ssrcs() const { + return incoming_rtx_ssrcs_; + } + + const std::set<uint32_t>& incoming_video_ssrcs() const { + return incoming_video_ssrcs_; + } + + const std::set<uint32_t>& incoming_audio_ssrcs() const { + return incoming_audio_ssrcs_; + } + + const std::set<uint32_t>& outgoing_rtx_ssrcs() const { + return outgoing_rtx_ssrcs_; + } + + const std::set<uint32_t>& outgoing_video_ssrcs() const { + return outgoing_video_ssrcs_; + } + + const std::set<uint32_t>& outgoing_audio_ssrcs() const { + return outgoing_audio_ssrcs_; + } + + // Stream configurations. + const std::vector<LoggedAudioRecvConfig>& audio_recv_configs() const { + return audio_recv_configs_; + } + + const std::vector<LoggedAudioSendConfig>& audio_send_configs() const { + return audio_send_configs_; + } + + const std::vector<LoggedVideoRecvConfig>& video_recv_configs() const { + return video_recv_configs_; + } + + const std::vector<LoggedVideoSendConfig>& video_send_configs() const { + return video_send_configs_; + } + + // Beginning and end of log segments. + const std::vector<LoggedStartEvent>& start_log_events() const { + return start_log_events_; + } + + const std::vector<LoggedStopEvent>& stop_log_events() const { + return stop_log_events_; + } + + const std::vector<LoggedAlrStateEvent>& alr_state_events() const { + return alr_state_events_; + } + + // Audio + const std::map<uint32_t, std::vector<LoggedAudioPlayoutEvent>>& + audio_playout_events() const { + return audio_playout_events_; + } + + const std::map<uint32_t, std::vector<LoggedNetEqSetMinimumDelayEvent>>& + neteq_set_minimum_delay_events() const { + return neteq_set_minimum_delay_events_; + } + + const std::vector<LoggedAudioNetworkAdaptationEvent>& + audio_network_adaptation_events() const { + return audio_network_adaptation_events_; + } + + // Bandwidth estimation + const std::vector<LoggedBweProbeClusterCreatedEvent>& + bwe_probe_cluster_created_events() const { + return bwe_probe_cluster_created_events_; + } + + const std::vector<LoggedBweProbeFailureEvent>& bwe_probe_failure_events() + const { + return bwe_probe_failure_events_; + } + + const std::vector<LoggedBweProbeSuccessEvent>& bwe_probe_success_events() + const { + return bwe_probe_success_events_; + } + + const std::vector<LoggedBweDelayBasedUpdate>& bwe_delay_updates() const { + return bwe_delay_updates_; + } + + const std::vector<LoggedBweLossBasedUpdate>& bwe_loss_updates() const { + return bwe_loss_updates_; + } + + // DTLS + const std::vector<LoggedDtlsTransportState>& dtls_transport_states() const { + return dtls_transport_states_; + } + + const std::vector<LoggedDtlsWritableState>& dtls_writable_states() const { + return dtls_writable_states_; + } + + // ICE events + const std::vector<LoggedIceCandidatePairConfig>& ice_candidate_pair_configs() + const { + return ice_candidate_pair_configs_; + } + + const std::vector<LoggedIceCandidatePairEvent>& ice_candidate_pair_events() + const { + return ice_candidate_pair_events_; + } + + const std::vector<LoggedRouteChangeEvent>& route_change_events() const { + return route_change_events_; + } + + const std::vector<LoggedRemoteEstimateEvent>& remote_estimate_events() const { + return remote_estimate_events_; + } + + // RTP + const std::vector<LoggedRtpStreamIncoming>& incoming_rtp_packets_by_ssrc() + const { + return incoming_rtp_packets_by_ssrc_; + } + + const std::vector<LoggedRtpStreamOutgoing>& outgoing_rtp_packets_by_ssrc() + const { + return outgoing_rtp_packets_by_ssrc_; + } + + const std::vector<LoggedRtpStreamView>& rtp_packets_by_ssrc( + PacketDirection direction) const { + if (direction == kIncomingPacket) + return incoming_rtp_packet_views_by_ssrc_; + else + return outgoing_rtp_packet_views_by_ssrc_; + } + + // RTCP + const std::vector<LoggedRtcpPacketIncoming>& incoming_rtcp_packets() const { + return incoming_rtcp_packets_; + } + + const std::vector<LoggedRtcpPacketOutgoing>& outgoing_rtcp_packets() const { + return outgoing_rtcp_packets_; + } + + const std::vector<LoggedRtcpPacketReceiverReport>& receiver_reports( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_rr_; + } else { + return outgoing_rr_; + } + } + + const std::vector<LoggedRtcpPacketSenderReport>& sender_reports( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_sr_; + } else { + return outgoing_sr_; + } + } + + const std::vector<LoggedRtcpPacketExtendedReports>& extended_reports( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_xr_; + } else { + return outgoing_xr_; + } + } + + const std::vector<LoggedRtcpPacketNack>& nacks( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_nack_; + } else { + return outgoing_nack_; + } + } + + const std::vector<LoggedRtcpPacketRemb>& rembs( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_remb_; + } else { + return outgoing_remb_; + } + } + + const std::vector<LoggedRtcpPacketFir>& firs( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_fir_; + } else { + return outgoing_fir_; + } + } + + const std::vector<LoggedRtcpPacketPli>& plis( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_pli_; + } else { + return outgoing_pli_; + } + } + + const std::vector<LoggedRtcpPacketBye>& byes( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_bye_; + } else { + return outgoing_bye_; + } + } + + const std::vector<LoggedRtcpPacketTransportFeedback>& transport_feedbacks( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_transport_feedback_; + } else { + return outgoing_transport_feedback_; + } + } + + const std::vector<LoggedRtcpPacketLossNotification>& loss_notifications( + PacketDirection direction) { + if (direction == kIncomingPacket) { + return incoming_loss_notification_; + } else { + return outgoing_loss_notification_; + } + } + + const std::vector<LoggedGenericPacketReceived>& generic_packets_received() + const { + return generic_packets_received_; + } + const std::vector<LoggedGenericPacketSent>& generic_packets_sent() const { + return generic_packets_sent_; + } + + const std::vector<LoggedGenericAckReceived>& generic_acks_received() const { + return generic_acks_received_; + } + + // Media + const std::map<uint32_t, std::vector<LoggedFrameDecoded>>& decoded_frames() + const { + return decoded_frames_; + } + + Timestamp first_timestamp() const { return first_timestamp_; } + Timestamp last_timestamp() const { return last_timestamp_; } + + const LogSegment& first_log_segment() const { return first_log_segment_; } + + std::vector<LoggedPacketInfo> GetPacketInfos(PacketDirection direction) const; + std::vector<LoggedPacketInfo> GetIncomingPacketInfos() const { + return GetPacketInfos(kIncomingPacket); + } + std::vector<LoggedPacketInfo> GetOutgoingPacketInfos() const { + return GetPacketInfos(kOutgoingPacket); + } + std::vector<LoggedIceCandidatePairConfig> GetIceCandidates() const; + std::vector<LoggedIceEvent> GetIceEvents() const; + + std::vector<InferredRouteChangeEvent> GetRouteChanges() const; + + private: + ABSL_MUST_USE_RESULT ParseStatus ParseStreamInternal(absl::string_view s); + ABSL_MUST_USE_RESULT ParseStatus ParseStreamInternalV3(absl::string_view s); + + ABSL_MUST_USE_RESULT ParseStatus + StoreParsedLegacyEvent(const rtclog::Event& event); + + template <typename T> + void StoreFirstAndLastTimestamp(const std::vector<T>& v); + + // Returns: a pointer to a header extensions map acquired from parsing + // corresponding Audio/Video Sender/Receiver config events. + // Warning: if the same SSRC is reused by both video and audio streams during + // call, extensions maps may be incorrect (the last one would be returned). + const RtpHeaderExtensionMap* GetRtpHeaderExtensionMap(bool incoming, + uint32_t ssrc); + + // Reads packet, direction and packet length from the RTCP event at `index`, + // and stores the values in the corresponding output parameters. + // Each output parameter can be set to nullptr if that value isn't needed. + // NB: The packet must have space for at least IP_PACKET_SIZE bytes. + ParseStatus GetRtcpPacket(const rtclog::Event& event, + PacketDirection* incoming, + std::vector<uint8_t>* packet) const; + + ParseStatusOr<rtclog::StreamConfig> GetVideoReceiveConfig( + const rtclog::Event& event) const; + ParseStatusOr<rtclog::StreamConfig> GetVideoSendConfig( + const rtclog::Event& event) const; + ParseStatusOr<rtclog::StreamConfig> GetAudioReceiveConfig( + const rtclog::Event& event) const; + ParseStatusOr<rtclog::StreamConfig> GetAudioSendConfig( + const rtclog::Event& event) const; + + ParsedRtcEventLog::ParseStatusOr<LoggedAudioPlayoutEvent> GetAudioPlayout( + const rtclog::Event& event) const; + + ParsedRtcEventLog::ParseStatusOr<LoggedBweLossBasedUpdate> + GetLossBasedBweUpdate(const rtclog::Event& event) const; + + ParsedRtcEventLog::ParseStatusOr<LoggedBweDelayBasedUpdate> + GetDelayBasedBweUpdate(const rtclog::Event& event) const; + + ParsedRtcEventLog::ParseStatusOr<LoggedAudioNetworkAdaptationEvent> + GetAudioNetworkAdaptation(const rtclog::Event& event) const; + + ParsedRtcEventLog::ParseStatusOr<LoggedBweProbeClusterCreatedEvent> + GetBweProbeClusterCreated(const rtclog::Event& event) const; + + ParsedRtcEventLog::ParseStatusOr<LoggedBweProbeFailureEvent> + GetBweProbeFailure(const rtclog::Event& event) const; + + ParsedRtcEventLog::ParseStatusOr<LoggedBweProbeSuccessEvent> + GetBweProbeSuccess(const rtclog::Event& event) const; + + ParsedRtcEventLog::ParseStatusOr<LoggedAlrStateEvent> GetAlrState( + const rtclog::Event& event) const; + + ParsedRtcEventLog::ParseStatusOr<LoggedIceCandidatePairConfig> + GetIceCandidatePairConfig(const rtclog::Event& event) const; + + ParsedRtcEventLog::ParseStatusOr<LoggedIceCandidatePairEvent> + GetIceCandidatePairEvent(const rtclog::Event& event) const; + + ParsedRtcEventLog::ParseStatusOr<LoggedRemoteEstimateEvent> + GetRemoteEstimateEvent(const rtclog::Event& event) const; + + // Parsing functions for new format. + ParseStatus StoreAlrStateEvent(const rtclog2::AlrState& proto); + ParseStatus StoreAudioNetworkAdaptationEvent( + const rtclog2::AudioNetworkAdaptations& proto); + ParseStatus StoreAudioPlayoutEvent(const rtclog2::AudioPlayoutEvents& proto); + ParseStatus StoreAudioRecvConfig(const rtclog2::AudioRecvStreamConfig& proto); + ParseStatus StoreAudioSendConfig(const rtclog2::AudioSendStreamConfig& proto); + ParseStatus StoreBweDelayBasedUpdate( + const rtclog2::DelayBasedBweUpdates& proto); + ParseStatus StoreBweLossBasedUpdate( + const rtclog2::LossBasedBweUpdates& proto); + ParseStatus StoreBweProbeClusterCreated( + const rtclog2::BweProbeCluster& proto); + ParseStatus StoreBweProbeFailureEvent( + const rtclog2::BweProbeResultFailure& proto); + ParseStatus StoreBweProbeSuccessEvent( + const rtclog2::BweProbeResultSuccess& proto); + ParseStatus StoreDtlsTransportState( + const rtclog2::DtlsTransportStateEvent& proto); + ParseStatus StoreDtlsWritableState(const rtclog2::DtlsWritableState& proto); + ParsedRtcEventLog::ParseStatus StoreFrameDecodedEvents( + const rtclog2::FrameDecodedEvents& proto); + ParseStatus StoreGenericAckReceivedEvent( + const rtclog2::GenericAckReceived& proto); + ParseStatus StoreGenericPacketReceivedEvent( + const rtclog2::GenericPacketReceived& proto); + ParseStatus StoreGenericPacketSentEvent( + const rtclog2::GenericPacketSent& proto); + ParseStatus StoreIceCandidateEvent( + const rtclog2::IceCandidatePairEvent& proto); + ParseStatus StoreIceCandidatePairConfig( + const rtclog2::IceCandidatePairConfig& proto); + ParseStatus StoreIncomingRtcpPackets( + const rtclog2::IncomingRtcpPackets& proto); + ParseStatus StoreIncomingRtpPackets(const rtclog2::IncomingRtpPackets& proto); + ParseStatus StoreNetEqSetMinimumDelay( + const rtclog2::NetEqSetMinimumDelay& proto); + ParseStatus StoreOutgoingRtcpPackets( + const rtclog2::OutgoingRtcpPackets& proto); + ParseStatus StoreOutgoingRtpPackets(const rtclog2::OutgoingRtpPackets& proto); + ParseStatus StoreParsedNewFormatEvent(const rtclog2::EventStream& event); + ParseStatus StoreRouteChangeEvent(const rtclog2::RouteChange& proto); + ParseStatus StoreRemoteEstimateEvent(const rtclog2::RemoteEstimates& proto); + ParseStatus StoreStartEvent(const rtclog2::BeginLogEvent& proto); + ParseStatus StoreStopEvent(const rtclog2::EndLogEvent& proto); + ParseStatus StoreVideoRecvConfig(const rtclog2::VideoRecvStreamConfig& proto); + ParseStatus StoreVideoSendConfig(const rtclog2::VideoSendStreamConfig& proto); + // End of new parsing functions. + + struct Stream { + Stream(uint32_t ssrc, + MediaType media_type, + PacketDirection direction, + webrtc::RtpHeaderExtensionMap map) + : ssrc(ssrc), + media_type(media_type), + direction(direction), + rtp_extensions_map(map) {} + uint32_t ssrc; + MediaType media_type; + PacketDirection direction; + webrtc::RtpHeaderExtensionMap rtp_extensions_map; + }; + + const UnconfiguredHeaderExtensions parse_unconfigured_header_extensions_; + const bool allow_incomplete_logs_; + + // Make a default extension map for streams without configuration information. + // TODO(ivoc): Once configuration of audio streams is stored in the event log, + // this can be removed. Tracking bug: webrtc:6399 + RtpHeaderExtensionMap default_extension_map_; + + // Tracks what each stream is configured for. Note that a single SSRC can be + // in several sets. For example, the SSRC used for sending video over RTX + // will appear in both video_ssrcs_ and rtx_ssrcs_. In the unlikely case that + // an SSRC is reconfigured to a different media type mid-call, it will also + // appear in multiple sets. + std::set<uint32_t> incoming_rtx_ssrcs_; + std::set<uint32_t> incoming_video_ssrcs_; + std::set<uint32_t> incoming_audio_ssrcs_; + std::set<uint32_t> outgoing_rtx_ssrcs_; + std::set<uint32_t> outgoing_video_ssrcs_; + std::set<uint32_t> outgoing_audio_ssrcs_; + + // Maps an SSRC to the parsed RTP headers in that stream. Header extensions + // are parsed if the stream has been configured. This is only used for + // grouping the events by SSRC during parsing; the events are moved to + // incoming_rtp_packets_by_ssrc_ once the parsing is done. + std::map<uint32_t, std::vector<LoggedRtpPacketIncoming>> + incoming_rtp_packets_map_; + std::map<uint32_t, std::vector<LoggedRtpPacketOutgoing>> + outgoing_rtp_packets_map_; + + // RTP headers. + std::vector<LoggedRtpStreamIncoming> incoming_rtp_packets_by_ssrc_; + std::vector<LoggedRtpStreamOutgoing> outgoing_rtp_packets_by_ssrc_; + std::vector<LoggedRtpStreamView> incoming_rtp_packet_views_by_ssrc_; + std::vector<LoggedRtpStreamView> outgoing_rtp_packet_views_by_ssrc_; + + // Raw RTCP packets. + std::vector<LoggedRtcpPacketIncoming> incoming_rtcp_packets_; + std::vector<LoggedRtcpPacketOutgoing> outgoing_rtcp_packets_; + + // Parsed RTCP messages. Currently not separated based on SSRC. + std::vector<LoggedRtcpPacketReceiverReport> incoming_rr_; + std::vector<LoggedRtcpPacketReceiverReport> outgoing_rr_; + std::vector<LoggedRtcpPacketSenderReport> incoming_sr_; + std::vector<LoggedRtcpPacketSenderReport> outgoing_sr_; + std::vector<LoggedRtcpPacketExtendedReports> incoming_xr_; + std::vector<LoggedRtcpPacketExtendedReports> outgoing_xr_; + std::vector<LoggedRtcpPacketNack> incoming_nack_; + std::vector<LoggedRtcpPacketNack> outgoing_nack_; + std::vector<LoggedRtcpPacketRemb> incoming_remb_; + std::vector<LoggedRtcpPacketRemb> outgoing_remb_; + std::vector<LoggedRtcpPacketFir> incoming_fir_; + std::vector<LoggedRtcpPacketFir> outgoing_fir_; + std::vector<LoggedRtcpPacketPli> incoming_pli_; + std::vector<LoggedRtcpPacketPli> outgoing_pli_; + std::vector<LoggedRtcpPacketBye> incoming_bye_; + std::vector<LoggedRtcpPacketBye> outgoing_bye_; + std::vector<LoggedRtcpPacketTransportFeedback> incoming_transport_feedback_; + std::vector<LoggedRtcpPacketTransportFeedback> outgoing_transport_feedback_; + std::vector<LoggedRtcpPacketLossNotification> incoming_loss_notification_; + std::vector<LoggedRtcpPacketLossNotification> outgoing_loss_notification_; + + std::vector<LoggedStartEvent> start_log_events_; + std::vector<LoggedStopEvent> stop_log_events_; + + std::vector<LoggedAlrStateEvent> alr_state_events_; + + std::map<uint32_t, std::vector<LoggedAudioPlayoutEvent>> + audio_playout_events_; + std::map<uint32_t, std::vector<LoggedNetEqSetMinimumDelayEvent>> + neteq_set_minimum_delay_events_; + + std::vector<LoggedAudioNetworkAdaptationEvent> + audio_network_adaptation_events_; + + std::vector<LoggedBweProbeClusterCreatedEvent> + bwe_probe_cluster_created_events_; + + std::vector<LoggedBweProbeFailureEvent> bwe_probe_failure_events_; + std::vector<LoggedBweProbeSuccessEvent> bwe_probe_success_events_; + + std::vector<LoggedBweDelayBasedUpdate> bwe_delay_updates_; + std::vector<LoggedBweLossBasedUpdate> bwe_loss_updates_; + + std::vector<LoggedDtlsTransportState> dtls_transport_states_; + std::vector<LoggedDtlsWritableState> dtls_writable_states_; + + std::map<uint32_t, std::vector<LoggedFrameDecoded>> decoded_frames_; + + std::vector<LoggedIceCandidatePairConfig> ice_candidate_pair_configs_; + std::vector<LoggedIceCandidatePairEvent> ice_candidate_pair_events_; + + std::vector<LoggedAudioRecvConfig> audio_recv_configs_; + std::vector<LoggedAudioSendConfig> audio_send_configs_; + std::vector<LoggedVideoRecvConfig> video_recv_configs_; + std::vector<LoggedVideoSendConfig> video_send_configs_; + + std::vector<LoggedGenericPacketReceived> generic_packets_received_; + std::vector<LoggedGenericPacketSent> generic_packets_sent_; + std::vector<LoggedGenericAckReceived> generic_acks_received_; + + std::vector<LoggedRouteChangeEvent> route_change_events_; + std::vector<LoggedRemoteEstimateEvent> remote_estimate_events_; + + std::vector<uint8_t> last_incoming_rtcp_packet_; + + Timestamp first_timestamp_ = Timestamp::PlusInfinity(); + Timestamp last_timestamp_ = Timestamp::MinusInfinity(); + + LogSegment first_log_segment_ = + LogSegment(0, std::numeric_limits<int64_t>::max()); + + // The extension maps are mutable to allow us to insert the default + // configuration when parsing an RTP header for an unconfigured stream. + // TODO(terelius): This is only used for the legacy format. Remove once we've + // fully transitioned to the new format. + mutable std::map<uint32_t, webrtc::RtpHeaderExtensionMap> + incoming_rtp_extensions_maps_; + mutable std::map<uint32_t, webrtc::RtpHeaderExtensionMap> + outgoing_rtp_extensions_maps_; +}; + +struct MatchedSendArrivalTimes { + static constexpr int64_t kNotReceived = -1; + + MatchedSendArrivalTimes(int64_t fb, int64_t tx, int64_t rx, int64_t ps) + : feedback_arrival_time_ms(fb), + send_time_ms(tx), + arrival_time_ms(rx), + payload_size(ps) {} + + int64_t feedback_arrival_time_ms; + int64_t send_time_ms; + int64_t arrival_time_ms; // kNotReceived for lost packets. + int64_t payload_size; +}; +const std::vector<MatchedSendArrivalTimes> GetNetworkTrace( + const ParsedRtcEventLog& parsed_log); + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_PARSER_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest.cc new file mode 100644 index 0000000000..3730a080dd --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest.cc @@ -0,0 +1,1073 @@ +/* + * Copyright (c) 2015 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 "api/rtc_event_log/rtc_event_log.h" + +#include <algorithm> +#include <limits> +#include <map> +#include <memory> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +#include "api/rtc_event_log/rtc_event_log_factory.h" +#include "api/task_queue/default_task_queue_factory.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_dtls_transport_state.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" +#include "logging/rtc_event_log/events/rtc_event_generic_ack_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_sent.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 "logging/rtc_event_log/rtc_stream_config.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "rtc_base/checks.h" +#include "rtc_base/fake_clock.h" +#include "rtc_base/random.h" +#include "test/gtest.h" +#include "test/logging/memory_log_writer.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { + +namespace { + +struct EventCounts { + size_t audio_send_streams = 0; + size_t audio_recv_streams = 0; + size_t video_send_streams = 0; + size_t video_recv_streams = 0; + size_t alr_states = 0; + size_t route_changes = 0; + size_t audio_playouts = 0; + size_t ana_configs = 0; + size_t bwe_loss_events = 0; + size_t bwe_delay_events = 0; + size_t dtls_transport_states = 0; + size_t dtls_writable_states = 0; + size_t frame_decoded_events = 0; + size_t probe_creations = 0; + size_t probe_successes = 0; + size_t probe_failures = 0; + size_t ice_configs = 0; + size_t ice_events = 0; + size_t incoming_rtp_packets = 0; + size_t outgoing_rtp_packets = 0; + size_t incoming_rtcp_packets = 0; + size_t outgoing_rtcp_packets = 0; + size_t generic_packets_sent = 0; + size_t generic_packets_received = 0; + size_t generic_acks_received = 0; + + size_t total_nonconfig_events() const { + return alr_states + route_changes + audio_playouts + ana_configs + + bwe_loss_events + bwe_delay_events + dtls_transport_states + + dtls_writable_states + frame_decoded_events + probe_creations + + probe_successes + probe_failures + ice_configs + ice_events + + incoming_rtp_packets + outgoing_rtp_packets + incoming_rtcp_packets + + outgoing_rtcp_packets + generic_packets_sent + + generic_packets_received + generic_acks_received; + } + + size_t total_config_events() const { + return audio_send_streams + audio_recv_streams + video_send_streams + + video_recv_streams; + } + + size_t total_events() const { + return total_nonconfig_events() + total_config_events(); + } +}; + +class RtcEventLogSession + : public ::testing::TestWithParam< + std::tuple<uint64_t, int64_t, RtcEventLog::EncodingType>> { + public: + RtcEventLogSession() + : seed_(std::get<0>(GetParam())), + prng_(seed_), + output_period_ms_(std::get<1>(GetParam())), + encoding_type_(std::get<2>(GetParam())), + gen_(seed_ * 880001UL), + verifier_(encoding_type_), + log_storage_(), + log_output_factory_(log_storage_.CreateFactory()) { + clock_.SetTime(Timestamp::Micros(prng_.Rand<uint32_t>())); + // Find the name of the current test, in order to use it as a temporary + // filename. + auto test_info = ::testing::UnitTest::GetInstance()->current_test_info(); + std::string test_name = + std::string(test_info->test_case_name()) + "_" + test_info->name(); + std::replace(test_name.begin(), test_name.end(), '/', '_'); + temp_filename_ = test::OutputPath() + test_name; + } + + // Create and buffer the config events and `num_events_before_log_start` + // randomized non-config events. Then call StartLogging and finally create and + // write the remaining non-config events. + void WriteLog(EventCounts count, size_t num_events_before_log_start); + void ReadAndVerifyLog(); + + bool IsNewFormat() { + return encoding_type_ == RtcEventLog::EncodingType::NewFormat; + } + + private: + void WriteAudioRecvConfigs(size_t audio_recv_streams, RtcEventLog* event_log); + void WriteAudioSendConfigs(size_t audio_send_streams, RtcEventLog* event_log); + void WriteVideoRecvConfigs(size_t video_recv_streams, RtcEventLog* event_log); + void WriteVideoSendConfigs(size_t video_send_streams, RtcEventLog* event_log); + + std::vector<std::pair<uint32_t, RtpHeaderExtensionMap>> incoming_extensions_; + std::vector<std::pair<uint32_t, RtpHeaderExtensionMap>> outgoing_extensions_; + + // Config events. + std::vector<std::unique_ptr<RtcEventAudioSendStreamConfig>> + audio_send_config_list_; + std::vector<std::unique_ptr<RtcEventAudioReceiveStreamConfig>> + audio_recv_config_list_; + std::vector<std::unique_ptr<RtcEventVideoSendStreamConfig>> + video_send_config_list_; + std::vector<std::unique_ptr<RtcEventVideoReceiveStreamConfig>> + video_recv_config_list_; + + // Regular events. + std::vector<std::unique_ptr<RtcEventAlrState>> alr_state_list_; + std::map<uint32_t, std::vector<std::unique_ptr<RtcEventAudioPlayout>>> + audio_playout_map_; // Groups audio by SSRC. + std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> + ana_configs_list_; + std::vector<std::unique_ptr<RtcEventBweUpdateDelayBased>> bwe_delay_list_; + std::vector<std::unique_ptr<RtcEventBweUpdateLossBased>> bwe_loss_list_; + std::vector<std::unique_ptr<RtcEventDtlsTransportState>> + dtls_transport_state_list_; + std::vector<std::unique_ptr<RtcEventDtlsWritableState>> + dtls_writable_state_list_; + std::map<uint32_t, std::vector<std::unique_ptr<RtcEventFrameDecoded>>> + frame_decoded_event_map_; + std::vector<std::unique_ptr<RtcEventGenericAckReceived>> + generic_acks_received_; + std::vector<std::unique_ptr<RtcEventGenericPacketReceived>> + generic_packets_received_; + std::vector<std::unique_ptr<RtcEventGenericPacketSent>> generic_packets_sent_; + std::vector<std::unique_ptr<RtcEventIceCandidatePair>> ice_event_list_; + std::vector<std::unique_ptr<RtcEventIceCandidatePairConfig>> ice_config_list_; + std::vector<std::unique_ptr<RtcEventProbeClusterCreated>> + probe_creation_list_; + std::vector<std::unique_ptr<RtcEventProbeResultFailure>> probe_failure_list_; + std::vector<std::unique_ptr<RtcEventProbeResultSuccess>> probe_success_list_; + std::vector<std::unique_ptr<RtcEventRouteChange>> route_change_list_; + std::vector<std::unique_ptr<RtcEventRemoteEstimate>> remote_estimate_list_; + std::vector<std::unique_ptr<RtcEventRtcpPacketIncoming>> incoming_rtcp_list_; + std::vector<std::unique_ptr<RtcEventRtcpPacketOutgoing>> outgoing_rtcp_list_; + std::map<uint32_t, std::vector<std::unique_ptr<RtcEventRtpPacketIncoming>>> + incoming_rtp_map_; // Groups incoming RTP by SSRC. + std::map<uint32_t, std::vector<std::unique_ptr<RtcEventRtpPacketOutgoing>>> + outgoing_rtp_map_; // Groups outgoing RTP by SSRC. + + int64_t start_time_us_; + int64_t utc_start_time_us_; + int64_t stop_time_us_; + + int64_t first_timestamp_ms_ = std::numeric_limits<int64_t>::max(); + int64_t last_timestamp_ms_ = std::numeric_limits<int64_t>::min(); + + const uint64_t seed_; + Random prng_; + const int64_t output_period_ms_; + const RtcEventLog::EncodingType encoding_type_; + test::EventGenerator gen_; + test::EventVerifier verifier_; + rtc::ScopedFakeClock clock_; + std::string temp_filename_; + MemoryLogStorage log_storage_; + std::unique_ptr<LogWriterFactoryInterface> log_output_factory_; +}; + +bool SsrcUsed( + uint32_t ssrc, + const std::vector<std::pair<uint32_t, RtpHeaderExtensionMap>>& streams) { + for (const auto& kv : streams) { + if (kv.first == ssrc) + return true; + } + return false; +} + +void RtcEventLogSession::WriteAudioRecvConfigs(size_t audio_recv_streams, + RtcEventLog* event_log) { + RTC_CHECK(event_log != nullptr); + uint32_t ssrc; + for (size_t i = 0; i < audio_recv_streams; i++) { + clock_.AdvanceTime(TimeDelta::Millis(prng_.Rand(20))); + do { + ssrc = prng_.Rand<uint32_t>(); + } while (SsrcUsed(ssrc, incoming_extensions_)); + RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap( + /*configure_all=*/false, + /*excluded_extensions=*/{RtpDependencyDescriptorExtension::kId}); + incoming_extensions_.emplace_back(ssrc, extensions); + auto event = gen_.NewAudioReceiveStreamConfig(ssrc, extensions); + event_log->Log(event->Copy()); + audio_recv_config_list_.push_back(std::move(event)); + } +} + +void RtcEventLogSession::WriteAudioSendConfigs(size_t audio_send_streams, + RtcEventLog* event_log) { + RTC_CHECK(event_log != nullptr); + uint32_t ssrc; + for (size_t i = 0; i < audio_send_streams; i++) { + clock_.AdvanceTime(TimeDelta::Millis(prng_.Rand(20))); + do { + ssrc = prng_.Rand<uint32_t>(); + } while (SsrcUsed(ssrc, outgoing_extensions_)); + RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap( + /*configure_all=*/false, + /*excluded_extensions=*/{RtpDependencyDescriptorExtension::kId}); + outgoing_extensions_.emplace_back(ssrc, extensions); + auto event = gen_.NewAudioSendStreamConfig(ssrc, extensions); + event_log->Log(event->Copy()); + audio_send_config_list_.push_back(std::move(event)); + } +} + +void RtcEventLogSession::WriteVideoRecvConfigs(size_t video_recv_streams, + RtcEventLog* event_log) { + RTC_CHECK(event_log != nullptr); + RTC_CHECK_GE(video_recv_streams, 1); + + // Force least one stream to use all header extensions, to ensure + // (statistically) that every extension is tested in packet creation. + RtpHeaderExtensionMap all_extensions = + ParsedRtcEventLog::GetDefaultHeaderExtensionMap(); + + if (std::get<2>(GetParam()) == RtcEventLog::EncodingType::Legacy) { + all_extensions.Deregister(RtpDependencyDescriptorExtension::Uri()); + } + + clock_.AdvanceTime(TimeDelta::Millis(prng_.Rand(20))); + uint32_t ssrc = prng_.Rand<uint32_t>(); + incoming_extensions_.emplace_back(ssrc, all_extensions); + auto event = gen_.NewVideoReceiveStreamConfig(ssrc, all_extensions); + event_log->Log(event->Copy()); + video_recv_config_list_.push_back(std::move(event)); + for (size_t i = 1; i < video_recv_streams; i++) { + clock_.AdvanceTime(TimeDelta::Millis(prng_.Rand(20))); + do { + ssrc = prng_.Rand<uint32_t>(); + } while (SsrcUsed(ssrc, incoming_extensions_)); + std::vector<RTPExtensionType> excluded_extensions; + if (std::get<2>(GetParam()) == RtcEventLog::EncodingType::Legacy) { + excluded_extensions.push_back(RtpDependencyDescriptorExtension::kId); + } + RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap( + /*configure_all=*/false, excluded_extensions); + incoming_extensions_.emplace_back(ssrc, extensions); + auto new_event = gen_.NewVideoReceiveStreamConfig(ssrc, extensions); + event_log->Log(new_event->Copy()); + video_recv_config_list_.push_back(std::move(new_event)); + } +} + +void RtcEventLogSession::WriteVideoSendConfigs(size_t video_send_streams, + RtcEventLog* event_log) { + RTC_CHECK(event_log != nullptr); + RTC_CHECK_GE(video_send_streams, 1); + + // Force least one stream to use all header extensions, to ensure + // (statistically) that every extension is tested in packet creation. + RtpHeaderExtensionMap all_extensions = + ParsedRtcEventLog::GetDefaultHeaderExtensionMap(); + + if (std::get<2>(GetParam()) == RtcEventLog::EncodingType::Legacy) { + all_extensions.Deregister(RtpDependencyDescriptorExtension::Uri()); + } + + clock_.AdvanceTime(TimeDelta::Millis(prng_.Rand(20))); + uint32_t ssrc = prng_.Rand<uint32_t>(); + outgoing_extensions_.emplace_back(ssrc, all_extensions); + auto event = gen_.NewVideoSendStreamConfig(ssrc, all_extensions); + event_log->Log(event->Copy()); + video_send_config_list_.push_back(std::move(event)); + for (size_t i = 1; i < video_send_streams; i++) { + clock_.AdvanceTime(TimeDelta::Millis(prng_.Rand(20))); + do { + ssrc = prng_.Rand<uint32_t>(); + } while (SsrcUsed(ssrc, outgoing_extensions_)); + std::vector<RTPExtensionType> excluded_extensions; + if (std::get<2>(GetParam()) == RtcEventLog::EncodingType::Legacy) { + excluded_extensions.push_back(RtpDependencyDescriptorExtension::kId); + } + RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap( + /*configure_all=*/false, excluded_extensions); + outgoing_extensions_.emplace_back(ssrc, extensions); + auto event = gen_.NewVideoSendStreamConfig(ssrc, extensions); + event_log->Log(event->Copy()); + video_send_config_list_.push_back(std::move(event)); + } +} + +void RtcEventLogSession::WriteLog(EventCounts count, + size_t num_events_before_start) { + // TODO(terelius): Allow test to run with either a real or a fake clock_. + // Maybe always use the ScopedFakeClock, but conditionally SleepMs()? + + auto task_queue_factory = CreateDefaultTaskQueueFactory(); + RtcEventLogFactory rtc_event_log_factory(task_queue_factory.get()); + // The log will be flushed to output when the event_log goes out of scope. + std::unique_ptr<RtcEventLog> event_log = + rtc_event_log_factory.CreateRtcEventLog(encoding_type_); + + // We can't send or receive packets without configured streams. + RTC_CHECK_GE(count.video_recv_streams, 1); + RTC_CHECK_GE(count.video_send_streams, 1); + + WriteAudioRecvConfigs(count.audio_recv_streams, event_log.get()); + WriteAudioSendConfigs(count.audio_send_streams, event_log.get()); + WriteVideoRecvConfigs(count.video_recv_streams, event_log.get()); + WriteVideoSendConfigs(count.video_send_streams, event_log.get()); + + size_t remaining_events = count.total_nonconfig_events(); + ASSERT_LE(num_events_before_start, remaining_events); + size_t remaining_events_at_start = remaining_events - num_events_before_start; + for (; remaining_events > 0; remaining_events--) { + if (remaining_events == remaining_events_at_start) { + clock_.AdvanceTime(TimeDelta::Millis(prng_.Rand(20))); + event_log->StartLogging(log_output_factory_->Create(temp_filename_), + output_period_ms_); + start_time_us_ = rtc::TimeMicros(); + utc_start_time_us_ = rtc::TimeUTCMicros(); + } + + clock_.AdvanceTime(TimeDelta::Millis(prng_.Rand(20))); + size_t selection = prng_.Rand(remaining_events - 1); + first_timestamp_ms_ = std::min(first_timestamp_ms_, rtc::TimeMillis()); + last_timestamp_ms_ = std::max(last_timestamp_ms_, rtc::TimeMillis()); + + if (selection < count.alr_states) { + auto event = gen_.NewAlrState(); + event_log->Log(event->Copy()); + alr_state_list_.push_back(std::move(event)); + count.alr_states--; + continue; + } + selection -= count.alr_states; + + if (selection < count.route_changes) { + auto event = gen_.NewRouteChange(); + event_log->Log(event->Copy()); + route_change_list_.push_back(std::move(event)); + count.route_changes--; + continue; + } + selection -= count.route_changes; + + if (selection < count.audio_playouts) { + size_t stream = prng_.Rand(incoming_extensions_.size() - 1); + // This might be a video SSRC, but the parser does not use the config. + uint32_t ssrc = incoming_extensions_[stream].first; + auto event = gen_.NewAudioPlayout(ssrc); + event_log->Log(event->Copy()); + audio_playout_map_[ssrc].push_back(std::move(event)); + count.audio_playouts--; + continue; + } + selection -= count.audio_playouts; + + if (selection < count.ana_configs) { + auto event = gen_.NewAudioNetworkAdaptation(); + event_log->Log(event->Copy()); + ana_configs_list_.push_back(std::move(event)); + count.ana_configs--; + continue; + } + selection -= count.ana_configs; + + if (selection < count.bwe_loss_events) { + auto event = gen_.NewBweUpdateLossBased(); + event_log->Log(event->Copy()); + bwe_loss_list_.push_back(std::move(event)); + count.bwe_loss_events--; + continue; + } + selection -= count.bwe_loss_events; + + if (selection < count.bwe_delay_events) { + auto event = gen_.NewBweUpdateDelayBased(); + event_log->Log(event->Copy()); + bwe_delay_list_.push_back(std::move(event)); + count.bwe_delay_events--; + continue; + } + selection -= count.bwe_delay_events; + + if (selection < count.probe_creations) { + auto event = gen_.NewProbeClusterCreated(); + event_log->Log(event->Copy()); + probe_creation_list_.push_back(std::move(event)); + count.probe_creations--; + continue; + } + selection -= count.probe_creations; + + if (selection < count.probe_successes) { + auto event = gen_.NewProbeResultSuccess(); + event_log->Log(event->Copy()); + probe_success_list_.push_back(std::move(event)); + count.probe_successes--; + continue; + } + selection -= count.probe_successes; + + if (selection < count.probe_failures) { + auto event = gen_.NewProbeResultFailure(); + event_log->Log(event->Copy()); + probe_failure_list_.push_back(std::move(event)); + count.probe_failures--; + continue; + } + selection -= count.probe_failures; + + if (selection < count.dtls_transport_states) { + auto event = gen_.NewDtlsTransportState(); + event_log->Log(event->Copy()); + dtls_transport_state_list_.push_back(std::move(event)); + count.dtls_transport_states--; + continue; + } + selection -= count.dtls_transport_states; + + if (selection < count.dtls_writable_states) { + auto event = gen_.NewDtlsWritableState(); + event_log->Log(event->Copy()); + dtls_writable_state_list_.push_back(std::move(event)); + count.dtls_writable_states--; + continue; + } + selection -= count.dtls_writable_states; + + if (selection < count.frame_decoded_events) { + size_t stream = prng_.Rand(incoming_extensions_.size() - 1); + // This might be an audio SSRC, but that won't affect the parser. + uint32_t ssrc = incoming_extensions_[stream].first; + auto event = gen_.NewFrameDecodedEvent(ssrc); + event_log->Log(event->Copy()); + frame_decoded_event_map_[ssrc].push_back(std::move(event)); + count.frame_decoded_events--; + continue; + } + selection -= count.frame_decoded_events; + + if (selection < count.ice_configs) { + auto event = gen_.NewIceCandidatePairConfig(); + event_log->Log(event->Copy()); + ice_config_list_.push_back(std::move(event)); + count.ice_configs--; + continue; + } + selection -= count.ice_configs; + + if (selection < count.ice_events) { + auto event = gen_.NewIceCandidatePair(); + event_log->Log(event->Copy()); + ice_event_list_.push_back(std::move(event)); + count.ice_events--; + continue; + } + selection -= count.ice_events; + + if (selection < count.incoming_rtp_packets) { + size_t stream = prng_.Rand(incoming_extensions_.size() - 1); + uint32_t ssrc = incoming_extensions_[stream].first; + auto event = + gen_.NewRtpPacketIncoming(ssrc, incoming_extensions_[stream].second); + event_log->Log(event->Copy()); + incoming_rtp_map_[ssrc].push_back(std::move(event)); + count.incoming_rtp_packets--; + continue; + } + selection -= count.incoming_rtp_packets; + + if (selection < count.outgoing_rtp_packets) { + size_t stream = prng_.Rand(outgoing_extensions_.size() - 1); + uint32_t ssrc = outgoing_extensions_[stream].first; + auto event = + gen_.NewRtpPacketOutgoing(ssrc, outgoing_extensions_[stream].second); + event_log->Log(event->Copy()); + outgoing_rtp_map_[ssrc].push_back(std::move(event)); + count.outgoing_rtp_packets--; + continue; + } + selection -= count.outgoing_rtp_packets; + + if (selection < count.incoming_rtcp_packets) { + auto event = gen_.NewRtcpPacketIncoming(); + event_log->Log(event->Copy()); + incoming_rtcp_list_.push_back(std::move(event)); + count.incoming_rtcp_packets--; + continue; + } + selection -= count.incoming_rtcp_packets; + + if (selection < count.outgoing_rtcp_packets) { + auto event = gen_.NewRtcpPacketOutgoing(); + event_log->Log(event->Copy()); + outgoing_rtcp_list_.push_back(std::move(event)); + count.outgoing_rtcp_packets--; + continue; + } + selection -= count.outgoing_rtcp_packets; + + if (selection < count.generic_packets_sent) { + auto event = gen_.NewGenericPacketSent(); + generic_packets_sent_.push_back(event->Copy()); + event_log->Log(std::move(event)); + count.generic_packets_sent--; + continue; + } + selection -= count.generic_packets_sent; + + if (selection < count.generic_packets_received) { + auto event = gen_.NewGenericPacketReceived(); + generic_packets_received_.push_back(event->Copy()); + event_log->Log(std::move(event)); + count.generic_packets_received--; + continue; + } + selection -= count.generic_packets_received; + + if (selection < count.generic_acks_received) { + auto event = gen_.NewGenericAckReceived(); + generic_acks_received_.push_back(event->Copy()); + event_log->Log(std::move(event)); + count.generic_acks_received--; + continue; + } + selection -= count.generic_acks_received; + + RTC_DCHECK_NOTREACHED(); + } + + event_log->StopLogging(); + stop_time_us_ = rtc::TimeMicros(); + + ASSERT_EQ(count.total_nonconfig_events(), static_cast<size_t>(0)); +} + +// Read the log and verify that what we read back from the event log is the +// same as what we wrote down. +void RtcEventLogSession::ReadAndVerifyLog() { + // Read the generated log from memory. + ParsedRtcEventLog parsed_log; + auto it = log_storage_.logs().find(temp_filename_); + ASSERT_TRUE(it != log_storage_.logs().end()); + ASSERT_TRUE(parsed_log.ParseString(it->second).ok()); + + // Start and stop events. + auto& parsed_start_log_events = parsed_log.start_log_events(); + ASSERT_EQ(parsed_start_log_events.size(), static_cast<size_t>(1)); + verifier_.VerifyLoggedStartEvent(start_time_us_, utc_start_time_us_, + parsed_start_log_events[0]); + + auto& parsed_stop_log_events = parsed_log.stop_log_events(); + ASSERT_EQ(parsed_stop_log_events.size(), static_cast<size_t>(1)); + verifier_.VerifyLoggedStopEvent(stop_time_us_, parsed_stop_log_events[0]); + + auto& parsed_alr_state_events = parsed_log.alr_state_events(); + ASSERT_EQ(parsed_alr_state_events.size(), alr_state_list_.size()); + for (size_t i = 0; i < parsed_alr_state_events.size(); i++) { + verifier_.VerifyLoggedAlrStateEvent(*alr_state_list_[i], + parsed_alr_state_events[i]); + } + auto& parsed_route_change_events = parsed_log.route_change_events(); + ASSERT_EQ(parsed_route_change_events.size(), route_change_list_.size()); + for (size_t i = 0; i < parsed_route_change_events.size(); i++) { + verifier_.VerifyLoggedRouteChangeEvent(*route_change_list_[i], + parsed_route_change_events[i]); + } + + const auto& parsed_audio_playout_map = parsed_log.audio_playout_events(); + ASSERT_EQ(parsed_audio_playout_map.size(), audio_playout_map_.size()); + for (const auto& kv : parsed_audio_playout_map) { + uint32_t ssrc = kv.first; + const auto& parsed_audio_playout_stream = kv.second; + const auto& audio_playout_stream = audio_playout_map_[ssrc]; + ASSERT_EQ(parsed_audio_playout_stream.size(), audio_playout_stream.size()); + for (size_t i = 0; i < audio_playout_stream.size(); i++) { + verifier_.VerifyLoggedAudioPlayoutEvent(*audio_playout_stream[i], + parsed_audio_playout_stream[i]); + } + } + + auto& parsed_audio_network_adaptation_events = + parsed_log.audio_network_adaptation_events(); + ASSERT_EQ(parsed_audio_network_adaptation_events.size(), + ana_configs_list_.size()); + for (size_t i = 0; i < parsed_audio_network_adaptation_events.size(); i++) { + verifier_.VerifyLoggedAudioNetworkAdaptationEvent( + *ana_configs_list_[i], parsed_audio_network_adaptation_events[i]); + } + + auto& parsed_bwe_delay_updates = parsed_log.bwe_delay_updates(); + ASSERT_EQ(parsed_bwe_delay_updates.size(), bwe_delay_list_.size()); + for (size_t i = 0; i < parsed_bwe_delay_updates.size(); i++) { + verifier_.VerifyLoggedBweDelayBasedUpdate(*bwe_delay_list_[i], + parsed_bwe_delay_updates[i]); + } + + auto& parsed_bwe_loss_updates = parsed_log.bwe_loss_updates(); + ASSERT_EQ(parsed_bwe_loss_updates.size(), bwe_loss_list_.size()); + for (size_t i = 0; i < parsed_bwe_loss_updates.size(); i++) { + verifier_.VerifyLoggedBweLossBasedUpdate(*bwe_loss_list_[i], + parsed_bwe_loss_updates[i]); + } + + auto& parsed_bwe_probe_cluster_created_events = + parsed_log.bwe_probe_cluster_created_events(); + ASSERT_EQ(parsed_bwe_probe_cluster_created_events.size(), + probe_creation_list_.size()); + for (size_t i = 0; i < parsed_bwe_probe_cluster_created_events.size(); i++) { + verifier_.VerifyLoggedBweProbeClusterCreatedEvent( + *probe_creation_list_[i], parsed_bwe_probe_cluster_created_events[i]); + } + + auto& parsed_bwe_probe_failure_events = parsed_log.bwe_probe_failure_events(); + ASSERT_EQ(parsed_bwe_probe_failure_events.size(), probe_failure_list_.size()); + for (size_t i = 0; i < parsed_bwe_probe_failure_events.size(); i++) { + verifier_.VerifyLoggedBweProbeFailureEvent( + *probe_failure_list_[i], parsed_bwe_probe_failure_events[i]); + } + + auto& parsed_bwe_probe_success_events = parsed_log.bwe_probe_success_events(); + ASSERT_EQ(parsed_bwe_probe_success_events.size(), probe_success_list_.size()); + for (size_t i = 0; i < parsed_bwe_probe_success_events.size(); i++) { + verifier_.VerifyLoggedBweProbeSuccessEvent( + *probe_success_list_[i], parsed_bwe_probe_success_events[i]); + } + + auto& parsed_dtls_transport_states = parsed_log.dtls_transport_states(); + ASSERT_EQ(parsed_dtls_transport_states.size(), + dtls_transport_state_list_.size()); + for (size_t i = 0; i < parsed_dtls_transport_states.size(); i++) { + verifier_.VerifyLoggedDtlsTransportState(*dtls_transport_state_list_[i], + parsed_dtls_transport_states[i]); + } + + auto& parsed_dtls_writable_states = parsed_log.dtls_writable_states(); + ASSERT_EQ(parsed_dtls_writable_states.size(), + dtls_writable_state_list_.size()); + for (size_t i = 0; i < parsed_dtls_writable_states.size(); i++) { + verifier_.VerifyLoggedDtlsWritableState(*dtls_writable_state_list_[i], + parsed_dtls_writable_states[i]); + } + + const auto& parsed_frame_decoded_map = parsed_log.decoded_frames(); + ASSERT_EQ(parsed_frame_decoded_map.size(), frame_decoded_event_map_.size()); + for (const auto& kv : parsed_frame_decoded_map) { + uint32_t ssrc = kv.first; + const auto& parsed_decoded_frames = kv.second; + const auto& decoded_frames = frame_decoded_event_map_[ssrc]; + ASSERT_EQ(parsed_decoded_frames.size(), decoded_frames.size()); + for (size_t i = 0; i < decoded_frames.size(); i++) { + verifier_.VerifyLoggedFrameDecoded(*decoded_frames[i], + parsed_decoded_frames[i]); + } + } + + auto& parsed_ice_candidate_pair_configs = + parsed_log.ice_candidate_pair_configs(); + ASSERT_EQ(parsed_ice_candidate_pair_configs.size(), ice_config_list_.size()); + for (size_t i = 0; i < parsed_ice_candidate_pair_configs.size(); i++) { + verifier_.VerifyLoggedIceCandidatePairConfig( + *ice_config_list_[i], parsed_ice_candidate_pair_configs[i]); + } + + auto& parsed_ice_candidate_pair_events = + parsed_log.ice_candidate_pair_events(); + ASSERT_EQ(parsed_ice_candidate_pair_events.size(), + parsed_ice_candidate_pair_events.size()); + for (size_t i = 0; i < parsed_ice_candidate_pair_events.size(); i++) { + verifier_.VerifyLoggedIceCandidatePairEvent( + *ice_event_list_[i], parsed_ice_candidate_pair_events[i]); + } + + auto& parsed_incoming_rtp_packets_by_ssrc = + parsed_log.incoming_rtp_packets_by_ssrc(); + ASSERT_EQ(parsed_incoming_rtp_packets_by_ssrc.size(), + incoming_rtp_map_.size()); + for (const auto& kv : parsed_incoming_rtp_packets_by_ssrc) { + uint32_t ssrc = kv.ssrc; + const auto& parsed_rtp_stream = kv.incoming_packets; + const auto& rtp_stream = incoming_rtp_map_[ssrc]; + ASSERT_EQ(parsed_rtp_stream.size(), rtp_stream.size()); + for (size_t i = 0; i < parsed_rtp_stream.size(); i++) { + verifier_.VerifyLoggedRtpPacketIncoming(*rtp_stream[i], + parsed_rtp_stream[i]); + } + } + + auto& parsed_outgoing_rtp_packets_by_ssrc = + parsed_log.outgoing_rtp_packets_by_ssrc(); + ASSERT_EQ(parsed_outgoing_rtp_packets_by_ssrc.size(), + outgoing_rtp_map_.size()); + for (const auto& kv : parsed_outgoing_rtp_packets_by_ssrc) { + uint32_t ssrc = kv.ssrc; + const auto& parsed_rtp_stream = kv.outgoing_packets; + const auto& rtp_stream = outgoing_rtp_map_[ssrc]; + ASSERT_EQ(parsed_rtp_stream.size(), rtp_stream.size()); + for (size_t i = 0; i < parsed_rtp_stream.size(); i++) { + verifier_.VerifyLoggedRtpPacketOutgoing(*rtp_stream[i], + parsed_rtp_stream[i]); + } + } + + auto& parsed_incoming_rtcp_packets = parsed_log.incoming_rtcp_packets(); + ASSERT_EQ(parsed_incoming_rtcp_packets.size(), incoming_rtcp_list_.size()); + for (size_t i = 0; i < parsed_incoming_rtcp_packets.size(); i++) { + verifier_.VerifyLoggedRtcpPacketIncoming(*incoming_rtcp_list_[i], + parsed_incoming_rtcp_packets[i]); + } + + auto& parsed_outgoing_rtcp_packets = parsed_log.outgoing_rtcp_packets(); + ASSERT_EQ(parsed_outgoing_rtcp_packets.size(), outgoing_rtcp_list_.size()); + for (size_t i = 0; i < parsed_outgoing_rtcp_packets.size(); i++) { + verifier_.VerifyLoggedRtcpPacketOutgoing(*outgoing_rtcp_list_[i], + parsed_outgoing_rtcp_packets[i]); + } + auto& parsed_audio_recv_configs = parsed_log.audio_recv_configs(); + ASSERT_EQ(parsed_audio_recv_configs.size(), audio_recv_config_list_.size()); + for (size_t i = 0; i < parsed_audio_recv_configs.size(); i++) { + verifier_.VerifyLoggedAudioRecvConfig(*audio_recv_config_list_[i], + parsed_audio_recv_configs[i]); + } + auto& parsed_audio_send_configs = parsed_log.audio_send_configs(); + ASSERT_EQ(parsed_audio_send_configs.size(), audio_send_config_list_.size()); + for (size_t i = 0; i < parsed_audio_send_configs.size(); i++) { + verifier_.VerifyLoggedAudioSendConfig(*audio_send_config_list_[i], + parsed_audio_send_configs[i]); + } + auto& parsed_video_recv_configs = parsed_log.video_recv_configs(); + ASSERT_EQ(parsed_video_recv_configs.size(), video_recv_config_list_.size()); + for (size_t i = 0; i < parsed_video_recv_configs.size(); i++) { + verifier_.VerifyLoggedVideoRecvConfig(*video_recv_config_list_[i], + parsed_video_recv_configs[i]); + } + auto& parsed_video_send_configs = parsed_log.video_send_configs(); + ASSERT_EQ(parsed_video_send_configs.size(), video_send_config_list_.size()); + for (size_t i = 0; i < parsed_video_send_configs.size(); i++) { + verifier_.VerifyLoggedVideoSendConfig(*video_send_config_list_[i], + parsed_video_send_configs[i]); + } + + auto& parsed_generic_packets_received = parsed_log.generic_packets_received(); + ASSERT_EQ(parsed_generic_packets_received.size(), + generic_packets_received_.size()); + for (size_t i = 0; i < parsed_generic_packets_received.size(); i++) { + verifier_.VerifyLoggedGenericPacketReceived( + *generic_packets_received_[i], parsed_generic_packets_received[i]); + } + + auto& parsed_generic_packets_sent = parsed_log.generic_packets_sent(); + ASSERT_EQ(parsed_generic_packets_sent.size(), generic_packets_sent_.size()); + for (size_t i = 0; i < parsed_generic_packets_sent.size(); i++) { + verifier_.VerifyLoggedGenericPacketSent(*generic_packets_sent_[i], + parsed_generic_packets_sent[i]); + } + + auto& parsed_generic_acks_received = parsed_log.generic_acks_received(); + ASSERT_EQ(parsed_generic_acks_received.size(), generic_acks_received_.size()); + for (size_t i = 0; i < parsed_generic_acks_received.size(); i++) { + verifier_.VerifyLoggedGenericAckReceived(*generic_acks_received_[i], + parsed_generic_acks_received[i]); + } + + EXPECT_EQ(first_timestamp_ms_, parsed_log.first_timestamp().ms()); + EXPECT_EQ(last_timestamp_ms_, parsed_log.last_timestamp().ms()); + + EXPECT_EQ(parsed_log.first_log_segment().start_time_ms(), + std::min(start_time_us_ / 1000, first_timestamp_ms_)); + EXPECT_EQ(parsed_log.first_log_segment().stop_time_ms(), + stop_time_us_ / 1000); +} + +} // namespace + +TEST_P(RtcEventLogSession, StartLoggingFromBeginning) { + EventCounts count; + count.audio_send_streams = 2; + count.audio_recv_streams = 2; + count.video_send_streams = 3; + count.video_recv_streams = 4; + count.alr_states = 4; + count.audio_playouts = 100; + count.ana_configs = 3; + count.bwe_loss_events = 20; + count.bwe_delay_events = 20; + count.probe_creations = 4; + count.probe_successes = 2; + count.probe_failures = 2; + count.ice_configs = 3; + count.ice_events = 10; + count.incoming_rtp_packets = 100; + count.outgoing_rtp_packets = 100; + count.incoming_rtcp_packets = 20; + count.outgoing_rtcp_packets = 20; + if (IsNewFormat()) { + count.dtls_transport_states = 4; + count.dtls_writable_states = 2; + count.frame_decoded_events = 50; + count.generic_packets_sent = 100; + count.generic_packets_received = 100; + count.generic_acks_received = 20; + count.route_changes = 4; + } + + WriteLog(count, 0); + ReadAndVerifyLog(); +} + +TEST_P(RtcEventLogSession, StartLoggingInTheMiddle) { + EventCounts count; + count.audio_send_streams = 3; + count.audio_recv_streams = 4; + count.video_send_streams = 5; + count.video_recv_streams = 6; + count.alr_states = 10; + count.audio_playouts = 500; + count.ana_configs = 10; + count.bwe_loss_events = 50; + count.bwe_delay_events = 50; + count.probe_creations = 10; + count.probe_successes = 5; + count.probe_failures = 5; + count.ice_configs = 10; + count.ice_events = 20; + count.incoming_rtp_packets = 500; + count.outgoing_rtp_packets = 500; + count.incoming_rtcp_packets = 50; + count.outgoing_rtcp_packets = 50; + if (IsNewFormat()) { + count.dtls_transport_states = 4; + count.dtls_writable_states = 5; + count.frame_decoded_events = 250; + count.generic_packets_sent = 500; + count.generic_packets_received = 500; + count.generic_acks_received = 50; + count.route_changes = 10; + } + + WriteLog(count, 500); + ReadAndVerifyLog(); +} + +INSTANTIATE_TEST_SUITE_P( + RtcEventLogTest, + RtcEventLogSession, + ::testing::Combine( + ::testing::Values(1234567, 7654321), + ::testing::Values(RtcEventLog::kImmediateOutput, 1, 5), + ::testing::Values(RtcEventLog::EncodingType::Legacy, + RtcEventLog::EncodingType::NewFormat))); + +class RtcEventLogCircularBufferTest + : public ::testing::TestWithParam<RtcEventLog::EncodingType> { + public: + RtcEventLogCircularBufferTest() + : encoding_type_(GetParam()), + verifier_(encoding_type_), + log_storage_(), + log_output_factory_(log_storage_.CreateFactory()) {} + const RtcEventLog::EncodingType encoding_type_; + const test::EventVerifier verifier_; + MemoryLogStorage log_storage_; + std::unique_ptr<LogWriterFactoryInterface> log_output_factory_; +}; + +TEST_P(RtcEventLogCircularBufferTest, KeepsMostRecentEvents) { + // TODO(terelius): Maybe make a separate RtcEventLogImplTest that can access + // the size of the cyclic buffer? + constexpr size_t kNumEvents = 20000; + constexpr int64_t kStartTimeSeconds = 1; + constexpr int32_t kStartBitrate = 1000000; + + auto test_info = ::testing::UnitTest::GetInstance()->current_test_info(); + std::string test_name = + std::string(test_info->test_case_name()) + "_" + test_info->name(); + std::replace(test_name.begin(), test_name.end(), '/', '_'); + const std::string temp_filename = test::OutputPath() + test_name; + + std::unique_ptr<rtc::ScopedFakeClock> fake_clock = + std::make_unique<rtc::ScopedFakeClock>(); + fake_clock->SetTime(Timestamp::Seconds(kStartTimeSeconds)); + + // Create a scope for the TQ and event log factories. + // This way, we make sure that task queue instances that may rely on a clock + // have been torn down before we run the verification steps at the end of + // the test. + int64_t start_time_us, utc_start_time_us, stop_time_us; + + { + auto task_queue_factory = CreateDefaultTaskQueueFactory(); + RtcEventLogFactory rtc_event_log_factory(task_queue_factory.get()); + // When `log` goes out of scope, the contents are flushed + // to the output. + std::unique_ptr<RtcEventLog> log = + rtc_event_log_factory.CreateRtcEventLog(encoding_type_); + + for (size_t i = 0; i < kNumEvents; i++) { + // The purpose of the test is to verify that the log can handle + // more events than what fits in the internal circular buffer. The exact + // type of events does not matter so we chose ProbeSuccess events for + // simplicity. + // We base the various values on the index. We use this for some basic + // consistency checks when we read back. + log->Log(std::make_unique<RtcEventProbeResultSuccess>( + i, kStartBitrate + i * 1000)); + fake_clock->AdvanceTime(TimeDelta::Millis(10)); + } + start_time_us = rtc::TimeMicros(); + utc_start_time_us = rtc::TimeUTCMicros(); + log->StartLogging(log_output_factory_->Create(temp_filename), + RtcEventLog::kImmediateOutput); + fake_clock->AdvanceTime(TimeDelta::Millis(10)); + stop_time_us = rtc::TimeMicros(); + log->StopLogging(); + } + + // Read the generated log from memory. + ParsedRtcEventLog parsed_log; + auto it = log_storage_.logs().find(temp_filename); + ASSERT_TRUE(it != log_storage_.logs().end()); + ASSERT_TRUE(parsed_log.ParseString(it->second).ok()); + + const auto& start_log_events = parsed_log.start_log_events(); + ASSERT_EQ(start_log_events.size(), 1u); + verifier_.VerifyLoggedStartEvent(start_time_us, utc_start_time_us, + start_log_events[0]); + + const auto& stop_log_events = parsed_log.stop_log_events(); + ASSERT_EQ(stop_log_events.size(), 1u); + verifier_.VerifyLoggedStopEvent(stop_time_us, stop_log_events[0]); + + const auto& probe_success_events = parsed_log.bwe_probe_success_events(); + // If the following fails, it probably means that kNumEvents isn't larger + // than the size of the cyclic buffer in the event log. Try increasing + // kNumEvents. + EXPECT_LT(probe_success_events.size(), kNumEvents); + + ASSERT_GT(probe_success_events.size(), 1u); + int64_t first_timestamp_ms = probe_success_events[0].timestamp.ms(); + uint32_t first_id = probe_success_events[0].id; + int32_t first_bitrate_bps = probe_success_events[0].bitrate_bps; + // We want to reset the time to what we used when generating the events, but + // the fake clock implementation DCHECKS if time moves backwards. We therefore + // recreate the clock. However we must ensure that the old fake_clock is + // destroyed before the new one is created, so we have to reset() first. + fake_clock.reset(); + fake_clock = std::make_unique<rtc::ScopedFakeClock>(); + fake_clock->SetTime(Timestamp::Millis(first_timestamp_ms)); + for (size_t i = 1; i < probe_success_events.size(); i++) { + fake_clock->AdvanceTime(TimeDelta::Millis(10)); + verifier_.VerifyLoggedBweProbeSuccessEvent( + RtcEventProbeResultSuccess(first_id + i, first_bitrate_bps + i * 1000), + probe_success_events[i]); + } +} + +INSTANTIATE_TEST_SUITE_P( + RtcEventLogTest, + RtcEventLogCircularBufferTest, + ::testing::Values(RtcEventLog::EncodingType::Legacy, + RtcEventLog::EncodingType::NewFormat)); + +// TODO(terelius): Verify parser behavior if the timestamps are not +// monotonically increasing in the log. + +TEST(DereferencingVectorTest, NonConstVector) { + std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + DereferencingVector<int> even; + EXPECT_TRUE(even.empty()); + EXPECT_EQ(even.size(), 0u); + EXPECT_EQ(even.begin(), even.end()); + for (size_t i = 0; i < v.size(); i += 2) { + even.push_back(&v[i]); + } + EXPECT_FALSE(even.empty()); + EXPECT_EQ(even.size(), 5u); + EXPECT_NE(even.begin(), even.end()); + + // Test direct access. + for (size_t i = 0; i < even.size(); i++) { + EXPECT_EQ(even[i], 2 * static_cast<int>(i)); + } + + // Test iterator. + for (int val : even) { + EXPECT_EQ(val % 2, 0); + } + + // Test modification through iterator. + for (int& val : even) { + val = val * 2; + EXPECT_EQ(val % 2, 0); + } + + // Backing vector should have been modified. + std::vector<int> expected{0, 1, 4, 3, 8, 5, 12, 7, 16, 9}; + for (size_t i = 0; i < v.size(); i++) { + EXPECT_EQ(v[i], expected[i]); + } +} + +TEST(DereferencingVectorTest, ConstVector) { + std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + DereferencingVector<const int> odd; + EXPECT_TRUE(odd.empty()); + EXPECT_EQ(odd.size(), 0u); + EXPECT_EQ(odd.begin(), odd.end()); + for (size_t i = 1; i < v.size(); i += 2) { + odd.push_back(&v[i]); + } + EXPECT_FALSE(odd.empty()); + EXPECT_EQ(odd.size(), 5u); + EXPECT_NE(odd.begin(), odd.end()); + + // Test direct access. + for (size_t i = 0; i < odd.size(); i++) { + EXPECT_EQ(odd[i], 2 * static_cast<int>(i) + 1); + } + + // Test iterator. + for (int val : odd) { + EXPECT_EQ(val % 2, 1); + } +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.cc b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.cc new file mode 100644 index 0000000000..aece4b5753 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.cc @@ -0,0 +1,1419 @@ +/* + * Copyright (c) 2016 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 "logging/rtc_event_log/rtc_event_log_unittest_helper.h" + +#include <string.h> // memcmp + +#include <cmath> +#include <cstdint> +#include <limits> +#include <memory> +#include <numeric> +#include <string> +#include <utility> +#include <vector> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/network_state_predictor.h" +#include "api/rtp_headers.h" +#include "api/rtp_parameters.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" +#include "modules/rtp_rtcp/include/rtp_cvo.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/rtcp_packet/dlrr.h" +#include "modules/rtp_rtcp/source/rtcp_packet/rrtr.h" +#include "modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h" +#include "modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "modules/rtp_rtcp/source/rtp_packet_to_send.h" +#include "rtc_base/buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/time_utils.h" +#include "system_wrappers/include/field_trial.h" +#include "system_wrappers/include/ntp_time.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace test { + +namespace { + +struct ExtensionPair { + RTPExtensionType type; + const char* name; +}; + +constexpr int kMaxCsrcs = 3; + +// Maximum serialized size of a header extension, including 1 byte ID. +constexpr int kMaxExtensionSizeBytes = 10; +constexpr int kMaxNumExtensions = 6; + +constexpr ExtensionPair kExtensions[kMaxNumExtensions] = { + {RTPExtensionType::kRtpExtensionTransmissionTimeOffset, + RtpExtension::kTimestampOffsetUri}, + {RTPExtensionType::kRtpExtensionAbsoluteSendTime, + RtpExtension::kAbsSendTimeUri}, + {RTPExtensionType::kRtpExtensionTransportSequenceNumber, + RtpExtension::kTransportSequenceNumberUri}, + {RTPExtensionType::kRtpExtensionAudioLevel, RtpExtension::kAudioLevelUri}, + {RTPExtensionType::kRtpExtensionVideoRotation, + RtpExtension::kVideoRotationUri}, + {RTPExtensionType::kRtpExtensionDependencyDescriptor, + RtpExtension::kDependencyDescriptorUri}}; + +template <typename T> +void ShuffleInPlace(Random* prng, rtc::ArrayView<T> array) { + RTC_DCHECK_LE(array.size(), std::numeric_limits<uint32_t>::max()); + for (uint32_t i = 0; i + 1 < array.size(); i++) { + uint32_t other = prng->Rand(i, static_cast<uint32_t>(array.size() - 1)); + std::swap(array[i], array[other]); + } +} + +absl::optional<int> GetExtensionId(const std::vector<RtpExtension>& extensions, + absl::string_view uri) { + for (const auto& extension : extensions) { + if (extension.uri == uri) + return extension.id; + } + return absl::nullopt; +} + +} // namespace + +std::unique_ptr<RtcEventAlrState> EventGenerator::NewAlrState() { + return std::make_unique<RtcEventAlrState>(prng_.Rand<bool>()); +} + +std::unique_ptr<RtcEventAudioPlayout> EventGenerator::NewAudioPlayout( + uint32_t ssrc) { + return std::make_unique<RtcEventAudioPlayout>(ssrc); +} + +std::unique_ptr<RtcEventAudioNetworkAdaptation> +EventGenerator::NewAudioNetworkAdaptation() { + std::unique_ptr<AudioEncoderRuntimeConfig> config = + std::make_unique<AudioEncoderRuntimeConfig>(); + + config->bitrate_bps = prng_.Rand(0, 3000000); + config->enable_fec = prng_.Rand<bool>(); + config->enable_dtx = prng_.Rand<bool>(); + config->frame_length_ms = prng_.Rand(10, 120); + config->num_channels = prng_.Rand(1, 2); + config->uplink_packet_loss_fraction = prng_.Rand<float>(); + + return std::make_unique<RtcEventAudioNetworkAdaptation>(std::move(config)); +} + +std::unique_ptr<RtcEventNetEqSetMinimumDelay> +EventGenerator::NewNetEqSetMinimumDelay(uint32_t ssrc) { + return std::make_unique<RtcEventNetEqSetMinimumDelay>( + ssrc, prng_.Rand(std::numeric_limits<int>::max())); +} + +std::unique_ptr<RtcEventBweUpdateDelayBased> +EventGenerator::NewBweUpdateDelayBased() { + constexpr int32_t kMaxBweBps = 20000000; + int32_t bitrate_bps = prng_.Rand(0, kMaxBweBps); + BandwidthUsage state = static_cast<BandwidthUsage>( + prng_.Rand(static_cast<uint32_t>(BandwidthUsage::kLast) - 1)); + return std::make_unique<RtcEventBweUpdateDelayBased>(bitrate_bps, state); +} + +std::unique_ptr<RtcEventBweUpdateLossBased> +EventGenerator::NewBweUpdateLossBased() { + constexpr int32_t kMaxBweBps = 20000000; + constexpr int32_t kMaxPackets = 1000; + int32_t bitrate_bps = prng_.Rand(0, kMaxBweBps); + uint8_t fraction_lost = prng_.Rand<uint8_t>(); + int32_t total_packets = prng_.Rand(1, kMaxPackets); + + return std::make_unique<RtcEventBweUpdateLossBased>( + bitrate_bps, fraction_lost, total_packets); +} + +std::unique_ptr<RtcEventDtlsTransportState> +EventGenerator::NewDtlsTransportState() { + DtlsTransportState state = static_cast<DtlsTransportState>( + prng_.Rand(static_cast<uint32_t>(DtlsTransportState::kNumValues) - 1)); + + return std::make_unique<RtcEventDtlsTransportState>(state); +} + +std::unique_ptr<RtcEventDtlsWritableState> +EventGenerator::NewDtlsWritableState() { + bool writable = prng_.Rand<bool>(); + return std::make_unique<RtcEventDtlsWritableState>(writable); +} + +std::unique_ptr<RtcEventFrameDecoded> EventGenerator::NewFrameDecodedEvent( + uint32_t ssrc) { + constexpr int kMinRenderDelayMs = 1; + constexpr int kMaxRenderDelayMs = 2000000; + constexpr int kMaxWidth = 15360; + constexpr int kMaxHeight = 8640; + constexpr int kMinWidth = 16; + constexpr int kMinHeight = 16; + constexpr int kNumCodecTypes = 6; + + constexpr VideoCodecType kCodecList[kNumCodecTypes] = { + kVideoCodecGeneric, kVideoCodecVP8, kVideoCodecVP9, + kVideoCodecAV1, kVideoCodecH264, kVideoCodecH265}; + const int64_t render_time_ms = + rtc::TimeMillis() + prng_.Rand(kMinRenderDelayMs, kMaxRenderDelayMs); + const int width = prng_.Rand(kMinWidth, kMaxWidth); + const int height = prng_.Rand(kMinHeight, kMaxHeight); + const VideoCodecType codec = kCodecList[prng_.Rand(0, kNumCodecTypes - 1)]; + const uint8_t qp = prng_.Rand<uint8_t>(); + return std::make_unique<RtcEventFrameDecoded>(render_time_ms, ssrc, width, + height, codec, qp); +} + +std::unique_ptr<RtcEventProbeClusterCreated> +EventGenerator::NewProbeClusterCreated() { + constexpr int kMaxBweBps = 20000000; + constexpr int kMaxNumProbes = 10000; + int id = prng_.Rand(1, kMaxNumProbes); + int bitrate_bps = prng_.Rand(0, kMaxBweBps); + int min_probes = prng_.Rand(5, 50); + int min_bytes = prng_.Rand(500, 50000); + + return std::make_unique<RtcEventProbeClusterCreated>(id, bitrate_bps, + min_probes, min_bytes); +} + +std::unique_ptr<RtcEventProbeResultFailure> +EventGenerator::NewProbeResultFailure() { + constexpr int kMaxNumProbes = 10000; + int id = prng_.Rand(1, kMaxNumProbes); + ProbeFailureReason reason = static_cast<ProbeFailureReason>( + prng_.Rand(static_cast<uint32_t>(ProbeFailureReason::kLast) - 1)); + + return std::make_unique<RtcEventProbeResultFailure>(id, reason); +} + +std::unique_ptr<RtcEventProbeResultSuccess> +EventGenerator::NewProbeResultSuccess() { + constexpr int kMaxBweBps = 20000000; + constexpr int kMaxNumProbes = 10000; + int id = prng_.Rand(1, kMaxNumProbes); + int bitrate_bps = prng_.Rand(0, kMaxBweBps); + + return std::make_unique<RtcEventProbeResultSuccess>(id, bitrate_bps); +} + +std::unique_ptr<RtcEventIceCandidatePairConfig> +EventGenerator::NewIceCandidatePairConfig() { + IceCandidateType local_candidate_type = static_cast<IceCandidateType>( + prng_.Rand(static_cast<uint32_t>(IceCandidateType::kNumValues) - 1)); + IceCandidateNetworkType local_network_type = + static_cast<IceCandidateNetworkType>(prng_.Rand( + static_cast<uint32_t>(IceCandidateNetworkType::kNumValues) - 1)); + IceCandidatePairAddressFamily local_address_family = + static_cast<IceCandidatePairAddressFamily>(prng_.Rand( + static_cast<uint32_t>(IceCandidatePairAddressFamily::kNumValues) - + 1)); + IceCandidateType remote_candidate_type = static_cast<IceCandidateType>( + prng_.Rand(static_cast<uint32_t>(IceCandidateType::kNumValues) - 1)); + IceCandidatePairAddressFamily remote_address_family = + static_cast<IceCandidatePairAddressFamily>(prng_.Rand( + static_cast<uint32_t>(IceCandidatePairAddressFamily::kNumValues) - + 1)); + IceCandidatePairProtocol protocol_type = + static_cast<IceCandidatePairProtocol>(prng_.Rand( + static_cast<uint32_t>(IceCandidatePairProtocol::kNumValues) - 1)); + + IceCandidatePairDescription desc; + desc.local_candidate_type = local_candidate_type; + desc.local_relay_protocol = protocol_type; + desc.local_network_type = local_network_type; + desc.local_address_family = local_address_family; + desc.remote_candidate_type = remote_candidate_type; + desc.remote_address_family = remote_address_family; + desc.candidate_pair_protocol = protocol_type; + + IceCandidatePairConfigType type = + static_cast<IceCandidatePairConfigType>(prng_.Rand( + static_cast<uint32_t>(IceCandidatePairConfigType::kNumValues) - 1)); + uint32_t pair_id = prng_.Rand<uint32_t>(); + return std::make_unique<RtcEventIceCandidatePairConfig>(type, pair_id, desc); +} + +std::unique_ptr<RtcEventIceCandidatePair> +EventGenerator::NewIceCandidatePair() { + IceCandidatePairEventType type = + static_cast<IceCandidatePairEventType>(prng_.Rand( + static_cast<uint32_t>(IceCandidatePairEventType::kNumValues) - 1)); + uint32_t pair_id = prng_.Rand<uint32_t>(); + uint32_t transaction_id = prng_.Rand<uint32_t>(); + + return std::make_unique<RtcEventIceCandidatePair>(type, pair_id, + transaction_id); +} + +rtcp::ReportBlock EventGenerator::NewReportBlock() { + rtcp::ReportBlock report_block; + report_block.SetMediaSsrc(prng_.Rand<uint32_t>()); + report_block.SetFractionLost(prng_.Rand<uint8_t>()); + // cumulative_lost is a 3-byte signed value. + RTC_DCHECK(report_block.SetCumulativeLost( + prng_.Rand(-(1 << 23) + 1, (1 << 23) - 1))); + report_block.SetExtHighestSeqNum(prng_.Rand<uint32_t>()); + report_block.SetJitter(prng_.Rand<uint32_t>()); + report_block.SetLastSr(prng_.Rand<uint32_t>()); + report_block.SetDelayLastSr(prng_.Rand<uint32_t>()); + return report_block; +} + +rtcp::SenderReport EventGenerator::NewSenderReport() { + rtcp::SenderReport sender_report; + sender_report.SetSenderSsrc(prng_.Rand<uint32_t>()); + sender_report.SetNtp(NtpTime(prng_.Rand<uint32_t>(), prng_.Rand<uint32_t>())); + sender_report.SetRtpTimestamp(prng_.Rand<uint32_t>()); + sender_report.SetPacketCount(prng_.Rand<uint32_t>()); + sender_report.SetOctetCount(prng_.Rand<uint32_t>()); + sender_report.AddReportBlock(NewReportBlock()); + return sender_report; +} + +rtcp::ReceiverReport EventGenerator::NewReceiverReport() { + rtcp::ReceiverReport receiver_report; + receiver_report.SetSenderSsrc(prng_.Rand<uint32_t>()); + receiver_report.AddReportBlock(NewReportBlock()); + return receiver_report; +} + +rtcp::ExtendedReports EventGenerator::NewExtendedReports() { + rtcp::ExtendedReports extended_report; + extended_report.SetSenderSsrc(prng_.Rand<uint32_t>()); + + rtcp::Rrtr rrtr; + rrtr.SetNtp(NtpTime(prng_.Rand<uint32_t>(), prng_.Rand<uint32_t>())); + extended_report.SetRrtr(rrtr); + + rtcp::ReceiveTimeInfo time_info( + prng_.Rand<uint32_t>(), prng_.Rand<uint32_t>(), prng_.Rand<uint32_t>()); + extended_report.AddDlrrItem(time_info); + + rtcp::TargetBitrate target_bitrate; + target_bitrate.AddTargetBitrate(/*spatial layer*/ prng_.Rand(0, 3), + /*temporal layer*/ prng_.Rand(0, 3), + /*bitrate kbps*/ prng_.Rand(0, 50000)); + target_bitrate.AddTargetBitrate(/*spatial layer*/ prng_.Rand(4, 7), + /*temporal layer*/ prng_.Rand(4, 7), + /*bitrate kbps*/ prng_.Rand(0, 50000)); + extended_report.SetTargetBitrate(target_bitrate); + return extended_report; +} + +rtcp::Nack EventGenerator::NewNack() { + rtcp::Nack nack; + uint16_t base_seq_no = prng_.Rand<uint16_t>(); + std::vector<uint16_t> nack_list; + nack_list.push_back(base_seq_no); + for (uint16_t i = 1u; i < 10u; i++) { + if (prng_.Rand<bool>()) + nack_list.push_back(base_seq_no + i); + } + nack.SetPacketIds(nack_list); + return nack; +} + +rtcp::Fir EventGenerator::NewFir() { + rtcp::Fir fir; + fir.SetSenderSsrc(prng_.Rand<uint32_t>()); + fir.AddRequestTo(/*ssrc*/ prng_.Rand<uint32_t>(), + /*seq num*/ prng_.Rand<uint8_t>()); + fir.AddRequestTo(/*ssrc*/ prng_.Rand<uint32_t>(), + /*seq num*/ prng_.Rand<uint8_t>()); + return fir; +} + +rtcp::Pli EventGenerator::NewPli() { + rtcp::Pli pli; + pli.SetSenderSsrc(prng_.Rand<uint32_t>()); + pli.SetMediaSsrc(prng_.Rand<uint32_t>()); + return pli; +} + +rtcp::Bye EventGenerator::NewBye() { + rtcp::Bye bye; + bye.SetSenderSsrc(prng_.Rand<uint32_t>()); + std::vector<uint32_t> csrcs{prng_.Rand<uint32_t>(), prng_.Rand<uint32_t>()}; + bye.SetCsrcs(csrcs); + if (prng_.Rand(0, 2)) { + bye.SetReason("foo"); + } else { + bye.SetReason("bar"); + } + return bye; +} + +rtcp::TransportFeedback EventGenerator::NewTransportFeedback() { + rtcp::TransportFeedback transport_feedback; + uint16_t base_seq_no = prng_.Rand<uint16_t>(); + Timestamp base_time = Timestamp::Micros(prng_.Rand<uint32_t>()); + transport_feedback.SetBase(base_seq_no, base_time); + transport_feedback.AddReceivedPacket(base_seq_no, base_time); + Timestamp time = base_time; + for (uint16_t i = 1u; i < 10u; i++) { + time += TimeDelta::Micros(prng_.Rand(0, 100'000)); + if (prng_.Rand<bool>()) { + transport_feedback.AddReceivedPacket(base_seq_no + i, time); + } + } + return transport_feedback; +} + +rtcp::Remb EventGenerator::NewRemb() { + rtcp::Remb remb; + // The remb bitrate is transported as a 16-bit mantissa and an 8-bit exponent. + uint64_t bitrate_bps = prng_.Rand(0, (1 << 16) - 1) << prng_.Rand(7); + std::vector<uint32_t> ssrcs{prng_.Rand<uint32_t>(), prng_.Rand<uint32_t>()}; + remb.SetSsrcs(ssrcs); + remb.SetBitrateBps(bitrate_bps); + return remb; +} + +rtcp::LossNotification EventGenerator::NewLossNotification() { + rtcp::LossNotification loss_notification; + const uint16_t last_decoded = prng_.Rand<uint16_t>(); + const uint16_t last_received = + last_decoded + (prng_.Rand<uint16_t>() & 0x7fff); + const bool decodability_flag = prng_.Rand<bool>(); + EXPECT_TRUE( + loss_notification.Set(last_decoded, last_received, decodability_flag)); + return loss_notification; +} + +std::unique_ptr<RtcEventRouteChange> EventGenerator::NewRouteChange() { + return std::make_unique<RtcEventRouteChange>(prng_.Rand<bool>(), + prng_.Rand(0, 128)); +} + +std::unique_ptr<RtcEventRemoteEstimate> EventGenerator::NewRemoteEstimate() { + return std::make_unique<RtcEventRemoteEstimate>( + DataRate::KilobitsPerSec(prng_.Rand(0, 100000)), + DataRate::KilobitsPerSec(prng_.Rand(0, 100000))); +} + +std::unique_ptr<RtcEventRtcpPacketIncoming> +EventGenerator::NewRtcpPacketIncoming() { + enum class SupportedRtcpTypes { + kSenderReport = 0, + kReceiverReport, + kExtendedReports, + kFir, + kPli, + kNack, + kRemb, + kBye, + kTransportFeedback, + kNumValues + }; + SupportedRtcpTypes type = static_cast<SupportedRtcpTypes>( + prng_.Rand(0, static_cast<int>(SupportedRtcpTypes::kNumValues) - 1)); + switch (type) { + case SupportedRtcpTypes::kSenderReport: { + rtcp::SenderReport sender_report = NewSenderReport(); + rtc::Buffer buffer = sender_report.Build(); + return std::make_unique<RtcEventRtcpPacketIncoming>(buffer); + } + case SupportedRtcpTypes::kReceiverReport: { + rtcp::ReceiverReport receiver_report = NewReceiverReport(); + rtc::Buffer buffer = receiver_report.Build(); + return std::make_unique<RtcEventRtcpPacketIncoming>(buffer); + } + case SupportedRtcpTypes::kExtendedReports: { + rtcp::ExtendedReports extended_report = NewExtendedReports(); + rtc::Buffer buffer = extended_report.Build(); + return std::make_unique<RtcEventRtcpPacketIncoming>(buffer); + } + case SupportedRtcpTypes::kFir: { + rtcp::Fir fir = NewFir(); + rtc::Buffer buffer = fir.Build(); + return std::make_unique<RtcEventRtcpPacketIncoming>(buffer); + } + case SupportedRtcpTypes::kPli: { + rtcp::Pli pli = NewPli(); + rtc::Buffer buffer = pli.Build(); + return std::make_unique<RtcEventRtcpPacketIncoming>(buffer); + } + case SupportedRtcpTypes::kNack: { + rtcp::Nack nack = NewNack(); + rtc::Buffer buffer = nack.Build(); + return std::make_unique<RtcEventRtcpPacketIncoming>(buffer); + } + case SupportedRtcpTypes::kRemb: { + rtcp::Remb remb = NewRemb(); + rtc::Buffer buffer = remb.Build(); + return std::make_unique<RtcEventRtcpPacketIncoming>(buffer); + } + case SupportedRtcpTypes::kBye: { + rtcp::Bye bye = NewBye(); + rtc::Buffer buffer = bye.Build(); + return std::make_unique<RtcEventRtcpPacketIncoming>(buffer); + } + case SupportedRtcpTypes::kTransportFeedback: { + rtcp::TransportFeedback transport_feedback = NewTransportFeedback(); + rtc::Buffer buffer = transport_feedback.Build(); + return std::make_unique<RtcEventRtcpPacketIncoming>(buffer); + } + default: + RTC_DCHECK_NOTREACHED(); + rtc::Buffer buffer; + return std::make_unique<RtcEventRtcpPacketIncoming>(buffer); + } +} + +std::unique_ptr<RtcEventRtcpPacketOutgoing> +EventGenerator::NewRtcpPacketOutgoing() { + enum class SupportedRtcpTypes { + kSenderReport = 0, + kReceiverReport, + kExtendedReports, + kFir, + kPli, + kNack, + kRemb, + kBye, + kTransportFeedback, + kNumValues + }; + SupportedRtcpTypes type = static_cast<SupportedRtcpTypes>( + prng_.Rand(0, static_cast<int>(SupportedRtcpTypes::kNumValues) - 1)); + switch (type) { + case SupportedRtcpTypes::kSenderReport: { + rtcp::SenderReport sender_report = NewSenderReport(); + rtc::Buffer buffer = sender_report.Build(); + return std::make_unique<RtcEventRtcpPacketOutgoing>(buffer); + } + case SupportedRtcpTypes::kReceiverReport: { + rtcp::ReceiverReport receiver_report = NewReceiverReport(); + rtc::Buffer buffer = receiver_report.Build(); + return std::make_unique<RtcEventRtcpPacketOutgoing>(buffer); + } + case SupportedRtcpTypes::kExtendedReports: { + rtcp::ExtendedReports extended_report = NewExtendedReports(); + rtc::Buffer buffer = extended_report.Build(); + return std::make_unique<RtcEventRtcpPacketOutgoing>(buffer); + } + case SupportedRtcpTypes::kFir: { + rtcp::Fir fir = NewFir(); + rtc::Buffer buffer = fir.Build(); + return std::make_unique<RtcEventRtcpPacketOutgoing>(buffer); + } + case SupportedRtcpTypes::kPli: { + rtcp::Pli pli = NewPli(); + rtc::Buffer buffer = pli.Build(); + return std::make_unique<RtcEventRtcpPacketOutgoing>(buffer); + } + case SupportedRtcpTypes::kNack: { + rtcp::Nack nack = NewNack(); + rtc::Buffer buffer = nack.Build(); + return std::make_unique<RtcEventRtcpPacketOutgoing>(buffer); + } + case SupportedRtcpTypes::kRemb: { + rtcp::Remb remb = NewRemb(); + rtc::Buffer buffer = remb.Build(); + return std::make_unique<RtcEventRtcpPacketOutgoing>(buffer); + } + case SupportedRtcpTypes::kBye: { + rtcp::Bye bye = NewBye(); + rtc::Buffer buffer = bye.Build(); + return std::make_unique<RtcEventRtcpPacketOutgoing>(buffer); + } + case SupportedRtcpTypes::kTransportFeedback: { + rtcp::TransportFeedback transport_feedback = NewTransportFeedback(); + rtc::Buffer buffer = transport_feedback.Build(); + return std::make_unique<RtcEventRtcpPacketOutgoing>(buffer); + } + default: + RTC_DCHECK_NOTREACHED(); + rtc::Buffer buffer; + return std::make_unique<RtcEventRtcpPacketOutgoing>(buffer); + } +} + +std::unique_ptr<RtcEventGenericPacketSent> +EventGenerator::NewGenericPacketSent() { + return std::make_unique<RtcEventGenericPacketSent>( + sent_packet_number_++, prng_.Rand(40, 50), prng_.Rand(0, 150), + prng_.Rand(0, 1000)); +} +std::unique_ptr<RtcEventGenericPacketReceived> +EventGenerator::NewGenericPacketReceived() { + return std::make_unique<RtcEventGenericPacketReceived>( + received_packet_number_++, prng_.Rand(40, 250)); +} +std::unique_ptr<RtcEventGenericAckReceived> +EventGenerator::NewGenericAckReceived() { + absl::optional<int64_t> receive_timestamp = absl::nullopt; + if (prng_.Rand(0, 2) > 0) { + receive_timestamp = prng_.Rand(0, 100000); + } + AckedPacket packet = {prng_.Rand(40, 250), receive_timestamp}; + return std::move(RtcEventGenericAckReceived::CreateLogs( + received_packet_number_++, std::vector<AckedPacket>{packet})[0]); +} + +void EventGenerator::RandomizeRtpPacket( + size_t payload_size, + size_t padding_size, + uint32_t ssrc, + const RtpHeaderExtensionMap& extension_map, + RtpPacket* rtp_packet, + bool all_configured_exts) { + constexpr int kMaxPayloadType = 127; + rtp_packet->SetPayloadType(prng_.Rand(kMaxPayloadType)); + rtp_packet->SetMarker(prng_.Rand<bool>()); + rtp_packet->SetSequenceNumber(prng_.Rand<uint16_t>()); + rtp_packet->SetSsrc(ssrc); + rtp_packet->SetTimestamp(prng_.Rand<uint32_t>()); + + uint32_t csrcs_count = prng_.Rand(0, kMaxCsrcs); + std::vector<uint32_t> csrcs; + for (size_t i = 0; i < csrcs_count; i++) { + csrcs.push_back(prng_.Rand<uint32_t>()); + } + rtp_packet->SetCsrcs(csrcs); + + if (extension_map.IsRegistered(TransmissionOffset::kId) && + (all_configured_exts || prng_.Rand<bool>())) { + rtp_packet->SetExtension<TransmissionOffset>(prng_.Rand(0x00ffffff)); + } + + if (extension_map.IsRegistered(AudioLevel::kId) && + (all_configured_exts || prng_.Rand<bool>())) { + rtp_packet->SetExtension<AudioLevel>(prng_.Rand<bool>(), prng_.Rand(127)); + } + + if (extension_map.IsRegistered(AbsoluteSendTime::kId) && + (all_configured_exts || prng_.Rand<bool>())) { + rtp_packet->SetExtension<AbsoluteSendTime>(prng_.Rand(0x00ffffff)); + } + + if (extension_map.IsRegistered(VideoOrientation::kId) && + (all_configured_exts || prng_.Rand<bool>())) { + rtp_packet->SetExtension<VideoOrientation>(prng_.Rand(3)); + } + + if (extension_map.IsRegistered(TransportSequenceNumber::kId) && + (all_configured_exts || prng_.Rand<bool>())) { + rtp_packet->SetExtension<TransportSequenceNumber>(prng_.Rand<uint16_t>()); + } + + if (extension_map.IsRegistered(RtpDependencyDescriptorExtension::kId) && + (all_configured_exts || prng_.Rand<bool>())) { + std::vector<uint8_t> raw_data(3 + prng_.Rand(6)); + for (uint8_t& d : raw_data) { + d = prng_.Rand<uint8_t>(); + } + rtp_packet->SetRawExtension<RtpDependencyDescriptorExtension>(raw_data); + } + + RTC_CHECK_LE(rtp_packet->headers_size() + payload_size, IP_PACKET_SIZE); + + uint8_t* payload = rtp_packet->AllocatePayload(payload_size); + RTC_DCHECK(payload != nullptr); + for (size_t i = 0; i < payload_size; i++) { + payload[i] = prng_.Rand<uint8_t>(); + } + RTC_CHECK(rtp_packet->SetPadding(padding_size)); +} + +std::unique_ptr<RtcEventRtpPacketIncoming> EventGenerator::NewRtpPacketIncoming( + uint32_t ssrc, + const RtpHeaderExtensionMap& extension_map, + bool all_configured_exts) { + constexpr size_t kMaxPaddingLength = 224; + const bool padding = prng_.Rand(0, 9) == 0; // Let padding be 10% probable. + const size_t padding_size = !padding ? 0u : prng_.Rand(0u, kMaxPaddingLength); + + // 12 bytes RTP header, 4 bytes for 0xBEDE + alignment, 4 bytes per CSRC. + constexpr size_t kMaxHeaderSize = + 16 + 4 * kMaxCsrcs + kMaxExtensionSizeBytes * kMaxNumExtensions; + + // In principle, a packet can contain both padding and other payload. + // Currently, RTC eventlog encoder-parser can only maintain padding length if + // packet is full padding. + // TODO(webrtc:9730): Remove the deterministic logic for padding_size > 0. + size_t payload_size = + padding_size > 0 ? 0 + : prng_.Rand(0u, static_cast<uint32_t>(IP_PACKET_SIZE - + 1 - padding_size - + kMaxHeaderSize)); + + RtpPacketReceived rtp_packet(&extension_map); + RandomizeRtpPacket(payload_size, padding_size, ssrc, extension_map, + &rtp_packet, all_configured_exts); + + return std::make_unique<RtcEventRtpPacketIncoming>(rtp_packet); +} + +std::unique_ptr<RtcEventRtpPacketOutgoing> EventGenerator::NewRtpPacketOutgoing( + uint32_t ssrc, + const RtpHeaderExtensionMap& extension_map, + bool all_configured_exts) { + constexpr size_t kMaxPaddingLength = 224; + const bool padding = prng_.Rand(0, 9) == 0; // Let padding be 10% probable. + const size_t padding_size = !padding ? 0u : prng_.Rand(0u, kMaxPaddingLength); + + // 12 bytes RTP header, 4 bytes for 0xBEDE + alignment, 4 bytes per CSRC. + constexpr size_t kMaxHeaderSize = + 16 + 4 * kMaxCsrcs + kMaxExtensionSizeBytes * kMaxNumExtensions; + + // In principle,a packet can contain both padding and other payload. + // Currently, RTC eventlog encoder-parser can only maintain padding length if + // packet is full padding. + // TODO(webrtc:9730): Remove the deterministic logic for padding_size > 0. + size_t payload_size = + padding_size > 0 ? 0 + : prng_.Rand(0u, static_cast<uint32_t>(IP_PACKET_SIZE - + 1 - padding_size - + kMaxHeaderSize)); + + RtpPacketToSend rtp_packet(&extension_map); + RandomizeRtpPacket(payload_size, padding_size, ssrc, extension_map, + &rtp_packet, all_configured_exts); + + int probe_cluster_id = prng_.Rand(0, 100000); + return std::make_unique<RtcEventRtpPacketOutgoing>(rtp_packet, + probe_cluster_id); +} + +RtpHeaderExtensionMap EventGenerator::NewRtpHeaderExtensionMap( + bool configure_all, + const std::vector<RTPExtensionType>& excluded_extensions) { + RtpHeaderExtensionMap extension_map; + std::vector<int> id(RtpExtension::kOneByteHeaderExtensionMaxId - + RtpExtension::kMinId + 1); + std::iota(id.begin(), id.end(), RtpExtension::kMinId); + ShuffleInPlace(&prng_, rtc::ArrayView<int>(id)); + + auto not_excluded = [&](RTPExtensionType type) -> bool { + return !absl::c_linear_search(excluded_extensions, type); + }; + + if (not_excluded(AudioLevel::kId) && (configure_all || prng_.Rand<bool>())) { + extension_map.Register<AudioLevel>(id[0]); + } + if (not_excluded(TransmissionOffset::kId) && + (configure_all || prng_.Rand<bool>())) { + extension_map.Register<TransmissionOffset>(id[1]); + } + if (not_excluded(AbsoluteSendTime::kId) && + (configure_all || prng_.Rand<bool>())) { + extension_map.Register<AbsoluteSendTime>(id[2]); + } + if (not_excluded(VideoOrientation::kId) && + (configure_all || prng_.Rand<bool>())) { + extension_map.Register<VideoOrientation>(id[3]); + } + if (not_excluded(TransportSequenceNumber::kId) && + (configure_all || prng_.Rand<bool>())) { + extension_map.Register<TransportSequenceNumber>(id[4]); + } + if (not_excluded(RtpDependencyDescriptorExtension::kId) && + (configure_all || prng_.Rand<bool>())) { + extension_map.Register<RtpDependencyDescriptorExtension>(id[5]); + } + + return extension_map; +} + +std::unique_ptr<RtcEventAudioReceiveStreamConfig> +EventGenerator::NewAudioReceiveStreamConfig( + uint32_t ssrc, + const RtpHeaderExtensionMap& extensions) { + auto config = std::make_unique<rtclog::StreamConfig>(); + // Add SSRCs for the stream. + config->remote_ssrc = ssrc; + config->local_ssrc = prng_.Rand<uint32_t>(); + // Add header extensions. + for (size_t i = 0; i < kMaxNumExtensions; i++) { + uint8_t id = extensions.GetId(kExtensions[i].type); + if (id != RtpHeaderExtensionMap::kInvalidId) { + config->rtp_extensions.emplace_back(kExtensions[i].name, id); + } + } + + return std::make_unique<RtcEventAudioReceiveStreamConfig>(std::move(config)); +} + +std::unique_ptr<RtcEventAudioSendStreamConfig> +EventGenerator::NewAudioSendStreamConfig( + uint32_t ssrc, + const RtpHeaderExtensionMap& extensions) { + auto config = std::make_unique<rtclog::StreamConfig>(); + // Add SSRC to the stream. + config->local_ssrc = ssrc; + // Add header extensions. + for (size_t i = 0; i < kMaxNumExtensions; i++) { + uint8_t id = extensions.GetId(kExtensions[i].type); + if (id != RtpHeaderExtensionMap::kInvalidId) { + config->rtp_extensions.emplace_back(kExtensions[i].name, id); + } + } + return std::make_unique<RtcEventAudioSendStreamConfig>(std::move(config)); +} + +std::unique_ptr<RtcEventVideoReceiveStreamConfig> +EventGenerator::NewVideoReceiveStreamConfig( + uint32_t ssrc, + const RtpHeaderExtensionMap& extensions) { + auto config = std::make_unique<rtclog::StreamConfig>(); + + // Add SSRCs for the stream. + config->remote_ssrc = ssrc; + config->local_ssrc = prng_.Rand<uint32_t>(); + // Add extensions and settings for RTCP. + config->rtcp_mode = + prng_.Rand<bool>() ? RtcpMode::kCompound : RtcpMode::kReducedSize; + config->remb = prng_.Rand<bool>(); + config->rtx_ssrc = prng_.Rand<uint32_t>(); + config->codecs.emplace_back(prng_.Rand<bool>() ? "VP8" : "H264", + prng_.Rand(127), prng_.Rand(127)); + // Add header extensions. + for (size_t i = 0; i < kMaxNumExtensions; i++) { + uint8_t id = extensions.GetId(kExtensions[i].type); + if (id != RtpHeaderExtensionMap::kInvalidId) { + config->rtp_extensions.emplace_back(kExtensions[i].name, id); + } + } + return std::make_unique<RtcEventVideoReceiveStreamConfig>(std::move(config)); +} + +std::unique_ptr<RtcEventVideoSendStreamConfig> +EventGenerator::NewVideoSendStreamConfig( + uint32_t ssrc, + const RtpHeaderExtensionMap& extensions) { + auto config = std::make_unique<rtclog::StreamConfig>(); + + config->codecs.emplace_back(prng_.Rand<bool>() ? "VP8" : "H264", + prng_.Rand(127), prng_.Rand(127)); + config->local_ssrc = ssrc; + config->rtx_ssrc = prng_.Rand<uint32_t>(); + // Add header extensions. + for (size_t i = 0; i < kMaxNumExtensions; i++) { + uint8_t id = extensions.GetId(kExtensions[i].type); + if (id != RtpHeaderExtensionMap::kInvalidId) { + config->rtp_extensions.emplace_back(kExtensions[i].name, id); + } + } + return std::make_unique<RtcEventVideoSendStreamConfig>(std::move(config)); +} + +void EventVerifier::VerifyLoggedAlrStateEvent( + const RtcEventAlrState& original_event, + const LoggedAlrStateEvent& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.in_alr(), logged_event.in_alr); +} + +void EventVerifier::VerifyLoggedAudioPlayoutEvent( + const RtcEventAudioPlayout& original_event, + const LoggedAudioPlayoutEvent& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.ssrc(), logged_event.ssrc); +} + +void EventVerifier::VerifyLoggedAudioNetworkAdaptationEvent( + const RtcEventAudioNetworkAdaptation& original_event, + const LoggedAudioNetworkAdaptationEvent& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + + EXPECT_EQ(original_event.config().bitrate_bps, + logged_event.config.bitrate_bps); + EXPECT_EQ(original_event.config().enable_dtx, logged_event.config.enable_dtx); + EXPECT_EQ(original_event.config().enable_fec, logged_event.config.enable_fec); + EXPECT_EQ(original_event.config().frame_length_ms, + logged_event.config.frame_length_ms); + EXPECT_EQ(original_event.config().num_channels, + logged_event.config.num_channels); + + // uplink_packet_loss_fraction + ASSERT_EQ(original_event.config().uplink_packet_loss_fraction.has_value(), + logged_event.config.uplink_packet_loss_fraction.has_value()); + if (original_event.config().uplink_packet_loss_fraction.has_value()) { + const float original = + original_event.config().uplink_packet_loss_fraction.value(); + const float logged = + logged_event.config.uplink_packet_loss_fraction.value(); + const float uplink_packet_loss_fraction_delta = std::abs(original - logged); + EXPECT_LE(uplink_packet_loss_fraction_delta, 0.0001f); + } +} + +void EventVerifier::VerifyLoggedBweDelayBasedUpdate( + const RtcEventBweUpdateDelayBased& original_event, + const LoggedBweDelayBasedUpdate& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.bitrate_bps(), logged_event.bitrate_bps); + EXPECT_EQ(original_event.detector_state(), logged_event.detector_state); +} + +void EventVerifier::VerifyLoggedBweLossBasedUpdate( + const RtcEventBweUpdateLossBased& original_event, + const LoggedBweLossBasedUpdate& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.bitrate_bps(), logged_event.bitrate_bps); + EXPECT_EQ(original_event.fraction_loss(), logged_event.fraction_lost); + EXPECT_EQ(original_event.total_packets(), logged_event.expected_packets); +} + +void EventVerifier::VerifyLoggedBweProbeClusterCreatedEvent( + const RtcEventProbeClusterCreated& original_event, + const LoggedBweProbeClusterCreatedEvent& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.id(), logged_event.id); + EXPECT_EQ(original_event.bitrate_bps(), logged_event.bitrate_bps); + EXPECT_EQ(original_event.min_probes(), logged_event.min_packets); + EXPECT_EQ(original_event.min_bytes(), logged_event.min_bytes); +} + +void EventVerifier::VerifyLoggedBweProbeFailureEvent( + const RtcEventProbeResultFailure& original_event, + const LoggedBweProbeFailureEvent& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.id(), logged_event.id); + EXPECT_EQ(original_event.failure_reason(), logged_event.failure_reason); +} + +void EventVerifier::VerifyLoggedBweProbeSuccessEvent( + const RtcEventProbeResultSuccess& original_event, + const LoggedBweProbeSuccessEvent& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.id(), logged_event.id); + EXPECT_EQ(original_event.bitrate_bps(), logged_event.bitrate_bps); +} + +void EventVerifier::VerifyLoggedDtlsTransportState( + const RtcEventDtlsTransportState& original_event, + const LoggedDtlsTransportState& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.dtls_transport_state(), + logged_event.dtls_transport_state); +} + +void EventVerifier::VerifyLoggedDtlsWritableState( + const RtcEventDtlsWritableState& original_event, + const LoggedDtlsWritableState& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.writable(), logged_event.writable); +} + +void EventVerifier::VerifyLoggedFrameDecoded( + const RtcEventFrameDecoded& original_event, + const LoggedFrameDecoded& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.ssrc(), logged_event.ssrc); + EXPECT_EQ(original_event.render_time_ms(), logged_event.render_time_ms); + EXPECT_EQ(original_event.width(), logged_event.width); + EXPECT_EQ(original_event.height(), logged_event.height); + EXPECT_EQ(original_event.codec(), logged_event.codec); + EXPECT_EQ(original_event.qp(), logged_event.qp); +} + +void EventVerifier::VerifyLoggedIceCandidatePairConfig( + const RtcEventIceCandidatePairConfig& original_event, + const LoggedIceCandidatePairConfig& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + + EXPECT_EQ(original_event.type(), logged_event.type); + EXPECT_EQ(original_event.candidate_pair_id(), logged_event.candidate_pair_id); + EXPECT_EQ(original_event.candidate_pair_desc().local_candidate_type, + logged_event.local_candidate_type); + EXPECT_EQ(original_event.candidate_pair_desc().local_relay_protocol, + logged_event.local_relay_protocol); + EXPECT_EQ(original_event.candidate_pair_desc().local_network_type, + logged_event.local_network_type); + EXPECT_EQ(original_event.candidate_pair_desc().local_address_family, + logged_event.local_address_family); + EXPECT_EQ(original_event.candidate_pair_desc().remote_candidate_type, + logged_event.remote_candidate_type); + EXPECT_EQ(original_event.candidate_pair_desc().remote_address_family, + logged_event.remote_address_family); + EXPECT_EQ(original_event.candidate_pair_desc().candidate_pair_protocol, + logged_event.candidate_pair_protocol); +} + +void EventVerifier::VerifyLoggedIceCandidatePairEvent( + const RtcEventIceCandidatePair& original_event, + const LoggedIceCandidatePairEvent& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + + EXPECT_EQ(original_event.type(), logged_event.type); + EXPECT_EQ(original_event.candidate_pair_id(), logged_event.candidate_pair_id); + if (encoding_type_ == RtcEventLog::EncodingType::NewFormat) { + EXPECT_EQ(original_event.transaction_id(), logged_event.transaction_id); + } +} + +template <typename Event> +void VerifyLoggedRtpHeader(const Event& original_header, + const RTPHeader& logged_header) { + // Standard RTP header. + EXPECT_EQ(original_header.Marker(), logged_header.markerBit); + EXPECT_EQ(original_header.PayloadType(), logged_header.payloadType); + EXPECT_EQ(original_header.SequenceNumber(), logged_header.sequenceNumber); + EXPECT_EQ(original_header.Timestamp(), logged_header.timestamp); + EXPECT_EQ(original_header.Ssrc(), logged_header.ssrc); + + EXPECT_EQ(original_header.header_length(), logged_header.headerLength); + + // TransmissionOffset header extension. + ASSERT_EQ(original_header.template HasExtension<TransmissionOffset>(), + logged_header.extension.hasTransmissionTimeOffset); + if (logged_header.extension.hasTransmissionTimeOffset) { + int32_t offset; + ASSERT_TRUE( + original_header.template GetExtension<TransmissionOffset>(&offset)); + EXPECT_EQ(offset, logged_header.extension.transmissionTimeOffset); + } + + // AbsoluteSendTime header extension. + ASSERT_EQ(original_header.template HasExtension<AbsoluteSendTime>(), + logged_header.extension.hasAbsoluteSendTime); + if (logged_header.extension.hasAbsoluteSendTime) { + uint32_t sendtime; + ASSERT_TRUE( + original_header.template GetExtension<AbsoluteSendTime>(&sendtime)); + EXPECT_EQ(sendtime, logged_header.extension.absoluteSendTime); + } + + // TransportSequenceNumber header extension. + ASSERT_EQ(original_header.template HasExtension<TransportSequenceNumber>(), + logged_header.extension.hasTransportSequenceNumber); + if (logged_header.extension.hasTransportSequenceNumber) { + uint16_t seqnum; + ASSERT_TRUE(original_header.template GetExtension<TransportSequenceNumber>( + &seqnum)); + EXPECT_EQ(seqnum, logged_header.extension.transportSequenceNumber); + } + + // AudioLevel header extension. + ASSERT_EQ(original_header.template HasExtension<AudioLevel>(), + logged_header.extension.hasAudioLevel); + if (logged_header.extension.hasAudioLevel) { + bool voice_activity; + uint8_t audio_level; + ASSERT_TRUE(original_header.template GetExtension<AudioLevel>( + &voice_activity, &audio_level)); + EXPECT_EQ(voice_activity, logged_header.extension.voiceActivity); + EXPECT_EQ(audio_level, logged_header.extension.audioLevel); + } + + // VideoOrientation header extension. + ASSERT_EQ(original_header.template HasExtension<VideoOrientation>(), + logged_header.extension.hasVideoRotation); + if (logged_header.extension.hasVideoRotation) { + uint8_t rotation; + ASSERT_TRUE( + original_header.template GetExtension<VideoOrientation>(&rotation)); + EXPECT_EQ(ConvertCVOByteToVideoRotation(rotation), + logged_header.extension.videoRotation); + } +} + +template <typename Event> +void VerifyLoggedDependencyDescriptor(const Event& packet, + const std::vector<uint8_t>& logged_dd) { + if (webrtc::field_trial::IsDisabled( + "WebRTC-RtcEventLogEncodeDependencyDescriptor")) { + EXPECT_TRUE(logged_dd.empty()); + } else { + rtc::ArrayView<const uint8_t> original = + packet.template GetRawExtension<RtpDependencyDescriptorExtension>(); + EXPECT_EQ(logged_dd.size(), original.size()); + bool dd_is_same = true; + for (size_t i = 0; i < logged_dd.size(); ++i) { + dd_is_same = logged_dd[i] == original[i]; + if (!dd_is_same) { + break; + } + } + EXPECT_TRUE(dd_is_same); + } +} + +void EventVerifier::VerifyLoggedRouteChangeEvent( + const RtcEventRouteChange& original_event, + const LoggedRouteChangeEvent& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.connected(), logged_event.connected); + EXPECT_EQ(original_event.overhead(), logged_event.overhead); +} + +void EventVerifier::VerifyLoggedRemoteEstimateEvent( + const RtcEventRemoteEstimate& original_event, + const LoggedRemoteEstimateEvent& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.link_capacity_lower_, + logged_event.link_capacity_lower); + EXPECT_EQ(original_event.link_capacity_upper_, + logged_event.link_capacity_upper); +} + +void EventVerifier::VerifyLoggedRtpPacketIncoming( + const RtcEventRtpPacketIncoming& original_event, + const LoggedRtpPacketIncoming& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + + EXPECT_EQ(original_event.header_length(), logged_event.rtp.header_length); + + EXPECT_EQ(original_event.packet_length(), logged_event.rtp.total_length); + + // Currently, RTC eventlog encoder-parser can only maintain padding length + // if packet is full padding. + EXPECT_EQ(original_event.padding_length(), + logged_event.rtp.header.paddingLength); + + VerifyLoggedRtpHeader(original_event, logged_event.rtp.header); + VerifyLoggedDependencyDescriptor( + original_event, logged_event.rtp.dependency_descriptor_wire_format); +} + +void EventVerifier::VerifyLoggedRtpPacketOutgoing( + const RtcEventRtpPacketOutgoing& original_event, + const LoggedRtpPacketOutgoing& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + + EXPECT_EQ(original_event.header_length(), logged_event.rtp.header_length); + + EXPECT_EQ(original_event.packet_length(), logged_event.rtp.total_length); + + // Currently, RTC eventlog encoder-parser can only maintain padding length + // if packet is full padding. + EXPECT_EQ(original_event.padding_length(), + logged_event.rtp.header.paddingLength); + + // TODO(terelius): Probe cluster ID isn't parsed, used or tested. Unless + // someone has a strong reason to keep it, it'll be removed. + + VerifyLoggedRtpHeader(original_event, logged_event.rtp.header); + VerifyLoggedDependencyDescriptor( + original_event, logged_event.rtp.dependency_descriptor_wire_format); +} + +void EventVerifier::VerifyLoggedGenericPacketSent( + const RtcEventGenericPacketSent& original_event, + const LoggedGenericPacketSent& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.packet_number(), logged_event.packet_number); + EXPECT_EQ(original_event.overhead_length(), logged_event.overhead_length); + EXPECT_EQ(original_event.payload_length(), logged_event.payload_length); + EXPECT_EQ(original_event.padding_length(), logged_event.padding_length); +} + +void EventVerifier::VerifyLoggedGenericPacketReceived( + const RtcEventGenericPacketReceived& original_event, + const LoggedGenericPacketReceived& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.packet_number(), logged_event.packet_number); + EXPECT_EQ(static_cast<int>(original_event.packet_length()), + logged_event.packet_length); +} + +void EventVerifier::VerifyLoggedGenericAckReceived( + const RtcEventGenericAckReceived& original_event, + const LoggedGenericAckReceived& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + EXPECT_EQ(original_event.packet_number(), logged_event.packet_number); + EXPECT_EQ(original_event.acked_packet_number(), + logged_event.acked_packet_number); + EXPECT_EQ(original_event.receive_acked_packet_time_ms(), + logged_event.receive_acked_packet_time_ms); +} + +void EventVerifier::VerifyLoggedRtcpPacketIncoming( + const RtcEventRtcpPacketIncoming& original_event, + const LoggedRtcpPacketIncoming& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + + ASSERT_EQ(original_event.packet().size(), logged_event.rtcp.raw_data.size()); + EXPECT_EQ( + memcmp(original_event.packet().data(), logged_event.rtcp.raw_data.data(), + original_event.packet().size()), + 0); +} + +void EventVerifier::VerifyLoggedRtcpPacketOutgoing( + const RtcEventRtcpPacketOutgoing& original_event, + const LoggedRtcpPacketOutgoing& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + + ASSERT_EQ(original_event.packet().size(), logged_event.rtcp.raw_data.size()); + EXPECT_EQ( + memcmp(original_event.packet().data(), logged_event.rtcp.raw_data.data(), + original_event.packet().size()), + 0); +} + +void EventVerifier::VerifyReportBlock( + const rtcp::ReportBlock& original_report_block, + const rtcp::ReportBlock& logged_report_block) { + EXPECT_EQ(original_report_block.source_ssrc(), + logged_report_block.source_ssrc()); + EXPECT_EQ(original_report_block.fraction_lost(), + logged_report_block.fraction_lost()); + EXPECT_EQ(original_report_block.cumulative_lost(), + logged_report_block.cumulative_lost()); + EXPECT_EQ(original_report_block.extended_high_seq_num(), + logged_report_block.extended_high_seq_num()); + EXPECT_EQ(original_report_block.jitter(), logged_report_block.jitter()); + EXPECT_EQ(original_report_block.last_sr(), logged_report_block.last_sr()); + EXPECT_EQ(original_report_block.delay_since_last_sr(), + logged_report_block.delay_since_last_sr()); +} + +void EventVerifier::VerifyLoggedSenderReport( + int64_t log_time_ms, + const rtcp::SenderReport& original_sr, + const LoggedRtcpPacketSenderReport& logged_sr) { + EXPECT_EQ(log_time_ms, logged_sr.log_time_ms()); + EXPECT_EQ(original_sr.sender_ssrc(), logged_sr.sr.sender_ssrc()); + EXPECT_EQ(original_sr.ntp(), logged_sr.sr.ntp()); + EXPECT_EQ(original_sr.rtp_timestamp(), logged_sr.sr.rtp_timestamp()); + EXPECT_EQ(original_sr.sender_packet_count(), + logged_sr.sr.sender_packet_count()); + EXPECT_EQ(original_sr.sender_octet_count(), + logged_sr.sr.sender_octet_count()); + ASSERT_EQ(original_sr.report_blocks().size(), + logged_sr.sr.report_blocks().size()); + for (size_t i = 0; i < original_sr.report_blocks().size(); i++) { + VerifyReportBlock(original_sr.report_blocks()[i], + logged_sr.sr.report_blocks()[i]); + } +} + +void EventVerifier::VerifyLoggedReceiverReport( + int64_t log_time_ms, + const rtcp::ReceiverReport& original_rr, + const LoggedRtcpPacketReceiverReport& logged_rr) { + EXPECT_EQ(log_time_ms, logged_rr.log_time_ms()); + EXPECT_EQ(original_rr.sender_ssrc(), logged_rr.rr.sender_ssrc()); + ASSERT_EQ(original_rr.report_blocks().size(), + logged_rr.rr.report_blocks().size()); + for (size_t i = 0; i < original_rr.report_blocks().size(); i++) { + VerifyReportBlock(original_rr.report_blocks()[i], + logged_rr.rr.report_blocks()[i]); + } +} + +void EventVerifier::VerifyLoggedExtendedReports( + int64_t log_time_ms, + const rtcp::ExtendedReports& original_xr, + const LoggedRtcpPacketExtendedReports& logged_xr) { + EXPECT_EQ(log_time_ms, logged_xr.log_time_ms()); + EXPECT_EQ(original_xr.sender_ssrc(), logged_xr.xr.sender_ssrc()); + + EXPECT_EQ(original_xr.rrtr().has_value(), logged_xr.xr.rrtr().has_value()); + if (original_xr.rrtr().has_value() && logged_xr.xr.rrtr().has_value()) { + EXPECT_EQ(original_xr.rrtr()->ntp(), logged_xr.xr.rrtr()->ntp()); + } + + const auto& original_subblocks = original_xr.dlrr().sub_blocks(); + const auto& logged_subblocks = logged_xr.xr.dlrr().sub_blocks(); + ASSERT_EQ(original_subblocks.size(), logged_subblocks.size()); + for (size_t i = 0; i < original_subblocks.size(); i++) { + EXPECT_EQ(original_subblocks[i].ssrc, logged_subblocks[i].ssrc); + EXPECT_EQ(original_subblocks[i].last_rr, logged_subblocks[i].last_rr); + EXPECT_EQ(original_subblocks[i].delay_since_last_rr, + logged_subblocks[i].delay_since_last_rr); + } + + EXPECT_EQ(original_xr.target_bitrate().has_value(), + logged_xr.xr.target_bitrate().has_value()); + if (original_xr.target_bitrate().has_value() && + logged_xr.xr.target_bitrate().has_value()) { + const auto& original_bitrates = + original_xr.target_bitrate()->GetTargetBitrates(); + const auto& logged_bitrates = + logged_xr.xr.target_bitrate()->GetTargetBitrates(); + ASSERT_EQ(original_bitrates.size(), logged_bitrates.size()); + for (size_t i = 0; i < original_bitrates.size(); i++) { + EXPECT_EQ(original_bitrates[i].spatial_layer, + logged_bitrates[i].spatial_layer); + EXPECT_EQ(original_bitrates[i].temporal_layer, + logged_bitrates[i].temporal_layer); + EXPECT_EQ(original_bitrates[i].target_bitrate_kbps, + logged_bitrates[i].target_bitrate_kbps); + } + } +} + +void EventVerifier::VerifyLoggedFir(int64_t log_time_ms, + const rtcp::Fir& original_fir, + const LoggedRtcpPacketFir& logged_fir) { + EXPECT_EQ(log_time_ms, logged_fir.log_time_ms()); + EXPECT_EQ(original_fir.sender_ssrc(), logged_fir.fir.sender_ssrc()); + const auto& original_requests = original_fir.requests(); + const auto& logged_requests = logged_fir.fir.requests(); + ASSERT_EQ(original_requests.size(), logged_requests.size()); + for (size_t i = 0; i < original_requests.size(); i++) { + EXPECT_EQ(original_requests[i].ssrc, logged_requests[i].ssrc); + EXPECT_EQ(original_requests[i].seq_nr, logged_requests[i].seq_nr); + } +} + +void EventVerifier::VerifyLoggedPli(int64_t log_time_ms, + const rtcp::Pli& original_pli, + const LoggedRtcpPacketPli& logged_pli) { + EXPECT_EQ(log_time_ms, logged_pli.log_time_ms()); + EXPECT_EQ(original_pli.sender_ssrc(), logged_pli.pli.sender_ssrc()); + EXPECT_EQ(original_pli.media_ssrc(), logged_pli.pli.media_ssrc()); +} + +void EventVerifier::VerifyLoggedBye(int64_t log_time_ms, + const rtcp::Bye& original_bye, + const LoggedRtcpPacketBye& logged_bye) { + EXPECT_EQ(log_time_ms, logged_bye.log_time_ms()); + EXPECT_EQ(original_bye.sender_ssrc(), logged_bye.bye.sender_ssrc()); + EXPECT_EQ(original_bye.csrcs(), logged_bye.bye.csrcs()); + EXPECT_EQ(original_bye.reason(), logged_bye.bye.reason()); +} + +void EventVerifier::VerifyLoggedNack(int64_t log_time_ms, + const rtcp::Nack& original_nack, + const LoggedRtcpPacketNack& logged_nack) { + EXPECT_EQ(log_time_ms, logged_nack.log_time_ms()); + EXPECT_EQ(original_nack.packet_ids(), logged_nack.nack.packet_ids()); +} + +void EventVerifier::VerifyLoggedTransportFeedback( + int64_t log_time_ms, + const rtcp::TransportFeedback& original_transport_feedback, + const LoggedRtcpPacketTransportFeedback& logged_transport_feedback) { + EXPECT_EQ(log_time_ms, logged_transport_feedback.log_time_ms()); + ASSERT_EQ( + original_transport_feedback.GetReceivedPackets().size(), + logged_transport_feedback.transport_feedback.GetReceivedPackets().size()); + for (size_t i = 0; + i < original_transport_feedback.GetReceivedPackets().size(); i++) { + EXPECT_EQ( + original_transport_feedback.GetReceivedPackets()[i].sequence_number(), + logged_transport_feedback.transport_feedback.GetReceivedPackets()[i] + .sequence_number()); + EXPECT_EQ( + original_transport_feedback.GetReceivedPackets()[i].delta(), + logged_transport_feedback.transport_feedback.GetReceivedPackets()[i] + .delta()); + } +} + +void EventVerifier::VerifyLoggedRemb(int64_t log_time_ms, + const rtcp::Remb& original_remb, + const LoggedRtcpPacketRemb& logged_remb) { + EXPECT_EQ(log_time_ms, logged_remb.log_time_ms()); + EXPECT_EQ(original_remb.ssrcs(), logged_remb.remb.ssrcs()); + EXPECT_EQ(original_remb.bitrate_bps(), logged_remb.remb.bitrate_bps()); +} + +void EventVerifier::VerifyLoggedLossNotification( + int64_t log_time_ms, + const rtcp::LossNotification& original_loss_notification, + const LoggedRtcpPacketLossNotification& logged_loss_notification) { + EXPECT_EQ(log_time_ms, logged_loss_notification.log_time_ms()); + EXPECT_EQ(original_loss_notification.last_decoded(), + logged_loss_notification.loss_notification.last_decoded()); + EXPECT_EQ(original_loss_notification.last_received(), + logged_loss_notification.loss_notification.last_received()); + EXPECT_EQ(original_loss_notification.decodability_flag(), + logged_loss_notification.loss_notification.decodability_flag()); +} + +void EventVerifier::VerifyLoggedStartEvent( + int64_t start_time_us, + int64_t utc_start_time_us, + const LoggedStartEvent& logged_event) const { + EXPECT_EQ(start_time_us / 1000, logged_event.log_time_ms()); + if (encoding_type_ == RtcEventLog::EncodingType::NewFormat) { + EXPECT_EQ(utc_start_time_us / 1000, logged_event.utc_start_time.ms()); + } +} + +void EventVerifier::VerifyLoggedStopEvent( + int64_t stop_time_us, + const LoggedStopEvent& logged_event) const { + EXPECT_EQ(stop_time_us / 1000, logged_event.log_time_ms()); +} + +void VerifyLoggedStreamConfig(const rtclog::StreamConfig& original_config, + const rtclog::StreamConfig& logged_config) { + EXPECT_EQ(original_config.local_ssrc, logged_config.local_ssrc); + EXPECT_EQ(original_config.remote_ssrc, logged_config.remote_ssrc); + EXPECT_EQ(original_config.rtx_ssrc, logged_config.rtx_ssrc); + + EXPECT_EQ(original_config.rtp_extensions.size(), + logged_config.rtp_extensions.size()); + size_t recognized_extensions = 0; + for (size_t i = 0; i < kMaxNumExtensions; i++) { + auto original_id = + GetExtensionId(original_config.rtp_extensions, kExtensions[i].name); + auto logged_id = + GetExtensionId(logged_config.rtp_extensions, kExtensions[i].name); + EXPECT_EQ(original_id, logged_id) + << "IDs for " << kExtensions[i].name << " don't match. Original ID " + << original_id.value_or(-1) << ". Parsed ID " << logged_id.value_or(-1) + << "."; + if (original_id) { + recognized_extensions++; + } + } + EXPECT_EQ(recognized_extensions, original_config.rtp_extensions.size()); +} + +void EventVerifier::VerifyLoggedAudioRecvConfig( + const RtcEventAudioReceiveStreamConfig& original_event, + const LoggedAudioRecvConfig& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + VerifyLoggedStreamConfig(original_event.config(), logged_event.config); +} + +void EventVerifier::VerifyLoggedAudioSendConfig( + const RtcEventAudioSendStreamConfig& original_event, + const LoggedAudioSendConfig& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + VerifyLoggedStreamConfig(original_event.config(), logged_event.config); +} + +void EventVerifier::VerifyLoggedVideoRecvConfig( + const RtcEventVideoReceiveStreamConfig& original_event, + const LoggedVideoRecvConfig& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + VerifyLoggedStreamConfig(original_event.config(), logged_event.config); +} + +void EventVerifier::VerifyLoggedVideoSendConfig( + const RtcEventVideoSendStreamConfig& original_event, + const LoggedVideoSendConfig& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms()); + VerifyLoggedStreamConfig(original_event.config(), logged_event.config); +} + +void EventVerifier::VerifyLoggedNetEqSetMinimumDelay( + const RtcEventNetEqSetMinimumDelay& original_event, + const LoggedNetEqSetMinimumDelayEvent& logged_event) const { + EXPECT_EQ(original_event.timestamp_ms(), logged_event.timestamp.ms()); + EXPECT_EQ(original_event.remote_ssrc(), logged_event.remote_ssrc); + EXPECT_EQ(original_event.minimum_delay_ms(), logged_event.minimum_delay_ms); +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.h b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.h new file mode 100644 index 0000000000..950a622f8b --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_unittest_helper.h @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2016 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_UNITTEST_HELPER_H_ +#define LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_UNITTEST_HELPER_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <memory> +#include <vector> + +#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_dtls_transport_state.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" +#include "logging/rtc_event_log/events/rtc_event_frame_decoded.h" +#include "logging/rtc_event_log/events/rtc_event_generic_ack_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_sent.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.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_remote_estimate.h" +#include "logging/rtc_event_log/events/rtc_event_route_change.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_stream_config.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/source/rtcp_packet/bye.h" +#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h" +#include "modules/rtp_rtcp/source/rtcp_packet/fir.h" +#include "modules/rtp_rtcp/source/rtcp_packet/loss_notification.h" +#include "modules/rtp_rtcp/source/rtcp_packet/nack.h" +#include "modules/rtp_rtcp/source/rtcp_packet/pli.h" +#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/remb.h" +#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" +#include "modules/rtp_rtcp/source/rtp_packet.h" +#include "rtc_base/random.h" + +namespace webrtc { + +namespace test { + +class EventGenerator { + public: + explicit EventGenerator(uint64_t seed) : prng_(seed) {} + + std::unique_ptr<RtcEventAlrState> NewAlrState(); + std::unique_ptr<RtcEventAudioNetworkAdaptation> NewAudioNetworkAdaptation(); + std::unique_ptr<RtcEventAudioPlayout> NewAudioPlayout(uint32_t ssrc); + std::unique_ptr<RtcEventBweUpdateDelayBased> NewBweUpdateDelayBased(); + std::unique_ptr<RtcEventBweUpdateLossBased> NewBweUpdateLossBased(); + std::unique_ptr<RtcEventDtlsTransportState> NewDtlsTransportState(); + std::unique_ptr<RtcEventDtlsWritableState> NewDtlsWritableState(); + std::unique_ptr<RtcEventFrameDecoded> NewFrameDecodedEvent(uint32_t ssrc); + std::unique_ptr<RtcEventGenericAckReceived> NewGenericAckReceived(); + std::unique_ptr<RtcEventGenericPacketReceived> NewGenericPacketReceived(); + std::unique_ptr<RtcEventGenericPacketSent> NewGenericPacketSent(); + std::unique_ptr<RtcEventIceCandidatePair> NewIceCandidatePair(); + std::unique_ptr<RtcEventIceCandidatePairConfig> NewIceCandidatePairConfig(); + std::unique_ptr<RtcEventNetEqSetMinimumDelay> NewNetEqSetMinimumDelay( + uint32_t ssrc); + std::unique_ptr<RtcEventProbeClusterCreated> NewProbeClusterCreated(); + std::unique_ptr<RtcEventProbeResultFailure> NewProbeResultFailure(); + std::unique_ptr<RtcEventProbeResultSuccess> NewProbeResultSuccess(); + std::unique_ptr<RtcEventRouteChange> NewRouteChange(); + std::unique_ptr<RtcEventRemoteEstimate> NewRemoteEstimate(); + std::unique_ptr<RtcEventRtcpPacketIncoming> NewRtcpPacketIncoming(); + std::unique_ptr<RtcEventRtcpPacketOutgoing> NewRtcpPacketOutgoing(); + + rtcp::SenderReport NewSenderReport(); + rtcp::ReceiverReport NewReceiverReport(); + rtcp::ExtendedReports NewExtendedReports(); + rtcp::Nack NewNack(); + rtcp::Remb NewRemb(); + rtcp::Fir NewFir(); + rtcp::Pli NewPli(); + rtcp::Bye NewBye(); + rtcp::TransportFeedback NewTransportFeedback(); + rtcp::LossNotification NewLossNotification(); + + // `all_configured_exts` determines whether the RTP packet exhibits all + // configured extensions, or a random subset thereof. + void RandomizeRtpPacket(size_t payload_size, + size_t padding_size, + uint32_t ssrc, + const RtpHeaderExtensionMap& extension_map, + RtpPacket* rtp_packet, + bool all_configured_exts); + + // `all_configured_exts` determines whether the RTP packet exhibits all + // configured extensions, or a random subset thereof. + std::unique_ptr<RtcEventRtpPacketIncoming> NewRtpPacketIncoming( + uint32_t ssrc, + const RtpHeaderExtensionMap& extension_map, + bool all_configured_exts = true); + + // `all_configured_exts` determines whether the RTP packet exhibits all + // configured extensions, or a random subset thereof. + std::unique_ptr<RtcEventRtpPacketOutgoing> NewRtpPacketOutgoing( + uint32_t ssrc, + const RtpHeaderExtensionMap& extension_map, + bool all_configured_exts = true); + + // `configure_all` determines whether all supported extensions are configured, + // or a random subset. Extensions in `excluded_extensions` will always be + // excluded. + RtpHeaderExtensionMap NewRtpHeaderExtensionMap( + bool configure_all = false, + const std::vector<RTPExtensionType>& excluded_extensions = {}); + + std::unique_ptr<RtcEventAudioReceiveStreamConfig> NewAudioReceiveStreamConfig( + uint32_t ssrc, + const RtpHeaderExtensionMap& extensions); + + std::unique_ptr<RtcEventAudioSendStreamConfig> NewAudioSendStreamConfig( + uint32_t ssrc, + const RtpHeaderExtensionMap& extensions); + + std::unique_ptr<RtcEventVideoReceiveStreamConfig> NewVideoReceiveStreamConfig( + uint32_t ssrc, + const RtpHeaderExtensionMap& extensions); + + std::unique_ptr<RtcEventVideoSendStreamConfig> NewVideoSendStreamConfig( + uint32_t ssrc, + const RtpHeaderExtensionMap& extensions); + + private: + rtcp::ReportBlock NewReportBlock(); + int sent_packet_number_ = 0; + int received_packet_number_ = 0; + + Random prng_; +}; + +class EventVerifier { + public: + explicit EventVerifier(RtcEventLog::EncodingType encoding_type) + : encoding_type_(encoding_type) {} + + void VerifyLoggedAlrStateEvent(const RtcEventAlrState& original_event, + const LoggedAlrStateEvent& logged_event) const; + + void VerifyLoggedAudioPlayoutEvent( + const RtcEventAudioPlayout& original_event, + const LoggedAudioPlayoutEvent& logged_event) const; + + void VerifyLoggedAudioNetworkAdaptationEvent( + const RtcEventAudioNetworkAdaptation& original_event, + const LoggedAudioNetworkAdaptationEvent& logged_event) const; + + void VerifyLoggedBweDelayBasedUpdate( + const RtcEventBweUpdateDelayBased& original_event, + const LoggedBweDelayBasedUpdate& logged_event) const; + + void VerifyLoggedBweLossBasedUpdate( + const RtcEventBweUpdateLossBased& original_event, + const LoggedBweLossBasedUpdate& logged_event) const; + + void VerifyLoggedBweProbeClusterCreatedEvent( + const RtcEventProbeClusterCreated& original_event, + const LoggedBweProbeClusterCreatedEvent& logged_event) const; + + void VerifyLoggedBweProbeFailureEvent( + const RtcEventProbeResultFailure& original_event, + const LoggedBweProbeFailureEvent& logged_event) const; + + void VerifyLoggedBweProbeSuccessEvent( + const RtcEventProbeResultSuccess& original_event, + const LoggedBweProbeSuccessEvent& logged_event) const; + + void VerifyLoggedDtlsTransportState( + const RtcEventDtlsTransportState& original_event, + const LoggedDtlsTransportState& logged_event) const; + + void VerifyLoggedDtlsWritableState( + const RtcEventDtlsWritableState& original_event, + const LoggedDtlsWritableState& logged_event) const; + + void VerifyLoggedFrameDecoded(const RtcEventFrameDecoded& original_event, + const LoggedFrameDecoded& logged_event) const; + + void VerifyLoggedIceCandidatePairConfig( + const RtcEventIceCandidatePairConfig& original_event, + const LoggedIceCandidatePairConfig& logged_event) const; + + void VerifyLoggedIceCandidatePairEvent( + const RtcEventIceCandidatePair& original_event, + const LoggedIceCandidatePairEvent& logged_event) const; + + void VerifyLoggedRouteChangeEvent( + const RtcEventRouteChange& original_event, + const LoggedRouteChangeEvent& logged_event) const; + + void VerifyLoggedRemoteEstimateEvent( + const RtcEventRemoteEstimate& original_event, + const LoggedRemoteEstimateEvent& logged_event) const; + + void VerifyLoggedRtpPacketIncoming( + const RtcEventRtpPacketIncoming& original_event, + const LoggedRtpPacketIncoming& logged_event) const; + + void VerifyLoggedRtpPacketOutgoing( + const RtcEventRtpPacketOutgoing& original_event, + const LoggedRtpPacketOutgoing& logged_event) const; + + void VerifyLoggedGenericPacketSent( + const RtcEventGenericPacketSent& original_event, + const LoggedGenericPacketSent& logged_event) const; + + void VerifyLoggedGenericPacketReceived( + const RtcEventGenericPacketReceived& original_event, + const LoggedGenericPacketReceived& logged_event) const; + + void VerifyLoggedGenericAckReceived( + const RtcEventGenericAckReceived& original_event, + const LoggedGenericAckReceived& logged_event) const; + + template <typename EventType, typename ParsedType> + void VerifyLoggedRtpPacket(const EventType& original_event, + const ParsedType& logged_event) { + static_assert(sizeof(ParsedType) == 0, + "You have to use one of the two defined template " + "specializations of VerifyLoggedRtpPacket"); + } + + template <> + void VerifyLoggedRtpPacket(const RtcEventRtpPacketIncoming& original_event, + const LoggedRtpPacketIncoming& logged_event) { + VerifyLoggedRtpPacketIncoming(original_event, logged_event); + } + + template <> + void VerifyLoggedRtpPacket(const RtcEventRtpPacketOutgoing& original_event, + const LoggedRtpPacketOutgoing& logged_event) { + VerifyLoggedRtpPacketOutgoing(original_event, logged_event); + } + + void VerifyLoggedRtcpPacketIncoming( + const RtcEventRtcpPacketIncoming& original_event, + const LoggedRtcpPacketIncoming& logged_event) const; + + void VerifyLoggedRtcpPacketOutgoing( + const RtcEventRtcpPacketOutgoing& original_event, + const LoggedRtcpPacketOutgoing& logged_event) const; + + void VerifyLoggedSenderReport(int64_t log_time_ms, + const rtcp::SenderReport& original_sr, + const LoggedRtcpPacketSenderReport& logged_sr); + void VerifyLoggedReceiverReport( + int64_t log_time_ms, + const rtcp::ReceiverReport& original_rr, + const LoggedRtcpPacketReceiverReport& logged_rr); + void VerifyLoggedExtendedReports( + int64_t log_time_ms, + const rtcp::ExtendedReports& original_xr, + const LoggedRtcpPacketExtendedReports& logged_xr); + void VerifyLoggedFir(int64_t log_time_ms, + const rtcp::Fir& original_fir, + const LoggedRtcpPacketFir& logged_fir); + void VerifyLoggedPli(int64_t log_time_ms, + const rtcp::Pli& original_pli, + const LoggedRtcpPacketPli& logged_pli); + void VerifyLoggedBye(int64_t log_time_ms, + const rtcp::Bye& original_bye, + const LoggedRtcpPacketBye& logged_bye); + void VerifyLoggedNack(int64_t log_time_ms, + const rtcp::Nack& original_nack, + const LoggedRtcpPacketNack& logged_nack); + void VerifyLoggedTransportFeedback( + int64_t log_time_ms, + const rtcp::TransportFeedback& original_transport_feedback, + const LoggedRtcpPacketTransportFeedback& logged_transport_feedback); + void VerifyLoggedRemb(int64_t log_time_ms, + const rtcp::Remb& original_remb, + const LoggedRtcpPacketRemb& logged_remb); + void VerifyLoggedLossNotification( + int64_t log_time_ms, + const rtcp::LossNotification& original_loss_notification, + const LoggedRtcpPacketLossNotification& logged_loss_notification); + + void VerifyLoggedStartEvent(int64_t start_time_us, + int64_t utc_start_time_us, + const LoggedStartEvent& logged_event) const; + void VerifyLoggedStopEvent(int64_t stop_time_us, + const LoggedStopEvent& logged_event) const; + + void VerifyLoggedAudioRecvConfig( + const RtcEventAudioReceiveStreamConfig& original_event, + const LoggedAudioRecvConfig& logged_event) const; + + void VerifyLoggedAudioSendConfig( + const RtcEventAudioSendStreamConfig& original_event, + const LoggedAudioSendConfig& logged_event) const; + + void VerifyLoggedVideoRecvConfig( + const RtcEventVideoReceiveStreamConfig& original_event, + const LoggedVideoRecvConfig& logged_event) const; + + void VerifyLoggedVideoSendConfig( + const RtcEventVideoSendStreamConfig& original_event, + const LoggedVideoSendConfig& logged_event) const; + + void VerifyLoggedNetEqSetMinimumDelay( + const RtcEventNetEqSetMinimumDelay& original_event, + const LoggedNetEqSetMinimumDelayEvent& logged_event) const; + + private: + void VerifyReportBlock(const rtcp::ReportBlock& original_report_block, + const rtcp::ReportBlock& logged_report_block); + + RtcEventLog::EncodingType encoding_type_; +}; + +} // namespace test +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_UNITTEST_HELPER_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_processor.cc b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_processor.cc new file mode 100644 index 0000000000..e1024b1b22 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_processor.cc @@ -0,0 +1,57 @@ +/* + * Copyright 2019 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 "logging/rtc_event_log/rtc_event_processor.h" + +#include "rtc_base/numerics/sequence_number_util.h" + +namespace webrtc { + +RtcEventProcessor::RtcEventProcessor() = default; +RtcEventProcessor::~RtcEventProcessor() = default; + +void RtcEventProcessor::ProcessEventsInOrder() { + // `event_lists_` is a min-heap of lists ordered by the timestamp of the + // first element in the list. We therefore process the first element of the + // first list, then reinsert the remainder of that list into the heap + // if the list still contains unprocessed elements. + std::make_heap(event_lists_.begin(), event_lists_.end(), Cmp); + + while (!event_lists_.empty()) { + event_lists_.front()->ProcessNext(); + std::pop_heap(event_lists_.begin(), event_lists_.end(), Cmp); + if (event_lists_.back()->IsEmpty()) { + event_lists_.pop_back(); + } else { + std::push_heap(event_lists_.begin(), event_lists_.end(), Cmp); + } + } +} + +bool RtcEventProcessor::Cmp(const RtcEventProcessor::ListPtrType& a, + const RtcEventProcessor::ListPtrType& b) { + int64_t time_diff = a->GetNextTime() - b->GetNextTime(); + if (time_diff != 0) + return time_diff > 0; + + if (a->GetTypeOrder() != b->GetTypeOrder()) + return a->GetTypeOrder() > b->GetTypeOrder(); + + absl::optional<uint16_t> wrapped_seq_num_a = a->GetTransportSeqNum(); + absl::optional<uint16_t> wrapped_seq_num_b = b->GetTransportSeqNum(); + if (wrapped_seq_num_a && wrapped_seq_num_b) { + return AheadOf<uint16_t>(*wrapped_seq_num_a, *wrapped_seq_num_b); + } else if (wrapped_seq_num_a.has_value() != wrapped_seq_num_b.has_value()) { + return wrapped_seq_num_a.has_value(); + } + + return a->GetInsertionOrder() > b->GetInsertionOrder(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_processor.h b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_processor.h new file mode 100644 index 0000000000..a46f390862 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_processor.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_RTC_EVENT_PROCESSOR_H_ +#define LOGGING_RTC_EVENT_LOG_RTC_EVENT_PROCESSOR_H_ + +#include <stdint.h> + +#include <algorithm> +#include <memory> +#include <utility> +#include <vector> + +#include "api/function_view.h" +#include "logging/rtc_event_log/rtc_event_processor_order.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// This file contains helper class used to process the elements of two or more +// sorted lists in timestamp order. The effect is the same as doing a merge step +// in the merge-sort algorithm but without copying the elements or modifying the +// lists. + +namespace event_processor_impl { +// Interface to allow "merging" lists of different types. ProcessNext() +// processes the next unprocessed element in the list. IsEmpty() checks if all +// elements have been processed. GetNextTime returns the timestamp of the next +// unprocessed element. +class ProcessableEventListInterface { + public: + virtual ~ProcessableEventListInterface() = default; + virtual void ProcessNext() = 0; + virtual bool IsEmpty() const = 0; + virtual int64_t GetNextTime() const = 0; + virtual int GetTypeOrder() const = 0; + virtual absl::optional<uint16_t> GetTransportSeqNum() const = 0; + virtual int GetInsertionOrder() const = 0; +}; + +// ProcessableEventList encapsulates a list of events and a function that will +// be applied to each element of the list. +template <typename Iterator, typename T> +class ProcessableEventList : public ProcessableEventListInterface { + public: + ProcessableEventList(Iterator begin, + Iterator end, + std::function<void(const T&)> f, + int type_order, + std::function<absl::optional<uint16_t>(const T&)> + transport_seq_num_accessor, + int insertion_order) + : begin_(begin), + end_(end), + f_(f), + type_order_(type_order), + transport_seq_num_accessor_(transport_seq_num_accessor), + insertion_order_(insertion_order) {} + + void ProcessNext() override { + RTC_DCHECK(!IsEmpty()); + f_(*begin_); + ++begin_; + } + + bool IsEmpty() const override { return begin_ == end_; } + + int64_t GetNextTime() const override { + RTC_DCHECK(!IsEmpty()); + return begin_->log_time_us(); + } + + int GetTypeOrder() const override { return type_order_; } + + absl::optional<uint16_t> GetTransportSeqNum() const override { + RTC_DCHECK(!IsEmpty()); + return transport_seq_num_accessor_(*begin_); + } + + int GetInsertionOrder() const override { return insertion_order_; } + + private: + Iterator begin_; + Iterator end_; + std::function<void(const T&)> f_; + int type_order_; + std::function<absl::optional<uint16_t>(const T&)> transport_seq_num_accessor_; + int insertion_order_; +}; + +} // namespace event_processor_impl + +// Helper class used to "merge" two or more lists of ordered RtcEventLog events +// so that they can be treated as a single ordered list. Since the individual +// lists may have different types, we need to access the lists via pointers to +// the common base class. +// +// Usage example: +// ParsedRtcEventLogNew log; +// auto incoming_handler = [] (LoggedRtcpPacketIncoming elem) { ... }; +// auto outgoing_handler = [] (LoggedRtcpPacketOutgoing elem) { ... }; +// +// RtcEventProcessor processor; +// processor.AddEvents(log.incoming_rtcp_packets(), +// incoming_handler); +// processor.AddEvents(log.outgoing_rtcp_packets(), +// outgoing_handler); +// processor.ProcessEventsInOrder(); +class RtcEventProcessor { + public: + RtcEventProcessor(); + ~RtcEventProcessor(); + // The elements of each list is processed in the index order. To process all + // elements in all lists in timestamp order, each list needs to be sorted in + // timestamp order prior to insertion. + // N.B. `iterable` is not owned by RtcEventProcessor. The caller must ensure + // that the iterable outlives RtcEventProcessor and it must not be modified + // until processing has finished. + template <typename Iterable> + void AddEvents( + const Iterable& iterable, + std::function<void(const typename Iterable::value_type&)> handler) { + using ValueType = + typename std::remove_const<typename Iterable::value_type>::type; + AddEvents(iterable, handler, TieBreaker<ValueType>::type_order, + TieBreaker<ValueType>::transport_seq_num_accessor, + num_insertions_); + } + + template <typename Iterable> + void AddEvents( + const Iterable& iterable, + std::function<void(const typename Iterable::value_type&)> handler, + PacketDirection direction) { + using ValueType = + typename std::remove_const<typename Iterable::value_type>::type; + AddEvents(iterable, handler, TieBreaker<ValueType>::type_order(direction), + TieBreaker<ValueType>::transport_seq_num_accessor, + num_insertions_); + } + + template <typename Iterable> + void AddEvents( + const Iterable& iterable, + std::function<void(const typename Iterable::value_type&)> handler, + int type_order, + std::function<absl::optional<uint16_t>( + const typename Iterable::value_type&)> transport_seq_num_accessor, + int insertion_order) { + if (iterable.begin() == iterable.end()) + return; + num_insertions_++; + event_lists_.push_back( + std::make_unique<event_processor_impl::ProcessableEventList< + typename Iterable::const_iterator, typename Iterable::value_type>>( + iterable.begin(), iterable.end(), handler, type_order, + transport_seq_num_accessor, insertion_order)); + } + + void ProcessEventsInOrder(); + + private: + using ListPtrType = + std::unique_ptr<event_processor_impl::ProcessableEventListInterface>; + // Comparison function to make `event_lists_` into a min heap. + static bool Cmp(const ListPtrType& a, const ListPtrType& b); + + std::vector<ListPtrType> event_lists_; + int num_insertions_ = 0; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_RTC_EVENT_PROCESSOR_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_processor_order.h b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_processor_order.h new file mode 100644 index 0000000000..47ef346d5f --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_processor_order.h @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2023 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_RTC_EVENT_PROCESSOR_ORDER_H_ +#define LOGGING_RTC_EVENT_LOG_RTC_EVENT_PROCESSOR_ORDER_H_ + +#include <stdint.h> + +#include "absl/types/optional.h" +#include "api/function_view.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/rtc_event_log_parser.h" + +namespace webrtc { + +// The RTC event log only uses millisecond precision timestamps +// and doesn't preserve order between events in different batches. +// This is a heuristic to order events in a way that preserves +// "typical" dependencies, e.g. we receive packets before we +// send feedback about them, and RTP packets sent or received +// during the same millisecond are in sequence number order. + +enum class TypeOrder { + Start, + // Connectivity and stream configurations before incoming packets + StreamConfig, + IceCondidateConfig, + IceCandidateEvent, + DtlsTransportState, + DtlsWritable, + RouteChange, + // Incoming packets + RtpIn, + RtcpIn, + GenericPacketIn, + GenericAckIn, + // BWE depends on incoming feedback (send side estimation) + // or incoming media packets (receive side estimation). + // Delay-based BWE depends on probe results. + // Loss-based BWE depends on delay-based BWE. + // Loss-based BWE may trigger new probes. + BweRemoteEstimate, + BweProbeFailure, + BweProbeSuccess, + BweDelayBased, + BweLossBased, + BweProbeCreated, + // General processing events. No obvious order. + AudioNetworkAdaptation, + NetEqSetMinDelay, + AudioPlayout, + FrameDecoded, + // Outgoing packets and feedback depends on BWE and might depend on + // processing. + RtpOut, + RtcpOut, + GenericPacketOut, + // Alr is updated after a packet is sent. + AlrState, + Stop, +}; + +template <typename T> +class TieBreaker { + static_assert(sizeof(T) != sizeof(T), + "Specialize TieBreaker to define an order for the event type."); +}; + +template <> +class TieBreaker<LoggedStartEvent> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::Start); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedStartEvent&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedStopEvent> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::Stop); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedStopEvent&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedAudioRecvConfig> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::StreamConfig); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedAudioRecvConfig&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedAudioSendConfig> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::StreamConfig); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedAudioSendConfig&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedVideoRecvConfig> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::StreamConfig); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedVideoRecvConfig&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedVideoSendConfig> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::StreamConfig); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedVideoSendConfig&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedIceCandidatePairConfig> { + public: + static constexpr int type_order = + static_cast<int>(TypeOrder::IceCondidateConfig); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedIceCandidatePairConfig&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedIceCandidatePairEvent> { + public: + static constexpr int type_order = + static_cast<int>(TypeOrder::IceCandidateEvent); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedIceCandidatePairEvent&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedDtlsTransportState> { + public: + static constexpr int type_order = + static_cast<int>(TypeOrder::DtlsTransportState); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedDtlsTransportState&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedDtlsWritableState> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::DtlsWritable); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedDtlsWritableState&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedRouteChangeEvent> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::RouteChange); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedRouteChangeEvent&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedRemoteEstimateEvent> { + public: + static constexpr int type_order = + static_cast<int>(TypeOrder::BweRemoteEstimate); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedRemoteEstimateEvent&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedBweProbeFailureEvent> { + public: + static constexpr int type_order = + static_cast<int>(TypeOrder::BweProbeFailure); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedBweProbeFailureEvent&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedBweProbeSuccessEvent> { + public: + static constexpr int type_order = + static_cast<int>(TypeOrder::BweProbeSuccess); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedBweProbeSuccessEvent&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedBweDelayBasedUpdate> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::BweDelayBased); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedBweDelayBasedUpdate&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedBweLossBasedUpdate> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::BweLossBased); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedBweLossBasedUpdate&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedBweProbeClusterCreatedEvent> { + public: + static constexpr int type_order = + static_cast<int>(TypeOrder::BweProbeCreated); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedBweProbeClusterCreatedEvent&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedAudioNetworkAdaptationEvent> { + public: + static constexpr int type_order = + static_cast<int>(TypeOrder::AudioNetworkAdaptation); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedAudioNetworkAdaptationEvent&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedNetEqSetMinimumDelayEvent> { + public: + static constexpr int type_order = + static_cast<int>(TypeOrder::NetEqSetMinDelay); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedNetEqSetMinimumDelayEvent&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedAudioPlayoutEvent> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::AudioPlayout); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedAudioPlayoutEvent&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedFrameDecoded> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::FrameDecoded); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedFrameDecoded&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedGenericPacketReceived> { + public: + static constexpr int type_order = + static_cast<int>(TypeOrder::GenericPacketIn); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedGenericPacketReceived&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedGenericAckReceived> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::GenericAckIn); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedGenericAckReceived&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedGenericPacketSent> { + public: + static constexpr int type_order = + static_cast<int>(TypeOrder::GenericPacketOut); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedGenericPacketSent&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedRtpPacket> { + public: + static constexpr int type_order(PacketDirection direction) { + return static_cast<int>(direction == PacketDirection::kIncomingPacket + ? TypeOrder::RtpIn + : TypeOrder::RtpOut); + } + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedRtpPacket& p) { + return p.header.extension.hasTransportSequenceNumber + ? p.header.extension.transportSequenceNumber + : absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedPacketInfo> { + public: + static constexpr int type_order(PacketDirection direction) { + return static_cast<int>(direction == PacketDirection::kIncomingPacket + ? TypeOrder::RtpIn + : TypeOrder::RtpOut); + } + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedPacketInfo& p) { + return p.has_transport_seq_no ? p.transport_seq_no + : absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedRtpPacketIncoming> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::RtpIn); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedRtpPacketIncoming& p) { + return p.rtp.header.extension.hasTransportSequenceNumber + ? p.rtp.header.extension.transportSequenceNumber + : absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedRtpPacketOutgoing> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::RtpOut); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedRtpPacketOutgoing& p) { + return p.rtp.header.extension.hasTransportSequenceNumber + ? p.rtp.header.extension.transportSequenceNumber + : absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedRtcpPacketIncoming> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::RtcpIn); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedRtcpPacketIncoming&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedRtcpPacketOutgoing> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::RtcpOut); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedRtcpPacketOutgoing&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedRtcpPacketTransportFeedback> { + public: + static constexpr int type_order(PacketDirection direction) { + return static_cast<int>(direction == PacketDirection::kIncomingPacket + ? TypeOrder::RtcpIn + : TypeOrder::RtcpOut); + } + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedRtcpPacketTransportFeedback&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedRtcpPacketReceiverReport> { + public: + static constexpr int type_order(PacketDirection direction) { + return static_cast<int>(direction == PacketDirection::kIncomingPacket + ? TypeOrder::RtcpIn + : TypeOrder::RtcpOut); + } + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedRtcpPacketReceiverReport&) { + return absl::optional<uint16_t>(); + } +}; + +template <> +class TieBreaker<LoggedAlrStateEvent> { + public: + static constexpr int type_order = static_cast<int>(TypeOrder::AlrState); + static absl::optional<uint16_t> transport_seq_num_accessor( + const LoggedAlrStateEvent&) { + return absl::optional<uint16_t>(); + } +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_RTC_EVENT_PROCESSOR_ORDER_H_ diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_processor_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_processor_unittest.cc new file mode 100644 index 0000000000..d57408d1e6 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_processor_unittest.cc @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2018 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 "logging/rtc_event_log/rtc_event_processor.h" + +#include <stddef.h> + +#include <cstdint> +#include <initializer_list> +#include <limits> + +#include "absl/memory/memory.h" +#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "rtc_base/checks.h" +#include "rtc_base/random.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { +LoggedStartEvent CreateEvent(int64_t time_ms, int64_t utc_time_ms) { + return LoggedStartEvent(Timestamp::Millis(time_ms), + Timestamp::Millis(utc_time_ms)); +} + +std::vector<LoggedStartEvent> CreateEventList( + std::initializer_list<int64_t> timestamp_list) { + std::vector<LoggedStartEvent> v; + for (int64_t timestamp_ms : timestamp_list) { + v.emplace_back(Timestamp::Millis(timestamp_ms)); + } + return v; +} + +std::vector<std::vector<LoggedStartEvent>> +CreateRandomEventLists(size_t num_lists, size_t num_elements, uint64_t seed) { + Random prng(seed); + std::vector<std::vector<LoggedStartEvent>> lists(num_lists); + for (size_t elem = 0; elem < num_elements; elem++) { + uint32_t i = prng.Rand(0u, num_lists - 1); + int64_t timestamp_ms = elem; + lists[i].emplace_back(Timestamp::Millis(timestamp_ms)); + } + return lists; +} + +LoggedRtpPacket CreateRtpPacket(int64_t time_ms, + uint32_t ssrc, + absl::optional<uint16_t> transport_seq_num) { + RTPHeader header; + header.ssrc = ssrc; + header.timestamp = static_cast<uint32_t>(time_ms); + header.paddingLength = 0; + header.headerLength = 20; + header.extension.hasTransportSequenceNumber = transport_seq_num.has_value(); + if (transport_seq_num.has_value()) { + header.extension.transportSequenceNumber = transport_seq_num.value(); + } + return LoggedRtpPacket(Timestamp::Millis(time_ms), header, 20, 1000); +} + +} // namespace + +TEST(RtcEventProcessor, NoList) { + RtcEventProcessor processor; + processor.ProcessEventsInOrder(); // Don't crash but do nothing. +} + +TEST(RtcEventProcessor, EmptyList) { + auto not_called = [](LoggedStartEvent /*elem*/) { EXPECT_TRUE(false); }; + std::vector<LoggedStartEvent> events; + RtcEventProcessor processor; + + processor.AddEvents(events, not_called); + processor.ProcessEventsInOrder(); // Don't crash but do nothing. +} + +TEST(RtcEventProcessor, OneList) { + std::vector<LoggedStartEvent> result; + auto f = [&result](LoggedStartEvent elem) { result.push_back(elem); }; + + std::vector<LoggedStartEvent> events(CreateEventList({1, 2, 3, 4})); + RtcEventProcessor processor; + processor.AddEvents(events, f); + processor.ProcessEventsInOrder(); + + std::vector<int64_t> expected_results{1, 2, 3, 4}; + ASSERT_EQ(result.size(), expected_results.size()); + for (size_t i = 0; i < expected_results.size(); i++) { + EXPECT_EQ(result[i].log_time_ms(), expected_results[i]); + } +} + +TEST(RtcEventProcessor, MergeTwoLists) { + std::vector<LoggedStartEvent> result; + auto f = [&result](LoggedStartEvent elem) { result.push_back(elem); }; + + std::vector<LoggedStartEvent> events1(CreateEventList({1, 2, 4, 7, 8, 9})); + std::vector<LoggedStartEvent> events2(CreateEventList({3, 5, 6, 10})); + RtcEventProcessor processor; + processor.AddEvents(events1, f); + processor.AddEvents(events2, f); + processor.ProcessEventsInOrder(); + + std::vector<int64_t> expected_results{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + ASSERT_EQ(result.size(), expected_results.size()); + for (size_t i = 0; i < expected_results.size(); i++) { + EXPECT_EQ(result[i].log_time_ms(), expected_results[i]); + } +} + +TEST(RtcEventProcessor, MergeTwoListsWithDuplicatedElements) { + std::vector<LoggedStartEvent> result; + auto f = [&result](LoggedStartEvent elem) { result.push_back(elem); }; + + std::vector<LoggedStartEvent> events1(CreateEventList({1, 2, 2, 3, 5, 5})); + std::vector<LoggedStartEvent> events2(CreateEventList({1, 3, 4, 4})); + RtcEventProcessor processor; + processor.AddEvents(events1, f); + processor.AddEvents(events2, f); + processor.ProcessEventsInOrder(); + + std::vector<int64_t> expected_results{1, 1, 2, 2, 3, 3, 4, 4, 5, 5}; + ASSERT_EQ(result.size(), expected_results.size()); + for (size_t i = 0; i < expected_results.size(); i++) { + EXPECT_EQ(result[i].log_time_ms(), expected_results[i]); + } +} + +TEST(RtcEventProcessor, MergeManyLists) { + std::vector<LoggedStartEvent> result; + auto f = [&result](LoggedStartEvent elem) { result.push_back(elem); }; + + constexpr size_t kNumLists = 5; + constexpr size_t kNumElems = 30; + constexpr uint64_t kSeed = 0xF3C6B91F; + std::vector<std::vector<LoggedStartEvent>> lists( + CreateRandomEventLists(kNumLists, kNumElems, kSeed)); + RTC_DCHECK_EQ(lists.size(), kNumLists); + RtcEventProcessor processor; + for (const auto& list : lists) { + processor.AddEvents(list, f); + } + processor.ProcessEventsInOrder(); + + std::vector<int64_t> expected_results(kNumElems); + std::iota(expected_results.begin(), expected_results.end(), 0); + ASSERT_EQ(result.size(), expected_results.size()); + for (size_t i = 0; i < expected_results.size(); i++) { + EXPECT_EQ(result[i].log_time_ms(), expected_results[i]); + } +} + +TEST(RtcEventProcessor, DifferentTypes) { + std::vector<int64_t> result; + auto f1 = [&result](LoggedStartEvent elem) { + result.push_back(elem.log_time_ms()); + }; + auto f2 = [&result](LoggedStopEvent elem) { + result.push_back(elem.log_time_ms()); + }; + + std::vector<LoggedStartEvent> events1{LoggedStartEvent(Timestamp::Millis(2))}; + std::vector<LoggedStopEvent> events2{LoggedStopEvent(Timestamp::Millis(1))}; + RtcEventProcessor processor; + processor.AddEvents(events1, f1); + processor.AddEvents(events2, f2); + processor.ProcessEventsInOrder(); + + std::vector<int64_t> expected_results{1, 2}; + ASSERT_EQ(result.size(), expected_results.size()); + for (size_t i = 0; i < expected_results.size(); i++) { + EXPECT_EQ(result[i], expected_results[i]); + } +} + +TEST(RtcEventProcessor, IncomingPacketBeforeOutgoingFeedback) { + EXPECT_LT(TieBreaker<LoggedRtpPacketIncoming>::type_order, + TieBreaker<LoggedRtcpPacketOutgoing>::type_order); +} + +TEST(RtcEventProcessor, PacketWrapperTypesOrderedAsRtp) { + EXPECT_EQ(TieBreaker<LoggedRtpPacketIncoming>::type_order, + TieBreaker<LoggedRtpPacket>::type_order( + PacketDirection::kIncomingPacket)); + EXPECT_EQ(TieBreaker<LoggedRtpPacketOutgoing>::type_order, + TieBreaker<LoggedRtpPacket>::type_order( + PacketDirection::kOutgoingPacket)); + + EXPECT_EQ(TieBreaker<LoggedRtpPacketIncoming>::type_order, + TieBreaker<LoggedPacketInfo>::type_order( + PacketDirection::kIncomingPacket)); + EXPECT_EQ(TieBreaker<LoggedRtpPacketOutgoing>::type_order, + TieBreaker<LoggedPacketInfo>::type_order( + PacketDirection::kOutgoingPacket)); +} + +TEST(RtcEventProcessor, IncomingFeedbackBeforeBwe) { + EXPECT_LT(TieBreaker<LoggedRtcpPacketIncoming>::type_order, + TieBreaker<LoggedBweProbeSuccessEvent>::type_order); + EXPECT_LT(TieBreaker<LoggedRtcpPacketIncoming>::type_order, + TieBreaker<LoggedRemoteEstimateEvent>::type_order); + EXPECT_LT(TieBreaker<LoggedRtcpPacketIncoming>::type_order, + TieBreaker<LoggedBweProbeSuccessEvent>::type_order); + EXPECT_LT(TieBreaker<LoggedRtcpPacketIncoming>::type_order, + TieBreaker<LoggedBweProbeFailureEvent>::type_order); + EXPECT_LT(TieBreaker<LoggedRtcpPacketIncoming>::type_order, + TieBreaker<LoggedBweDelayBasedUpdate>::type_order); + EXPECT_LT(TieBreaker<LoggedRtcpPacketIncoming>::type_order, + TieBreaker<LoggedBweLossBasedUpdate>::type_order); + EXPECT_LT(TieBreaker<LoggedRtcpPacketIncoming>::type_order, + TieBreaker<LoggedBweProbeClusterCreatedEvent>::type_order); +} + +TEST(RtcEventProcessor, RtpPacketsInTransportSeqNumOrder) { + std::vector<LoggedRtpPacket> ssrc_1234{ + CreateRtpPacket(1, 1234, absl::nullopt), + CreateRtpPacket(1, 1234, absl::nullopt)}; + std::vector<LoggedRtpPacket> ssrc_2345{CreateRtpPacket(1, 2345, 2), + CreateRtpPacket(1, 2345, 3), + CreateRtpPacket(1, 2345, 6)}; + std::vector<LoggedRtpPacket> ssrc_3456{CreateRtpPacket(1, 3456, 1), + CreateRtpPacket(1, 3456, 4), + CreateRtpPacket(1, 3456, 5)}; + + // Store SSRC and transport sequence number for each processed packet. + std::vector<std::pair<uint32_t, absl::optional<uint16_t>>> results; + auto get_packet = [&results](const LoggedRtpPacket& packet) { + absl::optional<uint16_t> transport_seq_num; + if (packet.header.extension.hasTransportSequenceNumber) + transport_seq_num = packet.header.extension.transportSequenceNumber; + results.emplace_back(packet.header.ssrc, transport_seq_num); + }; + + RtcEventProcessor processor; + processor.AddEvents(ssrc_1234, get_packet, PacketDirection::kIncomingPacket); + processor.AddEvents(ssrc_2345, get_packet, PacketDirection::kIncomingPacket); + processor.AddEvents(ssrc_3456, get_packet, PacketDirection::kIncomingPacket); + processor.ProcessEventsInOrder(); + + std::vector<std::pair<uint32_t, absl::optional<uint16_t>>> expected{ + {1234, absl::nullopt}, + {1234, absl::nullopt}, + {3456, 1}, + {2345, 2}, + {2345, 3}, + {3456, 4}, + {3456, 5}, + {2345, 6}}; + EXPECT_THAT(results, testing::ElementsAreArray(expected)); +} + +TEST(RtcEventProcessor, TransportSeqNumOrderHandlesWrapAround) { + std::vector<LoggedRtpPacket> ssrc_1234{ + CreateRtpPacket(0, 1234, std::numeric_limits<uint16_t>::max() - 1), + CreateRtpPacket(1, 1234, 1), CreateRtpPacket(1, 1234, 2)}; + std::vector<LoggedRtpPacket> ssrc_2345{ + CreateRtpPacket(1, 2345, std::numeric_limits<uint16_t>::max()), + CreateRtpPacket(1, 2345, 0), CreateRtpPacket(1, 2345, 3)}; + + // Store SSRC and transport sequence number for each processed packet. + std::vector<std::pair<uint32_t, absl::optional<uint16_t>>> results; + auto get_packet = [&results](const LoggedRtpPacket& packet) { + absl::optional<uint16_t> transport_seq_num; + if (packet.header.extension.hasTransportSequenceNumber) + transport_seq_num = packet.header.extension.transportSequenceNumber; + results.emplace_back(packet.header.ssrc, transport_seq_num); + }; + + RtcEventProcessor processor; + processor.AddEvents(ssrc_1234, get_packet, PacketDirection::kOutgoingPacket); + processor.AddEvents(ssrc_2345, get_packet, PacketDirection::kOutgoingPacket); + processor.ProcessEventsInOrder(); + + std::vector<std::pair<uint32_t, absl::optional<uint16_t>>> expected{ + {1234, std::numeric_limits<uint16_t>::max() - 1}, + {2345, std::numeric_limits<uint16_t>::max()}, + {2345, 0}, + {1234, 1}, + {1234, 2}, + {2345, 3}}; + EXPECT_THAT(results, testing::ElementsAreArray(expected)); +} + +TEST(RtcEventProcessor, InsertionOrderIfNoTransportSeqNum) { + std::vector<LoggedStartEvent> events1{CreateEvent(1, 222)}; + std::vector<LoggedStartEvent> events2{CreateEvent(1, 111)}; + std::vector<LoggedStartEvent> events3{CreateEvent(1, 333)}; + + std::vector<int64_t> results; + auto get_utc_time = [&results](const LoggedStartEvent& elem) { + results.push_back(elem.utc_time().ms()); + }; + + RtcEventProcessor processor; + processor.AddEvents(events1, get_utc_time); + processor.AddEvents(events2, get_utc_time); + processor.AddEvents(events3, get_utc_time); + processor.ProcessEventsInOrder(); + + EXPECT_THAT(results, testing::ElementsAreArray({222, 111, 333})); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_stream_config.cc b/third_party/libwebrtc/logging/rtc_event_log/rtc_stream_config.cc new file mode 100644 index 0000000000..aa107c80bc --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_stream_config.cc @@ -0,0 +1,49 @@ +/* + * 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 "logging/rtc_event_log/rtc_stream_config.h" + +#include "absl/strings/string_view.h" + +namespace webrtc { +namespace rtclog { + +StreamConfig::StreamConfig() {} + +StreamConfig::~StreamConfig() {} + +StreamConfig::StreamConfig(const StreamConfig& other) = default; + +bool StreamConfig::operator==(const StreamConfig& other) const { + return local_ssrc == other.local_ssrc && remote_ssrc == other.remote_ssrc && + rtx_ssrc == other.rtx_ssrc && rsid == other.rsid && + remb == other.remb && rtcp_mode == other.rtcp_mode && + rtp_extensions == other.rtp_extensions && codecs == other.codecs; +} + +bool StreamConfig::operator!=(const StreamConfig& other) const { + return !(*this == other); +} + +StreamConfig::Codec::Codec(absl::string_view payload_name, + int payload_type, + int rtx_payload_type) + : payload_name(payload_name), + payload_type(payload_type), + rtx_payload_type(rtx_payload_type) {} + +bool StreamConfig::Codec::operator==(const Codec& other) const { + return payload_name == other.payload_name && + payload_type == other.payload_type && + rtx_payload_type == other.rtx_payload_type; +} + +} // namespace rtclog +} // namespace webrtc diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_stream_config.h b/third_party/libwebrtc/logging/rtc_event_log/rtc_stream_config.h new file mode 100644 index 0000000000..d114332d34 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_stream_config.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_RTC_STREAM_CONFIG_H_ +#define LOGGING_RTC_EVENT_LOG_RTC_STREAM_CONFIG_H_ + +#include <stdint.h> + +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "api/rtp_headers.h" +#include "api/rtp_parameters.h" + +namespace webrtc { +namespace rtclog { + +struct StreamConfig { + StreamConfig(); + StreamConfig(const StreamConfig& other); + ~StreamConfig(); + + bool operator==(const StreamConfig& other) const; + bool operator!=(const StreamConfig& other) const; + + uint32_t local_ssrc = 0; + uint32_t remote_ssrc = 0; + uint32_t rtx_ssrc = 0; + std::string rsid; + + bool remb = false; + std::vector<RtpExtension> rtp_extensions; + + RtcpMode rtcp_mode = RtcpMode::kReducedSize; + + struct Codec { + Codec(absl::string_view payload_name, + int payload_type, + int rtx_payload_type); + + bool operator==(const Codec& other) const; + + std::string payload_name; + int payload_type; + int rtx_payload_type; + }; + + std::vector<Codec> codecs; +}; + +} // namespace rtclog +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_RTC_STREAM_CONFIG_H_ |