summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/logging/rtc_event_log/encoder
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/logging/rtc_event_log/encoder')
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/bit_writer.cc49
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/bit_writer.h61
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/blob_encoding.cc92
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/blob_encoding.h53
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/blob_encoding_unittest.cc152
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/delta_encoding.cc839
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/delta_encoding.h49
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/delta_encoding_unittest.cc693
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/optional_blob_encoding.cc117
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/optional_blob_encoding.h40
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/optional_blob_encoding_unittest.cc213
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder.h36
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_common.cc40
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h93
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_common_unittest.cc84
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc814
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h110
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc1970
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h164
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc1416
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.cc164
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h46
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/var_int.cc77
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/var_int.h50
24 files changed, 7422 insertions, 0 deletions
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(&params, 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_