Use delta-encoding in new WebRTC event logs
The new event log format makes use of delta encoding to compress
parts of the log.
Bug: webrtc:8111
Change-Id: I7bec839555323a7537dcec831d4ac1d5eb109932
Reviewed-on: https://webrtc-review.googlesource.com/c/109161
Commit-Queue: Elad Alon <eladalon@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25584}
diff --git a/logging/BUILD.gn b/logging/BUILD.gn
index 27977d3..cc19ec6 100644
--- a/logging/BUILD.gn
+++ b/logging/BUILD.gn
@@ -153,6 +153,8 @@
]
}
+# TODO(eladalon): Break down into (1) encoder and (2) decoder; we don't need
+# the decoder code in the WebRTC library, only in unit tests and tools.
rtc_static_library("rtc_event_log_impl_encoder") {
visibility = [ "*" ]
sources = [
@@ -160,6 +162,8 @@
"rtc_event_log/encoder/blob_encoding.h",
"rtc_event_log/encoder/delta_encoding.cc",
"rtc_event_log/encoder/delta_encoding.h",
+ "rtc_event_log/encoder/rtc_event_log_encoder_common.cc",
+ "rtc_event_log/encoder/rtc_event_log_encoder_common.h",
"rtc_event_log/encoder/varint.cc",
"rtc_event_log/encoder/varint.h",
]
@@ -291,6 +295,7 @@
":rtc_event_bwe",
":rtc_event_log2_proto",
":rtc_event_log_api",
+ ":rtc_event_log_impl_encoder",
":rtc_event_log_proto",
":rtc_stream_config",
"..:webrtc_common",
@@ -317,6 +322,7 @@
sources = [
"rtc_event_log/encoder/blob_encoding_unittest.cc",
"rtc_event_log/encoder/delta_encoding_unittest.cc",
+ "rtc_event_log/encoder/rtc_event_log_encoder_common_unittest.cc",
"rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc",
"rtc_event_log/output/rtc_event_log_output_file_unittest.cc",
"rtc_event_log/rtc_event_log_unittest.cc",
diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_common.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_common.cc
new file mode 100644
index 0000000..7aea476
--- /dev/null
+++ b/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/logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h b/logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h
new file mode 100644
index 0000000..429f8ed
--- /dev/null
+++ b/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/logging/rtc_event_log/encoder/rtc_event_log_encoder_common_unittest.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_common_unittest.cc
new file mode 100644
index 0000000..a7d9440
--- /dev/null
+++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_common_unittest.cc
@@ -0,0 +1,83 @@
+/*
+ * 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 <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_CASE_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_CASE_P(SignednessConversionTest,
+ CorrectlyConvertsLegalValues,
+ FailsOnConvertingIllegalValues);
+
+using Types = ::testing::Types<int8_t, int16_t, int32_t, int64_t>;
+
+INSTANTIATE_TYPED_TEST_CASE_P(_, SignednessConversionTest, Types);
+
+} // namespace
+} // namespace webrtc_event_logging
diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc
index 8b2199b..46d99b9 100644
--- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc
+++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc
@@ -10,9 +10,11 @@
#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h"
-#include <vector>
-
+#include "absl/types/optional.h"
#include "api/array_view.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"
@@ -61,6 +63,8 @@
#endif
RTC_POP_IGNORING_WUNDEF()
+using webrtc_event_logging::ToUnsigned;
+
namespace webrtc {
namespace {
@@ -235,6 +239,13 @@
return rtclog2::IceCandidatePairEvent::UNKNOWN_CHECK_TYPE;
}
+uint8_t ConvertAudioLevelToProtoFormat(bool voice_activity,
+ uint8_t audio_level) {
+ RTC_DCHECK_EQ(audio_level & static_cast<uint8_t>(0x80), 0);
+ constexpr uint8_t kVoiceActivityBit = 0x80;
+ return audio_level | (voice_activity ? kVoiceActivityBit : 0);
+}
+
// Copies all RTCP blocks except APP, SDES and unknown from |packet| to
// |buffer|. |buffer| must have space for |IP_PACKET_SIZE| bytes. |packet| must
// be at most |IP_PACKET_SIZE| bytes long.
@@ -282,6 +293,311 @@
}
return buffer_length;
}
+
+template <typename EventType, typename ProtoType>
+void EncodeRtcpPacket(rtc::ArrayView<const EventType*> batch,
+ ProtoType* proto_batch) {
+ if (batch.size() == 0) {
+ return;
+ }
+
+ // Base event
+ const EventType* const base_event = batch[0];
+ proto_batch->set_timestamp_ms(base_event->timestamp_us_ / 1000);
+ {
+ uint8_t buffer[IP_PACKET_SIZE];
+ size_t buffer_length =
+ RemoveNonWhitelistedRtcpBlocks(base_event->packet_, buffer);
+ proto_batch->set_raw_packet(buffer, 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] = event->timestamp_us_ / 1000;
+ }
+ encoded_deltas = EncodeDeltas(base_event->timestamp_us_ / 1000, values);
+ if (!encoded_deltas.empty()) {
+ proto_batch->set_timestamp_deltas_ms(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 = RemoveNonWhitelistedRtcpBlocks(
+ event->packet_, reinterpret_cast<uint8_t*>(&scrubed_packets[i][0]));
+ scrubed_packets[i].resize(buffer_length);
+ }
+ // TODO(eladalon): s/deltas/blobs in separate CL.
+ proto_batch->set_raw_packet_deltas(EncodeBlobs(scrubed_packets));
+}
+
+template <typename EventType, typename ProtoType>
+void EncodeRtpPacket(const std::vector<const EventType*>& batch,
+ ProtoType* proto_batch) {
+ if (batch.size() == 0) {
+ return;
+ }
+
+ // Base event
+ const EventType* const base_event = batch[0];
+ proto_batch->set_timestamp_ms(base_event->timestamp_us_ / 1000);
+ proto_batch->set_marker(base_event->header_.Marker());
+ // TODO(terelius): Is payload type needed?
+ proto_batch->set_payload_type(base_event->header_.PayloadType());
+ proto_batch->set_sequence_number(base_event->header_.SequenceNumber());
+ proto_batch->set_rtp_timestamp(base_event->header_.Timestamp());
+ proto_batch->set_ssrc(base_event->header_.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->header_.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->header_.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->header_.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->header_.template GetExtension<VideoOrientation>(
+ &video_rotation)) {
+ proto_batch->set_video_rotation(
+ ConvertVideoRotationToCVOByte(video_rotation));
+ base_video_rotation = ConvertVideoRotationToCVOByte(video_rotation);
+ }
+ }
+
+ // TODO(eladalon): Separate audio level from voice activity.
+ absl::optional<uint64_t> base_audio_level;
+ {
+ bool voice_activity;
+ uint8_t audio_level;
+ if (base_event->header_.template GetExtension<AudioLevel>(&voice_activity,
+ &audio_level)) {
+ proto_batch->set_audio_level(
+ ConvertAudioLevelToProtoFormat(voice_activity, audio_level));
+ base_audio_level =
+ ConvertAudioLevelToProtoFormat(voice_activity, audio_level);
+ }
+ }
+
+ 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] = event->timestamp_us_ / 1000;
+ }
+ encoded_deltas = EncodeDeltas(base_event->timestamp_us_ / 1000, values);
+ if (!encoded_deltas.empty()) {
+ proto_batch->set_timestamp_deltas_ms(encoded_deltas);
+ }
+
+ // marker (RTP base)
+ for (size_t i = 0; i < values.size(); ++i) {
+ const EventType* event = batch[i + 1];
+ values[i] = event->header_.Marker();
+ }
+ encoded_deltas = EncodeDeltas(base_event->header_.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->header_.PayloadType();
+ }
+ encoded_deltas = EncodeDeltas(base_event->header_.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->header_.SequenceNumber();
+ }
+ encoded_deltas = EncodeDeltas(base_event->header_.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->header_.Timestamp();
+ }
+ encoded_deltas = EncodeDeltas(base_event->header_.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->header_.Ssrc();
+ }
+ encoded_deltas = EncodeDeltas(base_event->header_.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->header_.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->header_.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->header_.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->header_.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->header_.template GetExtension<AudioLevel>(&voice_activity,
+ &audio_level)) {
+ values[i] = ConvertAudioLevelToProtoFormat(voice_activity, 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);
+ }
+}
} // namespace
std::string RtcEventLogEncoderNewFormat::EncodeLogStart(int64_t timestamp_us) {
@@ -320,8 +636,10 @@
std::vector<const RtcEventProbeResultSuccess*> probe_result_success_events;
std::vector<const RtcEventRtcpPacketIncoming*> incoming_rtcp_packets;
std::vector<const RtcEventRtcpPacketOutgoing*> outgoing_rtcp_packets;
- std::vector<const RtcEventRtpPacketIncoming*> incoming_rtp_packets;
- std::vector<const RtcEventRtpPacketOutgoing*> outgoing_rtp_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;
@@ -408,13 +726,15 @@
case RtcEvent::Type::RtpPacketIncoming: {
auto* rtc_event =
static_cast<const RtcEventRtpPacketIncoming* const>(it->get());
- incoming_rtp_packets.push_back(rtc_event);
+ auto& v = incoming_rtp_packets[rtc_event->header_.Ssrc()];
+ v.emplace_back(rtc_event);
break;
}
case RtcEvent::Type::RtpPacketOutgoing: {
auto* rtc_event =
static_cast<const RtcEventRtpPacketOutgoing* const>(it->get());
- outgoing_rtp_packets.push_back(rtc_event);
+ auto& v = outgoing_rtp_packets[rtc_event->header_.Ssrc()];
+ v.emplace_back(rtc_event);
break;
}
case RtcEvent::Type::VideoReceiveStreamConfig: {
@@ -487,28 +807,130 @@
rtclog2::EventStream* event_stream) {
if (batch.size() == 0)
return;
- for (const RtcEventAudioNetworkAdaptation* base_event : batch) {
- rtclog2::AudioNetworkAdaptations* proto_batch =
- event_stream->add_audio_network_adaptations();
- proto_batch->set_timestamp_ms(base_event->timestamp_us_ / 1000);
- 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());
- }
- if (base_event->config_->uplink_packet_loss_fraction.has_value()) {
- proto_batch->set_uplink_packet_loss_fraction(
- base_event->config_->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());
- if (base_event->config_->num_channels.has_value())
- proto_batch->set_num_channels(base_event->config_->num_channels.value());
+
+ // 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_us_ / 1000);
+ 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());
}
- // TODO(terelius): Delta-compress rest of batch.
+ 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());
+ 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] = event->timestamp_us_ / 1000;
+ }
+ encoded_deltas = EncodeDeltas(base_event->timestamp_us_ / 1000, values);
+ if (!encoded_deltas.empty()) {
+ proto_batch->set_timestamp_deltas_ms(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_deltas_bps(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_deltas_ms(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];
+ values[i] = event->config_->num_channels;
+ }
+ encoded_deltas = EncodeDeltas(base_event->config_->num_channels, values);
+ if (!encoded_deltas.empty()) {
+ proto_batch->set_num_channels_deltas(encoded_deltas);
+ }
}
void RtcEventLogEncoderNewFormat::EncodeAudioPlayout(
@@ -516,13 +938,41 @@
rtclog2::EventStream* event_stream) {
if (batch.size() == 0)
return;
- for (const RtcEventAudioPlayout* base_event : batch) {
- rtclog2::AudioPlayoutEvents* proto_batch =
- event_stream->add_audio_playout_events();
- proto_batch->set_timestamp_ms(base_event->timestamp_us_ / 1000);
- proto_batch->set_local_ssrc(base_event->ssrc_);
+
+ // 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_us_ / 1000);
+ 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] = event->timestamp_us_ / 1000;
}
- // TODO(terelius): Delta-compress rest of batch.
+ encoded_deltas = EncodeDeltas(base_event->timestamp_us_ / 1000, values);
+ if (!encoded_deltas.empty()) {
+ proto_batch->set_timestamp_deltas_ms(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::EncodeAudioRecvStreamConfig(
@@ -571,15 +1021,56 @@
rtclog2::EventStream* event_stream) {
if (batch.size() == 0)
return;
- for (const RtcEventBweUpdateDelayBased* base_event : batch) {
- rtclog2::DelayBasedBweUpdates* proto_batch =
- event_stream->add_delay_based_bwe_updates();
- proto_batch->set_timestamp_ms(base_event->timestamp_us_ / 1000);
- proto_batch->set_bitrate_bps(base_event->bitrate_bps_);
- proto_batch->set_detector_state(
- ConvertToProtoFormat(base_event->detector_state_));
+
+ // 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_us_ / 1000);
+ 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] = event->timestamp_us_ / 1000;
}
- // TODO(terelius): Delta-compress rest of batch.
+ encoded_deltas = EncodeDeltas(base_event->timestamp_us_ / 1000, values);
+ if (!encoded_deltas.empty()) {
+ proto_batch->set_timestamp_deltas_ms(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_deltas_bps(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(
@@ -587,15 +1078,63 @@
rtclog2::EventStream* event_stream) {
if (batch.size() == 0)
return;
- for (const RtcEventBweUpdateLossBased* base_event : batch) {
- rtclog2::LossBasedBweUpdates* proto_batch =
- event_stream->add_loss_based_bwe_updates();
- proto_batch->set_timestamp_ms(base_event->timestamp_us_ / 1000);
- 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_);
+
+ // 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_us_ / 1000);
+ 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] = event->timestamp_us_ / 1000;
}
- // TODO(terelius): Delta-compress rest of batch.
+ encoded_deltas = EncodeDeltas(base_event->timestamp_us_ / 1000, values);
+ if (!encoded_deltas.empty()) {
+ proto_batch->set_timestamp_deltas_ms(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_deltas_bps(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::EncodeProbeClusterCreated(
@@ -640,149 +1179,39 @@
void RtcEventLogEncoderNewFormat::EncodeRtcpPacketIncoming(
rtc::ArrayView<const RtcEventRtcpPacketIncoming*> batch,
rtclog2::EventStream* event_stream) {
- if (batch.size() == 0)
+ if (batch.empty()) {
return;
- for (const RtcEventRtcpPacketIncoming* base_event : batch) {
- rtclog2::IncomingRtcpPackets* proto_batch =
- event_stream->add_incoming_rtcp_packets();
- proto_batch->set_timestamp_ms(base_event->timestamp_us_ / 1000);
-
- uint8_t buffer[IP_PACKET_SIZE];
- size_t buffer_length =
- RemoveNonWhitelistedRtcpBlocks(base_event->packet_, buffer);
- proto_batch->set_raw_packet(buffer, buffer_length);
}
- // TODO(terelius): Delta-compress rest of batch.
+ EncodeRtcpPacket(batch, event_stream->add_incoming_rtcp_packets());
}
void RtcEventLogEncoderNewFormat::EncodeRtcpPacketOutgoing(
rtc::ArrayView<const RtcEventRtcpPacketOutgoing*> batch,
rtclog2::EventStream* event_stream) {
- if (batch.size() == 0)
+ if (batch.empty()) {
return;
- for (const RtcEventRtcpPacketOutgoing* base_event : batch) {
- rtclog2::OutgoingRtcpPackets* proto_batch =
- event_stream->add_outgoing_rtcp_packets();
- proto_batch->set_timestamp_ms(base_event->timestamp_us_ / 1000);
-
- uint8_t buffer[IP_PACKET_SIZE];
- size_t buffer_length =
- RemoveNonWhitelistedRtcpBlocks(base_event->packet_, buffer);
- proto_batch->set_raw_packet(buffer, buffer_length);
}
- // TODO(terelius): Delta-compress rest of batch.
+ EncodeRtcpPacket(batch, event_stream->add_outgoing_rtcp_packets());
}
void RtcEventLogEncoderNewFormat::EncodeRtpPacketIncoming(
- rtc::ArrayView<const RtcEventRtpPacketIncoming*> batch,
+ const std::map<uint32_t, std::vector<const RtcEventRtpPacketIncoming*>>&
+ batch,
rtclog2::EventStream* event_stream) {
- if (batch.size() == 0)
- return;
- for (const RtcEventRtpPacketIncoming* base_event : batch) {
- rtclog2::IncomingRtpPackets* proto_batch =
- event_stream->add_incoming_rtp_packets();
- proto_batch->set_timestamp_ms(base_event->timestamp_us_ / 1000);
- proto_batch->set_marker(base_event->header_.Marker());
- // TODO(terelius): Is payload type needed?
- proto_batch->set_payload_type(base_event->header_.PayloadType());
- proto_batch->set_sequence_number(base_event->header_.SequenceNumber());
- proto_batch->set_rtp_timestamp(base_event->header_.Timestamp());
- proto_batch->set_ssrc(base_event->header_.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.
- if (base_event->header_.HasExtension<TransmissionOffset>()) {
- int32_t offset;
- base_event->header_.GetExtension<TransmissionOffset>(&offset);
- proto_batch->set_transmission_time_offset(offset);
- }
- if (base_event->header_.HasExtension<AbsoluteSendTime>()) {
- uint32_t sendtime;
- base_event->header_.GetExtension<AbsoluteSendTime>(&sendtime);
- proto_batch->set_absolute_send_time(sendtime);
- }
- if (base_event->header_.HasExtension<TransportSequenceNumber>()) {
- uint16_t seqnum;
- base_event->header_.GetExtension<TransportSequenceNumber>(&seqnum);
- proto_batch->set_transport_sequence_number(seqnum);
- }
- if (base_event->header_.HasExtension<AudioLevel>()) {
- bool voice_activity;
- uint8_t audio_level;
- base_event->header_.GetExtension<AudioLevel>(&voice_activity,
- &audio_level);
- RTC_DCHECK(audio_level < 128);
- if (voice_activity) {
- audio_level += 128; // Most significant bit indicates voice activity.
- }
- proto_batch->set_audio_level(audio_level);
- }
- if (base_event->header_.HasExtension<VideoOrientation>()) {
- VideoRotation video_rotation;
- base_event->header_.GetExtension<VideoOrientation>(&video_rotation);
- proto_batch->set_video_rotation(
- ConvertVideoRotationToCVOByte(video_rotation));
- }
+ for (auto it : batch) {
+ RTC_DCHECK(!it.second.empty());
+ EncodeRtpPacket(it.second, event_stream->add_incoming_rtp_packets());
}
- // TODO(terelius): Delta-compress rest of batch.
}
void RtcEventLogEncoderNewFormat::EncodeRtpPacketOutgoing(
- rtc::ArrayView<const RtcEventRtpPacketOutgoing*> batch,
+ const std::map<uint32_t, std::vector<const RtcEventRtpPacketOutgoing*>>&
+ batch,
rtclog2::EventStream* event_stream) {
- if (batch.size() == 0)
- return;
- for (const RtcEventRtpPacketOutgoing* base_event : batch) {
- rtclog2::OutgoingRtpPackets* proto_batch =
- event_stream->add_outgoing_rtp_packets();
- proto_batch->set_timestamp_ms(base_event->timestamp_us_ / 1000);
- proto_batch->set_marker(base_event->header_.Marker());
- // TODO(terelius): Is payload type needed?
- proto_batch->set_payload_type(base_event->header_.PayloadType());
- proto_batch->set_sequence_number(base_event->header_.SequenceNumber());
- proto_batch->set_rtp_timestamp(base_event->header_.Timestamp());
- proto_batch->set_ssrc(base_event->header_.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.
- if (base_event->header_.HasExtension<TransmissionOffset>()) {
- int32_t offset;
- base_event->header_.GetExtension<TransmissionOffset>(&offset);
- proto_batch->set_transmission_time_offset(offset);
- }
- if (base_event->header_.HasExtension<AbsoluteSendTime>()) {
- uint32_t sendtime;
- base_event->header_.GetExtension<AbsoluteSendTime>(&sendtime);
- proto_batch->set_absolute_send_time(sendtime);
- }
- if (base_event->header_.HasExtension<TransportSequenceNumber>()) {
- uint16_t seqnum;
- base_event->header_.GetExtension<TransportSequenceNumber>(&seqnum);
- proto_batch->set_transport_sequence_number(seqnum);
- }
- if (base_event->header_.HasExtension<AudioLevel>()) {
- bool voice_activity;
- uint8_t audio_level;
- base_event->header_.GetExtension<AudioLevel>(&voice_activity,
- &audio_level);
- RTC_DCHECK(audio_level < 128);
- if (voice_activity) {
- audio_level += 128; // Most significant bit indicates voice activity.
- }
- proto_batch->set_audio_level(audio_level);
- }
- if (base_event->header_.HasExtension<VideoOrientation>()) {
- VideoRotation video_rotation;
- base_event->header_.GetExtension<VideoOrientation>(&video_rotation);
- proto_batch->set_video_rotation(
- ConvertVideoRotationToCVOByte(video_rotation));
- }
+ for (auto it : batch) {
+ RTC_DCHECK(!it.second.empty());
+ EncodeRtpPacket(it.second, event_stream->add_outgoing_rtp_packets());
}
- // TODO(terelius): Delta-compress rest of batch.
}
void RtcEventLogEncoderNewFormat::EncodeVideoRecvStreamConfig(
diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h
index b49286d..d9a3f0a 100644
--- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h
+++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h
@@ -12,8 +12,10 @@
#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"
@@ -98,10 +100,12 @@
rtc::ArrayView<const RtcEventRtcpPacketOutgoing*> batch,
rtclog2::EventStream* event_stream);
void EncodeRtpPacketIncoming(
- rtc::ArrayView<const RtcEventRtpPacketIncoming*> batch,
+ const std::map<uint32_t, std::vector<const RtcEventRtpPacketIncoming*>>&
+ batch,
rtclog2::EventStream* event_stream);
void EncodeRtpPacketOutgoing(
- rtc::ArrayView<const RtcEventRtpPacketOutgoing*> batch,
+ const std::map<uint32_t, std::vector<const RtcEventRtpPacketOutgoing*>>&
+ batch,
rtclog2::EventStream* event_stream);
void EncodeVideoRecvStreamConfig(
rtc::ArrayView<const RtcEventVideoReceiveStreamConfig*> batch,
diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc
index 726161e..b29237e 100644
--- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc
+++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc
@@ -41,13 +41,17 @@
#include "test/gtest.h"
namespace webrtc {
-
class RtcEventLogEncoderTest
- : public testing::TestWithParam<std::tuple<int, bool>> {
+ : public testing::TestWithParam<std::tuple<int, bool, size_t, bool>> {
protected:
RtcEventLogEncoderTest()
- : seed_(std::get<0>(GetParam())), prng_(seed_), gen_(seed_ * 880001UL) {
- if (std::get<1>(GetParam()))
+ : seed_(std::get<0>(GetParam())),
+ prng_(seed_),
+ gen_(seed_ * 880001UL),
+ new_encoding_(std::get<1>(GetParam())),
+ event_count_(std::get<2>(GetParam())),
+ force_repeated_fields_(std::get<3>(GetParam())) {
+ if (new_encoding_)
encoder_ = absl::make_unique<RtcEventLogEncoderNewFormat>();
else
encoder_ = absl::make_unique<RtcEventLogEncoderLegacy>();
@@ -58,140 +62,345 @@
// correct behavior both when all of the values are there, as well as when
// only some.
void TestRtcEventAudioNetworkAdaptation(
- std::unique_ptr<AudioEncoderRuntimeConfig> runtime_config);
+ 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 ParsedRtcEventLogNew* parsed_log);
+
+ template <typename EventType, typename ParsedType>
+ void TestRtpPackets();
std::deque<std::unique_ptr<RtcEvent>> history_;
- // TODO(eladalon): Once we have more than one possible encoder, parameterize
- // encoder selection.
std::unique_ptr<RtcEventLogEncoder> encoder_;
ParsedRtcEventLogNew parsed_log_;
const uint64_t seed_;
Random prng_;
test::EventGenerator gen_;
+ const bool new_encoding_;
+ const size_t event_count_;
+ const bool force_repeated_fields_;
};
-TEST_P(RtcEventLogEncoderTest, RtcEventAlrState) {
- std::unique_ptr<RtcEventAlrState> event = gen_.NewAlrState();
- history_.push_back(event->Copy());
-
- std::string encoded = encoder_->EncodeBatch(history_.begin(), history_.end());
- ASSERT_TRUE(parsed_log_.ParseString(encoded));
- const auto& alr_state_events = parsed_log_.alr_state_events();
-
- ASSERT_EQ(alr_state_events.size(), 1u);
- test::VerifyLoggedAlrStateEvent(*event, alr_state_events[0]);
-}
-
void RtcEventLogEncoderTest::TestRtcEventAudioNetworkAdaptation(
- std::unique_ptr<AudioEncoderRuntimeConfig> runtime_config) {
- // This function is called repeatedly. Clear state between calls.
- history_.clear();
- auto original_runtime_config = *runtime_config;
- auto event = absl::make_unique<RtcEventAudioNetworkAdaptation>(
- std::move(runtime_config));
- history_.push_back(event->Copy());
+ 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());
+ }
std::string encoded = encoder_->EncodeBatch(history_.begin(), history_.end());
ASSERT_TRUE(parsed_log_.ParseString(encoded));
const auto& ana_configs = parsed_log_.audio_network_adaptation_events();
- ASSERT_EQ(ana_configs.size(), 1u);
- test::VerifyLoggedAudioNetworkAdaptationEvent(*event, ana_configs[0]);
+ ASSERT_EQ(ana_configs.size(), events.size());
+ for (size_t i = 0; i < events.size(); ++i) {
+ test::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<ParsedRtcEventLogNew::LoggedRtpStreamIncoming>&
+RtcEventLogEncoderTest::GetRtpPacketsBySsrc(
+ const ParsedRtcEventLogNew* parsed_log) {
+ return parsed_log->incoming_rtp_packets_by_ssrc();
+}
+
+template <>
+const std::vector<ParsedRtcEventLogNew::LoggedRtpStreamOutgoing>&
+RtcEventLogEncoderTest::GetRtpPacketsBySsrc(
+ const ParsedRtcEventLogNew* parsed_log) {
+ return parsed_log->outgoing_rtp_packets_by_ssrc();
+}
+
+template <typename EventType, typename ParsedType>
+void CompareRtpPacketSequences(
+ const std::vector<std::unique_ptr<EventType>>& original_events,
+ const ParsedType& parsed_events);
+
+template <>
+void CompareRtpPacketSequences(
+ const std::vector<std::unique_ptr<RtcEventRtpPacketIncoming>>& original,
+ const ParsedRtcEventLogNew::LoggedRtpStreamIncoming& parsed) {
+ ASSERT_EQ(parsed.incoming_packets.size(), original.size());
+ for (size_t i = 0; i < original.size(); ++i) {
+ test::VerifyLoggedRtpPacketIncoming(*original[i],
+ parsed.incoming_packets[i]);
+ }
+}
+
+template <>
+void CompareRtpPacketSequences(
+ const std::vector<std::unique_ptr<RtcEventRtpPacketOutgoing>>& original,
+ const ParsedRtcEventLogNew::LoggedRtpStreamOutgoing& parsed) {
+ ASSERT_EQ(parsed.outgoing_packets.size(), original.size());
+ for (size_t i = 0; i < original.size(); ++i) {
+ test::VerifyLoggedRtpPacketOutgoing(*original[i],
+ parsed.outgoing_packets[i]);
+ }
+}
+
+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 (new_encoding_) {
+ 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.
+ std::string encoded = encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded));
+
+ // Expect as many distinct SSRCs to be parsed, as were simulated.
+ const std::vector<ParsedType>& parsed_rtp_packets =
+ GetRtpPacketsBySsrc<ParsedType>(&parsed_log_);
+ ASSERT_EQ(parsed_rtp_packets.size(), events_by_ssrc.size());
+
+ // 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;
+ auto parsed = std::find_if(
+ parsed_rtp_packets.begin(), parsed_rtp_packets.end(),
+ [ssrc](const ParsedType& packet) { return packet.ssrc == ssrc; });
+ ASSERT_NE(parsed, parsed_rtp_packets.end());
+ CompareRtpPacketSequences(it->second, *parsed);
+ }
+}
+
+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());
+ }
+
+ std::string encoded = encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded));
+ 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) {
+ test::VerifyLoggedAlrStateEvent(*events[i], alr_state_events[i]);
+ }
}
TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationBitrate) {
- auto runtime_config = absl::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;
- TestRtcEventAudioNetworkAdaptation(std::move(runtime_config));
+ 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 = absl::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] = absl::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
+ }
+ TestRtcEventAudioNetworkAdaptation(events);
}
TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationFrameLength) {
- auto runtime_config = absl::make_unique<AudioEncoderRuntimeConfig>();
- const int frame_length_ms = prng_.Rand(1, 1000);
- runtime_config->frame_length_ms = frame_length_ms;
- TestRtcEventAudioNetworkAdaptation(std::move(runtime_config));
+ 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 = absl::make_unique<AudioEncoderRuntimeConfig>();
+ const int frame_length_ms = prng_.Rand(1, 1000);
+ runtime_config->frame_length_ms = frame_length_ms;
+ events[i] = absl::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
+ }
+ TestRtcEventAudioNetworkAdaptation(events);
}
TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationPacketLoss) {
- // To simplify the test, we just check powers of two.
- const float plr = std::pow(0.5f, prng_.Rand(1, 8));
- auto runtime_config = absl::make_unique<AudioEncoderRuntimeConfig>();
- runtime_config->uplink_packet_loss_fraction = plr;
- TestRtcEventAudioNetworkAdaptation(std::move(runtime_config));
+ 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 = absl::make_unique<AudioEncoderRuntimeConfig>();
+ runtime_config->uplink_packet_loss_fraction = plr;
+ events[i] = absl::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
+ }
+ TestRtcEventAudioNetworkAdaptation(events);
}
TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationFec) {
- // The test might be trivially passing for one of the two boolean values, so
- // for safety's sake, we test both.
- for (bool fec_enabled : {false, true}) {
- auto runtime_config = absl::make_unique<AudioEncoderRuntimeConfig>();
- runtime_config->enable_fec = fec_enabled;
- TestRtcEventAudioNetworkAdaptation(std::move(runtime_config));
+ 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 = absl::make_unique<AudioEncoderRuntimeConfig>();
+ runtime_config->enable_fec = prng_.Rand<bool>();
+ events[i] = absl::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
}
+ TestRtcEventAudioNetworkAdaptation(events);
}
TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationDtx) {
- // The test might be trivially passing for one of the two boolean values, so
- // for safety's sake, we test both.
- for (bool dtx_enabled : {false, true}) {
- auto runtime_config = absl::make_unique<AudioEncoderRuntimeConfig>();
- runtime_config->enable_dtx = dtx_enabled;
- TestRtcEventAudioNetworkAdaptation(std::move(runtime_config));
+ 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 = absl::make_unique<AudioEncoderRuntimeConfig>();
+ runtime_config->enable_dtx = prng_.Rand<bool>();
+ events[i] = absl::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
}
+ TestRtcEventAudioNetworkAdaptation(events);
}
TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationChannels) {
- // The test might be trivially passing for one of the two possible values, so
- // for safety's sake, we test both.
- for (size_t channels : {1, 2}) {
- auto runtime_config = absl::make_unique<AudioEncoderRuntimeConfig>();
- runtime_config->num_channels = channels;
- TestRtcEventAudioNetworkAdaptation(std::move(runtime_config));
+ 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 = absl::make_unique<AudioEncoderRuntimeConfig>();
+ runtime_config->num_channels = prng_.Rand(1, 2);
+ events[i] = absl::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
}
+ TestRtcEventAudioNetworkAdaptation(events);
}
TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationAll) {
- const int bitrate_bps = rtc::checked_cast<int>(
- prng_.Rand(0, std::numeric_limits<int32_t>::max()));
- const int frame_length_ms = prng_.Rand(1, 1000);
- const float plr = std::pow(0.5f, prng_.Rand(1, 8));
- for (bool fec_enabled : {false, true}) {
- for (bool dtx_enabled : {false, true}) {
- for (size_t channels : {1, 2}) {
- auto runtime_config = absl::make_unique<AudioEncoderRuntimeConfig>();
- runtime_config->bitrate_bps = bitrate_bps;
- runtime_config->frame_length_ms = frame_length_ms;
- runtime_config->uplink_packet_loss_fraction = plr;
- runtime_config->enable_fec = fec_enabled;
- runtime_config->enable_dtx = dtx_enabled;
- runtime_config->num_channels = channels;
-
- TestRtcEventAudioNetworkAdaptation(std::move(runtime_config));
- }
+ 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 = absl::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] = absl::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
}
}
+ TestRtcEventAudioNetworkAdaptation(events);
}
TEST_P(RtcEventLogEncoderTest, RtcEventAudioPlayout) {
- uint32_t ssrc = prng_.Rand<uint32_t>();
- std::unique_ptr<RtcEventAudioPlayout> event = gen_.NewAudioPlayout(ssrc);
- history_.push_back(event->Copy());
+ // 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));
+ }
std::string encoded = encoder_->EncodeBatch(history_.begin(), history_.end());
ASSERT_TRUE(parsed_log_.ParseString(encoded));
- const auto& playout_events = parsed_log_.audio_playout_events();
- ASSERT_EQ(playout_events.size(), 1u);
- const auto playout_stream = playout_events.find(ssrc);
- ASSERT_TRUE(playout_stream != playout_events.end());
+ const auto& parsed_playout_events_by_ssrc =
+ parsed_log_.audio_playout_events();
- ASSERT_EQ(playout_stream->second.size(), 1u);
- LoggedAudioPlayoutEvent playout_event = playout_stream->second[0];
- test::VerifyLoggedAudioPlayoutEvent(*event, playout_event);
+ // 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) {
+ test::VerifyLoggedAudioPlayoutEvent(*original_playout_events[i],
+ parsed_playout_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();
@@ -207,6 +416,7 @@
test::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();
@@ -223,31 +433,47 @@
}
TEST_P(RtcEventLogEncoderTest, RtcEventBweUpdateDelayBased) {
- std::unique_ptr<RtcEventBweUpdateDelayBased> event =
- gen_.NewBweUpdateDelayBased();
- history_.push_back(event->Copy());
+ 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());
+ }
std::string encoded = encoder_->EncodeBatch(history_.begin(), history_.end());
ASSERT_TRUE(parsed_log_.ParseString(encoded));
- const auto& bwe_delay_updates = parsed_log_.bwe_delay_updates();
- ASSERT_EQ(bwe_delay_updates.size(), 1u);
- test::VerifyLoggedBweDelayBasedUpdate(*event, bwe_delay_updates[0]);
+ 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) {
+ test::VerifyLoggedBweDelayBasedUpdate(*events[i], bwe_delay_updates[i]);
+ }
}
TEST_P(RtcEventLogEncoderTest, RtcEventBweUpdateLossBased) {
- std::unique_ptr<RtcEventBweUpdateLossBased> event =
- gen_.NewBweUpdateLossBased();
- history_.push_back(event->Copy());
+ 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());
+ }
std::string encoded = encoder_->EncodeBatch(history_.begin(), history_.end());
ASSERT_TRUE(parsed_log_.ParseString(encoded));
- const auto& bwe_loss_updates = parsed_log_.bwe_loss_updates();
- ASSERT_EQ(bwe_loss_updates.size(), 1u);
- test::VerifyLoggedBweLossBasedUpdate(*event, bwe_loss_updates[0]);
+ 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) {
+ test::VerifyLoggedBweLossBasedUpdate(*events[i], bwe_loss_updates[i]);
+ }
}
+// TODO(eladalon/terelius): Test with multiple events in the batch.
TEST_P(RtcEventLogEncoderTest, RtcEventIceCandidatePairConfig) {
std::unique_ptr<RtcEventIceCandidatePairConfig> event =
gen_.NewIceCandidatePairConfig();
@@ -263,6 +489,7 @@
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());
@@ -276,6 +503,8 @@
test::VerifyLoggedIceCandidatePairEvent(*event, ice_candidate_pair_events[0]);
}
+// TODO(eladalon/terelius): Test with multiple events in the batch, or prevent
+// it from happening.
TEST_P(RtcEventLogEncoderTest, RtcEventLoggingStarted) {
const int64_t timestamp_us = rtc::TimeMicros();
@@ -286,6 +515,8 @@
test::VerifyLoggedStartEvent(timestamp_us, start_log_events[0]);
}
+// TODO(eladalon/terelius): Test with multiple events in the batch, or prevent
+// it from happening.
TEST_P(RtcEventLogEncoderTest, RtcEventLoggingStopped) {
const int64_t timestamp_us = rtc::TimeMicros();
@@ -296,6 +527,7 @@
test::VerifyLoggedStopEvent(timestamp_us, 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();
@@ -311,6 +543,7 @@
*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();
@@ -324,6 +557,7 @@
test::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();
@@ -338,73 +572,62 @@
}
TEST_P(RtcEventLogEncoderTest, RtcEventRtcpPacketIncoming) {
- std::unique_ptr<RtcEventRtcpPacketIncoming> event =
- gen_.NewRtcpPacketIncoming();
- history_.push_back(event->Copy());
+ if (!new_encoding_ && force_repeated_fields_) {
+ // The old encoding does not work with duplicated packets. Since the legacy
+ // encoding is being phased out, we will not fix this.
+ 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());
+ }
std::string encoded = encoder_->EncodeBatch(history_.begin(), history_.end());
ASSERT_TRUE(parsed_log_.ParseString(encoded));
+
const auto& incoming_rtcp_packets = parsed_log_.incoming_rtcp_packets();
+ ASSERT_EQ(incoming_rtcp_packets.size(), event_count_);
- ASSERT_EQ(incoming_rtcp_packets.size(), 1u);
-
- test::VerifyLoggedRtcpPacketIncoming(*event, incoming_rtcp_packets[0]);
+ for (size_t i = 0; i < event_count_; ++i) {
+ test::VerifyLoggedRtcpPacketIncoming(*events[i], incoming_rtcp_packets[i]);
+ }
}
TEST_P(RtcEventLogEncoderTest, RtcEventRtcpPacketOutgoing) {
- std::unique_ptr<RtcEventRtcpPacketOutgoing> event =
- gen_.NewRtcpPacketOutgoing();
- history_.push_back(event->Copy());
+ 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());
+ }
std::string encoded = encoder_->EncodeBatch(history_.begin(), history_.end());
ASSERT_TRUE(parsed_log_.ParseString(encoded));
+
const auto& outgoing_rtcp_packets = parsed_log_.outgoing_rtcp_packets();
+ ASSERT_EQ(outgoing_rtcp_packets.size(), event_count_);
- ASSERT_EQ(outgoing_rtcp_packets.size(), 1u);
-
- test::VerifyLoggedRtcpPacketOutgoing(*event, outgoing_rtcp_packets[0]);
+ for (size_t i = 0; i < event_count_; ++i) {
+ test::VerifyLoggedRtcpPacketOutgoing(*events[i], outgoing_rtcp_packets[i]);
+ }
}
TEST_P(RtcEventLogEncoderTest, RtcEventRtpPacketIncoming) {
- uint32_t ssrc = prng_.Rand<uint32_t>();
- RtpHeaderExtensionMap extension_map; // TODO(terelius): Test extensions too.
- std::unique_ptr<RtcEventRtpPacketIncoming> event =
- gen_.NewRtpPacketIncoming(ssrc, extension_map);
- history_.push_back(event->Copy());
-
- std::string encoded = encoder_->EncodeBatch(history_.begin(), history_.end());
- ASSERT_TRUE(parsed_log_.ParseString(encoded));
- const auto& incoming_rtp_packets_by_ssrc =
- parsed_log_.incoming_rtp_packets_by_ssrc();
-
- ASSERT_EQ(incoming_rtp_packets_by_ssrc.size(), 1u);
- const auto& stream = incoming_rtp_packets_by_ssrc[0];
- EXPECT_EQ(stream.ssrc, ssrc);
- ASSERT_EQ(stream.incoming_packets.size(), 1u);
-
- test::VerifyLoggedRtpPacketIncoming(*event, stream.incoming_packets[0]);
+ TestRtpPackets<RtcEventRtpPacketIncoming,
+ ParsedRtcEventLogNew::LoggedRtpStreamIncoming>();
}
TEST_P(RtcEventLogEncoderTest, RtcEventRtpPacketOutgoing) {
- uint32_t ssrc = prng_.Rand<uint32_t>();
- RtpHeaderExtensionMap extension_map; // TODO(terelius): Test extensions too.
- std::unique_ptr<RtcEventRtpPacketOutgoing> event =
- gen_.NewRtpPacketOutgoing(ssrc, extension_map);
- history_.push_back(event->Copy());
-
- std::string encoded = encoder_->EncodeBatch(history_.begin(), history_.end());
- ASSERT_TRUE(parsed_log_.ParseString(encoded));
- const auto& outgoing_rtp_packets_by_ssrc =
- parsed_log_.outgoing_rtp_packets_by_ssrc();
-
- ASSERT_EQ(outgoing_rtp_packets_by_ssrc.size(), 1u);
- const auto& stream = outgoing_rtp_packets_by_ssrc[0];
- EXPECT_EQ(stream.ssrc, ssrc);
- ASSERT_EQ(stream.outgoing_packets.size(), 1u);
-
- test::VerifyLoggedRtpPacketOutgoing(*event, stream.outgoing_packets[0]);
+ TestRtpPackets<RtcEventRtpPacketOutgoing,
+ ParsedRtcEventLogNew::LoggedRtpStreamOutgoing>();
}
+// 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();
@@ -420,6 +643,7 @@
test::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();
@@ -435,9 +659,12 @@
test::VerifyLoggedVideoSendConfig(*event, video_send_configs[0]);
}
-INSTANTIATE_TEST_CASE_P(RandomSeeds,
- RtcEventLogEncoderTest,
- ::testing::Combine(::testing::Values(1, 2, 3, 4, 5),
- ::testing::Bool()));
+INSTANTIATE_TEST_CASE_P(
+ RandomSeeds,
+ RtcEventLogEncoderTest,
+ ::testing::Combine(/* Random seed*: */ ::testing::Values(1, 2, 3, 4, 5),
+ /* Encoding: */ ::testing::Bool(),
+ /* Event count: */ ::testing::Values(1, 2, 10, 100),
+ /* Repeated fields: */ ::testing::Bool()));
} // namespace webrtc
diff --git a/logging/rtc_event_log/rtc_event_log2.proto b/logging/rtc_event_log/rtc_event_log2.proto
index fb8f760..fb64951 100644
--- a/logging/rtc_event_log/rtc_event_log2.proto
+++ b/logging/rtc_event_log/rtc_event_log2.proto
@@ -10,6 +10,8 @@
// single EventStream object containing the same events. Hence, it is not
// necessary to wait for the entire log to be complete before beginning to
// write it to a file.
+// Note: For all X_deltas fields, we rely on the default value being an
+// empty string.
message EventStream {
// Deprecated - Maintained for compatibility with the old event log.
repeated Event stream = 1 [deprecated = true];
@@ -395,7 +397,12 @@
// Packet loss fraction that the encoder's forward error correction (FEC) is
// optimized for.
- optional float uplink_packet_loss_fraction = 4;
+ // Instead of encoding a float, we encode a value between 0 and 16383, which
+ // if divided by 16383, will give a value close to the original float.
+ // The value 16383 (2^14 - 1) was chosen so that it would give good precision
+ // on the one hand, and would be encodable with two bytes in varint form
+ // on the other hand.
+ optional uint32 uplink_packet_loss_fraction = 4;
// Whether forward error correction (FEC) is turned on or off.
optional bool enable_fec = 5;
diff --git a/logging/rtc_event_log/rtc_event_log_parser_new.cc b/logging/rtc_event_log/rtc_event_log_parser_new.cc
index d352817..5b6b26a 100644
--- a/logging/rtc_event_log/rtc_event_log_parser_new.cc
+++ b/logging/rtc_event_log/rtc_event_log_parser_new.cc
@@ -24,6 +24,9 @@
#include "absl/types/optional.h"
#include "api/rtp_headers.h"
#include "api/rtpparameters.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/rtc_event_log.h"
#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
#include "modules/congestion_controller/rtp/transport_feedback_adapter.h"
@@ -35,8 +38,12 @@
#include "modules/rtp_rtcp/source/rtp_utility.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/protobuf_utils.h"
+using webrtc_event_logging::ToSigned;
+using webrtc_event_logging::ToUnsigned;
+
namespace webrtc {
namespace {
@@ -442,6 +449,276 @@
std::sort(vec->begin(), vec->end(), LossHandlingPacketFeedbackComparator());
}
+template <typename ProtoType, typename LoggedType>
+void StoreRtpPackets(
+ const ProtoType& proto,
+ std::map<uint32_t, std::vector<LoggedType>>* rtp_packets_map) {
+ RTC_CHECK(proto.has_timestamp_ms());
+ RTC_CHECK(proto.has_marker());
+ RTC_CHECK(proto.has_payload_type());
+ RTC_CHECK(proto.has_sequence_number());
+ RTC_CHECK(proto.has_rtp_timestamp());
+ RTC_CHECK(proto.has_ssrc());
+ RTC_CHECK(proto.has_payload_size());
+ RTC_CHECK(proto.has_header_size());
+ RTC_CHECK(proto.has_padding_size());
+
+ // Base event
+ {
+ RTPHeader header;
+ header.markerBit = rtc::checked_cast<bool>(proto.marker());
+ header.payloadType = rtc::checked_cast<uint8_t>(proto.payload_type());
+ header.sequenceNumber =
+ rtc::checked_cast<uint16_t>(proto.sequence_number());
+ header.timestamp = rtc::checked_cast<uint32_t>(proto.rtp_timestamp());
+ header.ssrc = rtc::checked_cast<uint32_t>(proto.ssrc());
+ header.numCSRCs = 0; // TODO(terelius): Implement CSRC.
+ header.paddingLength = rtc::checked_cast<size_t>(proto.padding_size());
+ header.headerLength = rtc::checked_cast<size_t>(proto.header_size());
+ // TODO(terelius): Should we implement payload_type_frequency?
+ if (proto.has_transport_sequence_number()) {
+ header.extension.hasTransportSequenceNumber = true;
+ header.extension.transportSequenceNumber =
+ rtc::checked_cast<uint16_t>(proto.transport_sequence_number());
+ }
+ if (proto.has_transmission_time_offset()) {
+ header.extension.hasTransmissionTimeOffset = true;
+ header.extension.transmissionTimeOffset =
+ rtc::checked_cast<int32_t>(proto.transmission_time_offset());
+ }
+ if (proto.has_absolute_send_time()) {
+ header.extension.hasAbsoluteSendTime = true;
+ header.extension.absoluteSendTime =
+ rtc::checked_cast<uint32_t>(proto.absolute_send_time());
+ }
+ if (proto.has_video_rotation()) {
+ header.extension.hasVideoRotation = true;
+ header.extension.videoRotation = ConvertCVOByteToVideoRotation(
+ rtc::checked_cast<uint8_t>(proto.video_rotation()));
+ }
+ if (proto.has_audio_level()) {
+ header.extension.hasAudioLevel = true;
+ const uint8_t audio_level =
+ rtc::checked_cast<uint8_t>(proto.audio_level());
+ header.extension.voiceActivity = (audio_level >> 7) != 0;
+ header.extension.audioLevel = audio_level & 0x7Fu;
+ }
+ (*rtp_packets_map)[header.ssrc].emplace_back(
+ proto.timestamp_ms() * 1000, header, proto.header_size(),
+ proto.payload_size() + header.headerLength + header.paddingLength);
+ }
+
+ const size_t number_of_deltas =
+ proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u;
+ if (number_of_deltas == 0) {
+ return;
+ }
+
+ // timestamp_ms (event)
+ std::vector<absl::optional<uint64_t>> timestamp_ms_values = DecodeDeltas(
+ proto.timestamp_deltas_ms(), proto.timestamp_ms(), number_of_deltas);
+ RTC_CHECK_EQ(timestamp_ms_values.size(), number_of_deltas);
+
+ // marker (RTP base)
+ std::vector<absl::optional<uint64_t>> marker_values =
+ DecodeDeltas(proto.marker_deltas(), proto.marker(), number_of_deltas);
+ RTC_CHECK_EQ(marker_values.size(), number_of_deltas);
+
+ // payload_type (RTP base)
+ std::vector<absl::optional<uint64_t>> payload_type_values = DecodeDeltas(
+ proto.payload_type_deltas(), proto.payload_type(), number_of_deltas);
+ RTC_CHECK_EQ(payload_type_values.size(), number_of_deltas);
+
+ // sequence_number (RTP base)
+ std::vector<absl::optional<uint64_t>> sequence_number_values =
+ DecodeDeltas(proto.sequence_number_deltas(), proto.sequence_number(),
+ number_of_deltas);
+ RTC_CHECK_EQ(sequence_number_values.size(), number_of_deltas);
+
+ // rtp_timestamp (RTP base)
+ std::vector<absl::optional<uint64_t>> rtp_timestamp_values = DecodeDeltas(
+ proto.rtp_timestamp_deltas(), proto.rtp_timestamp(), number_of_deltas);
+ RTC_CHECK_EQ(rtp_timestamp_values.size(), number_of_deltas);
+
+ // ssrc (RTP base)
+ std::vector<absl::optional<uint64_t>> ssrc_values =
+ DecodeDeltas(proto.ssrc_deltas(), proto.ssrc(), number_of_deltas);
+ RTC_CHECK_EQ(ssrc_values.size(), number_of_deltas);
+
+ // payload_size (RTP base)
+ std::vector<absl::optional<uint64_t>> payload_size_values = DecodeDeltas(
+ proto.payload_size_deltas(), proto.payload_size(), number_of_deltas);
+ RTC_CHECK_EQ(payload_size_values.size(), number_of_deltas);
+
+ // header_size (RTP base)
+ std::vector<absl::optional<uint64_t>> header_size_values = DecodeDeltas(
+ proto.header_size_deltas(), proto.header_size(), number_of_deltas);
+ RTC_CHECK_EQ(header_size_values.size(), number_of_deltas);
+
+ // padding_size (RTP base)
+ std::vector<absl::optional<uint64_t>> padding_size_values = DecodeDeltas(
+ proto.padding_size_deltas(), proto.padding_size(), number_of_deltas);
+ RTC_CHECK_EQ(padding_size_values.size(), number_of_deltas);
+
+ // transport_sequence_number (RTP extension)
+ std::vector<absl::optional<uint64_t>> transport_sequence_number_values;
+ {
+ const absl::optional<uint64_t> base_transport_sequence_number =
+ proto.has_transport_sequence_number()
+ ? proto.transport_sequence_number()
+ : absl::optional<uint64_t>();
+ transport_sequence_number_values =
+ DecodeDeltas(proto.transport_sequence_number_deltas(),
+ base_transport_sequence_number, number_of_deltas);
+ RTC_CHECK_EQ(transport_sequence_number_values.size(), number_of_deltas);
+ }
+
+ // transmission_time_offset (RTP extension)
+ std::vector<absl::optional<uint64_t>> transmission_time_offset_values;
+ {
+ const absl::optional<uint64_t> unsigned_base_transmission_time_offset =
+ proto.has_transmission_time_offset()
+ ? ToUnsigned(proto.transmission_time_offset())
+ : absl::optional<uint64_t>();
+ transmission_time_offset_values =
+ DecodeDeltas(proto.transmission_time_offset_deltas(),
+ unsigned_base_transmission_time_offset, number_of_deltas);
+ RTC_CHECK_EQ(transmission_time_offset_values.size(), number_of_deltas);
+ }
+
+ // absolute_send_time (RTP extension)
+ std::vector<absl::optional<uint64_t>> absolute_send_time_values;
+ {
+ const absl::optional<uint64_t> base_absolute_send_time =
+ proto.has_absolute_send_time() ? proto.absolute_send_time()
+ : absl::optional<uint64_t>();
+ absolute_send_time_values =
+ DecodeDeltas(proto.absolute_send_time_deltas(), base_absolute_send_time,
+ number_of_deltas);
+ RTC_CHECK_EQ(absolute_send_time_values.size(), number_of_deltas);
+ }
+
+ // video_rotation (RTP extension)
+ std::vector<absl::optional<uint64_t>> video_rotation_values;
+ {
+ const absl::optional<uint64_t> base_video_rotation =
+ proto.has_video_rotation() ? proto.video_rotation()
+ : absl::optional<uint64_t>();
+ video_rotation_values = DecodeDeltas(proto.video_rotation_deltas(),
+ base_video_rotation, number_of_deltas);
+ RTC_CHECK_EQ(video_rotation_values.size(), number_of_deltas);
+ }
+
+ // audio_level (RTP extension)
+ std::vector<absl::optional<uint64_t>> audio_level_values;
+ {
+ const absl::optional<uint64_t> base_audio_level =
+ proto.has_audio_level() ? proto.audio_level()
+ : absl::optional<uint64_t>();
+ audio_level_values = DecodeDeltas(proto.audio_level_deltas(),
+ base_audio_level, number_of_deltas);
+ RTC_CHECK_EQ(audio_level_values.size(), number_of_deltas);
+ }
+
+ // Delta decoding
+ for (size_t i = 0; i < number_of_deltas; ++i) {
+ RTC_CHECK(timestamp_ms_values[i].has_value());
+ RTC_CHECK(marker_values[i].has_value());
+ RTC_CHECK(payload_type_values[i].has_value());
+ RTC_CHECK(sequence_number_values[i].has_value());
+ RTC_CHECK(rtp_timestamp_values[i].has_value());
+ RTC_CHECK(ssrc_values[i].has_value());
+ RTC_CHECK(payload_size_values[i].has_value());
+ RTC_CHECK(header_size_values[i].has_value());
+ RTC_CHECK(padding_size_values[i].has_value());
+
+ RTPHeader header;
+ header.markerBit = rtc::checked_cast<bool>(*marker_values[i]);
+ header.payloadType = rtc::checked_cast<uint8_t>(*payload_type_values[i]);
+ header.sequenceNumber =
+ rtc::checked_cast<uint16_t>(*sequence_number_values[i]);
+ header.timestamp = rtc::checked_cast<uint32_t>(*rtp_timestamp_values[i]);
+ header.ssrc = rtc::checked_cast<uint32_t>(*ssrc_values[i]);
+ header.numCSRCs = 0; // TODO(terelius): Implement CSRC.
+ header.paddingLength = rtc::checked_cast<size_t>(*padding_size_values[i]);
+ header.headerLength = rtc::checked_cast<size_t>(*header_size_values[i]);
+ // TODO(terelius): Should we implement payload_type_frequency?
+ if (transport_sequence_number_values.size() > i &&
+ transport_sequence_number_values[i].has_value()) {
+ header.extension.hasTransportSequenceNumber = true;
+ header.extension.transportSequenceNumber = rtc::checked_cast<uint16_t>(
+ transport_sequence_number_values[i].value());
+ }
+ if (transmission_time_offset_values.size() > i &&
+ transmission_time_offset_values[i].has_value()) {
+ header.extension.hasTransmissionTimeOffset = true;
+ int32_t transmission_time_offset;
+ RTC_CHECK(ToSigned(transmission_time_offset_values[i].value(),
+ &transmission_time_offset));
+ header.extension.transmissionTimeOffset = transmission_time_offset;
+ }
+ if (absolute_send_time_values.size() > i &&
+ absolute_send_time_values[i].has_value()) {
+ header.extension.hasAbsoluteSendTime = true;
+ header.extension.absoluteSendTime =
+ rtc::checked_cast<uint32_t>(absolute_send_time_values[i].value());
+ }
+ if (video_rotation_values.size() > i &&
+ video_rotation_values[i].has_value()) {
+ header.extension.hasVideoRotation = true;
+ header.extension.videoRotation = ConvertCVOByteToVideoRotation(
+ rtc::checked_cast<uint8_t>(video_rotation_values[i].value()));
+ }
+ if (audio_level_values.size() > i && audio_level_values[i].has_value()) {
+ header.extension.hasAudioLevel = true;
+ const uint8_t audio_level =
+ rtc::checked_cast<uint8_t>(audio_level_values[i].value());
+ header.extension.voiceActivity = (audio_level >> 7) != 0;
+ header.extension.audioLevel = audio_level & 0x7Fu;
+ }
+ (*rtp_packets_map)[header.ssrc].emplace_back(
+ timestamp_ms_values[i].value() * 1000, header, header.headerLength,
+ payload_size_values[i].value() + header.headerLength +
+ header.paddingLength);
+ }
+}
+
+template <typename ProtoType, typename LoggedType>
+void StoreRtcpPackets(const ProtoType& proto,
+ std::vector<LoggedType>* rtcp_packets) {
+ RTC_CHECK(proto.has_timestamp_ms());
+ RTC_CHECK(proto.has_raw_packet());
+
+ // Base event
+ rtcp_packets->emplace_back(proto.timestamp_ms() * 1000, proto.raw_packet());
+
+ const size_t number_of_deltas =
+ proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u;
+ if (number_of_deltas == 0) {
+ return;
+ }
+
+ // timestamp_ms
+ std::vector<absl::optional<uint64_t>> timestamp_ms_values = DecodeDeltas(
+ proto.timestamp_deltas_ms(), proto.timestamp_ms(), number_of_deltas);
+ RTC_CHECK_EQ(timestamp_ms_values.size(), number_of_deltas);
+
+ // raw_packet
+ RTC_CHECK(proto.has_raw_packet_deltas());
+ std::vector<absl::string_view> raw_packet_values =
+ DecodeBlobs(proto.raw_packet_deltas(), number_of_deltas);
+ RTC_CHECK_EQ(raw_packet_values.size(), number_of_deltas);
+
+ // Delta decoding
+ for (size_t i = 0; i < number_of_deltas; ++i) {
+ RTC_CHECK(timestamp_ms_values[i].has_value());
+ rtcp_packets->emplace_back(
+ 1000 * timestamp_ms_values[i].value(),
+ reinterpret_cast<const uint8_t*>(raw_packet_values[i].data()),
+ raw_packet_values[i].size());
+ }
+}
+
} // namespace
LoggedRtcpPacket::LoggedRtcpPacket(uint64_t timestamp_us,
@@ -1675,109 +1952,71 @@
return rtp_rtcp_matched;
}
-// Helper functions for new format starts here
+// Helper functions for new format start here
void ParsedRtcEventLogNew::StoreParsedNewFormatEvent(
const rtclog2::EventStream& stream) {
RTC_DCHECK_EQ(stream.stream_size(), 0);
- RTC_DCHECK_LE(stream.incoming_rtp_packets_size(), 1);
+ RTC_DCHECK_EQ(
+ stream.incoming_rtp_packets_size() + stream.outgoing_rtp_packets_size() +
+ stream.incoming_rtcp_packets_size() +
+ stream.outgoing_rtcp_packets_size() +
+ stream.audio_playout_events_size() + stream.begin_log_events_size() +
+ stream.end_log_events_size() + stream.loss_based_bwe_updates_size() +
+ stream.delay_based_bwe_updates_size() +
+ stream.audio_network_adaptations_size() +
+ stream.probe_clusters_size() + stream.probe_success_size() +
+ stream.probe_failure_size() + stream.alr_states_size() +
+ stream.ice_candidate_configs_size() +
+ stream.ice_candidate_events_size() +
+ stream.audio_recv_stream_configs_size() +
+ stream.audio_send_stream_configs_size() +
+ stream.video_recv_stream_configs_size() +
+ stream.video_send_stream_configs_size(),
+ 1u);
+
if (stream.incoming_rtp_packets_size() == 1) {
StoreIncomingRtpPackets(stream.incoming_rtp_packets(0));
- }
-
- RTC_DCHECK_LE(stream.outgoing_rtp_packets_size(), 1);
- if (stream.outgoing_rtp_packets_size() == 1) {
- StoreOutgoingRtpPacket(stream.outgoing_rtp_packets(0));
- }
-
- RTC_DCHECK_LE(stream.incoming_rtcp_packets_size(), 1);
- if (stream.incoming_rtcp_packets_size() == 1) {
+ } else if (stream.outgoing_rtp_packets_size() == 1) {
+ StoreOutgoingRtpPackets(stream.outgoing_rtp_packets(0));
+ } else if (stream.incoming_rtcp_packets_size() == 1) {
StoreIncomingRtcpPackets(stream.incoming_rtcp_packets(0));
- }
-
- RTC_DCHECK_LE(stream.outgoing_rtcp_packets_size(), 1);
- if (stream.outgoing_rtcp_packets_size() == 1) {
+ } else if (stream.outgoing_rtcp_packets_size() == 1) {
StoreOutgoingRtcpPackets(stream.outgoing_rtcp_packets(0));
- }
-
- RTC_DCHECK_LE(stream.audio_playout_events_size(), 1);
- if (stream.audio_playout_events_size() == 1) {
+ } else if (stream.audio_playout_events_size() == 1) {
StoreAudioPlayoutEvent(stream.audio_playout_events(0));
- }
-
- RTC_DCHECK_LE(stream.begin_log_events_size(), 1);
- if (stream.begin_log_events_size() == 1) {
+ } else if (stream.begin_log_events_size() == 1) {
StoreStartEvent(stream.begin_log_events(0));
- }
-
- RTC_DCHECK_LE(stream.end_log_events_size(), 1);
- if (stream.end_log_events_size() == 1) {
+ } else if (stream.end_log_events_size() == 1) {
StoreStopEvent(stream.end_log_events(0));
- }
-
- RTC_DCHECK_LE(stream.loss_based_bwe_updates_size(), 1);
- if (stream.loss_based_bwe_updates_size() == 1) {
+ } else if (stream.loss_based_bwe_updates_size() == 1) {
StoreBweLossBasedUpdate(stream.loss_based_bwe_updates(0));
- }
-
- RTC_DCHECK_LE(stream.delay_based_bwe_updates_size(), 1);
- if (stream.delay_based_bwe_updates_size() == 1) {
+ } else if (stream.delay_based_bwe_updates_size() == 1) {
StoreBweDelayBasedUpdate(stream.delay_based_bwe_updates(0));
- }
-
- RTC_DCHECK_LE(stream.audio_network_adaptations_size(), 1);
- if (stream.audio_network_adaptations_size() == 1) {
+ } else if (stream.audio_network_adaptations_size() == 1) {
StoreAudioNetworkAdaptationEvent(stream.audio_network_adaptations(0));
- }
-
- RTC_DCHECK_LE(stream.probe_clusters_size(), 1);
- if (stream.probe_clusters_size() == 1) {
+ } else if (stream.probe_clusters_size() == 1) {
StoreBweProbeClusterCreated(stream.probe_clusters(0));
- }
-
- RTC_DCHECK_LE(stream.probe_success_size(), 1);
- if (stream.probe_success_size() == 1) {
+ } else if (stream.probe_success_size() == 1) {
StoreBweProbeSuccessEvent(stream.probe_success(0));
- }
-
- RTC_DCHECK_LE(stream.probe_failure_size(), 1);
- if (stream.probe_failure_size() == 1) {
+ } else if (stream.probe_failure_size() == 1) {
StoreBweProbeFailureEvent(stream.probe_failure(0));
- }
-
- RTC_DCHECK_LE(stream.alr_states_size(), 1);
- if (stream.alr_states_size() == 1) {
+ } else if (stream.alr_states_size() == 1) {
StoreAlrStateEvent(stream.alr_states(0));
- }
-
- RTC_DCHECK_LE(stream.ice_candidate_configs_size(), 1);
- if (stream.ice_candidate_configs_size() == 1) {
+ } else if (stream.ice_candidate_configs_size() == 1) {
StoreIceCandidatePairConfig(stream.ice_candidate_configs(0));
- }
-
- RTC_DCHECK_LE(stream.ice_candidate_events_size(), 1);
- if (stream.ice_candidate_events_size() == 1) {
+ } else if (stream.ice_candidate_events_size() == 1) {
StoreIceCandidateEvent(stream.ice_candidate_events(0));
- }
-
- RTC_DCHECK_LE(stream.audio_recv_stream_configs_size(), 1);
- if (stream.audio_recv_stream_configs_size() == 1) {
+ } else if (stream.audio_recv_stream_configs_size() == 1) {
StoreAudioRecvConfig(stream.audio_recv_stream_configs(0));
- }
-
- RTC_DCHECK_LE(stream.audio_send_stream_configs_size(), 1);
- if (stream.audio_send_stream_configs_size() == 1) {
+ } else if (stream.audio_send_stream_configs_size() == 1) {
StoreAudioSendConfig(stream.audio_send_stream_configs(0));
- }
-
- RTC_DCHECK_LE(stream.video_recv_stream_configs_size(), 1);
- if (stream.video_recv_stream_configs_size() == 1) {
+ } else if (stream.video_recv_stream_configs_size() == 1) {
StoreVideoRecvConfig(stream.video_recv_stream_configs(0));
- }
-
- RTC_DCHECK_LE(stream.video_send_stream_configs_size(), 1);
- if (stream.video_send_stream_configs_size() == 1) {
+ } else if (stream.video_send_stream_configs_size() == 1) {
StoreVideoSendConfig(stream.video_send_stream_configs(0));
+ } else {
+ RTC_NOTREACHED();
}
}
@@ -1796,157 +2035,59 @@
const rtclog2::AudioPlayoutEvents& proto) {
RTC_CHECK(proto.has_timestamp_ms());
RTC_CHECK(proto.has_local_ssrc());
- LoggedAudioPlayoutEvent audio_playout_event;
- audio_playout_event.timestamp_us = proto.timestamp_ms() * 1000;
- audio_playout_event.ssrc = proto.local_ssrc();
- audio_playout_events_[audio_playout_event.ssrc].push_back(
- audio_playout_event);
- // TODO(terelius): Parse deltas.
+ // Base event
+ auto map_it = audio_playout_events_[proto.local_ssrc()];
+ audio_playout_events_[proto.local_ssrc()].emplace_back(
+ 1000 * proto.timestamp_ms(), proto.local_ssrc());
+
+ const size_t number_of_deltas =
+ proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u;
+ if (number_of_deltas == 0) {
+ return;
+ }
+
+ // timestamp_ms
+ std::vector<absl::optional<uint64_t>> timestamp_ms_values = DecodeDeltas(
+ proto.timestamp_deltas_ms(), proto.timestamp_ms(), number_of_deltas);
+ RTC_CHECK_EQ(timestamp_ms_values.size(), number_of_deltas);
+
+ // local_ssrc
+ std::vector<absl::optional<uint64_t>> local_ssrc_values = DecodeDeltas(
+ proto.local_ssrc_deltas(), proto.local_ssrc(), number_of_deltas);
+ RTC_CHECK_EQ(local_ssrc_values.size(), number_of_deltas);
+
+ // Delta decoding
+ for (size_t i = 0; i < number_of_deltas; ++i) {
+ RTC_CHECK(timestamp_ms_values[i].has_value());
+ RTC_CHECK(local_ssrc_values[i].has_value());
+ RTC_CHECK_LE(local_ssrc_values[i].value(),
+ std::numeric_limits<uint32_t>::max());
+ const uint32_t local_ssrc =
+ static_cast<uint32_t>(local_ssrc_values[i].value());
+ audio_playout_events_[local_ssrc].emplace_back(
+ 1000 * timestamp_ms_values[i].value(), local_ssrc);
+ }
}
void ParsedRtcEventLogNew::StoreIncomingRtpPackets(
const rtclog2::IncomingRtpPackets& proto) {
- RTC_CHECK(proto.has_timestamp_ms());
- int64_t timestamp_ms = proto.timestamp_ms();
-
- RTC_CHECK(proto.has_header_size());
- size_t header_length = proto.header_size();
-
- RTC_CHECK(proto.has_padding_size());
- size_t padding_length = proto.padding_size();
-
- RTC_CHECK(proto.has_payload_size());
- size_t total_length = proto.payload_size() + header_length + padding_length;
-
- RTPHeader header;
- RTC_CHECK(proto.has_marker());
- header.markerBit = proto.marker();
- RTC_CHECK(proto.has_payload_type());
- header.payloadType = proto.payload_type();
- RTC_CHECK(proto.has_sequence_number());
- header.sequenceNumber = proto.sequence_number();
- RTC_CHECK(proto.has_rtp_timestamp());
- header.timestamp = proto.rtp_timestamp();
- RTC_CHECK(proto.has_ssrc());
- header.ssrc = proto.ssrc();
-
- header.numCSRCs = 0; // TODO(terelius): Implement CSRC.
- header.paddingLength = padding_length;
- header.headerLength = header_length;
- // TODO(terelius): Should we implement payload_type_frequency?
-
- if (proto.has_transmission_time_offset()) {
- header.extension.hasTransmissionTimeOffset = true;
- header.extension.transmissionTimeOffset = proto.transmission_time_offset();
- }
- if (proto.has_absolute_send_time()) {
- header.extension.hasAbsoluteSendTime = true;
- header.extension.absoluteSendTime = proto.absolute_send_time();
- }
- if (proto.has_transport_sequence_number()) {
- header.extension.hasTransportSequenceNumber = true;
- header.extension.transportSequenceNumber =
- proto.transport_sequence_number();
- }
- if (proto.has_audio_level()) {
- header.extension.hasAudioLevel = true;
- header.extension.voiceActivity = (proto.audio_level() >> 7) != 0;
- header.extension.audioLevel = proto.audio_level() & 0x7Fu;
- }
- if (proto.has_video_rotation()) {
- header.extension.hasVideoRotation = true;
- header.extension.videoRotation =
- ConvertCVOByteToVideoRotation(proto.video_rotation());
- }
-
- incoming_rtp_packets_map_[header.ssrc].push_back(LoggedRtpPacketIncoming(
- timestamp_ms * 1000, header, header_length, total_length));
- // TODO(terelius): Parse deltas.
+ StoreRtpPackets(proto, &incoming_rtp_packets_map_);
}
-void ParsedRtcEventLogNew::StoreOutgoingRtpPacket(
+void ParsedRtcEventLogNew::StoreOutgoingRtpPackets(
const rtclog2::OutgoingRtpPackets& proto) {
- RTC_CHECK(proto.has_timestamp_ms());
- int64_t timestamp_ms = proto.timestamp_ms();
-
- RTC_CHECK(proto.has_header_size());
- size_t header_length = proto.header_size();
-
- RTC_CHECK(proto.has_padding_size());
- size_t padding_length = proto.padding_size();
-
- RTC_CHECK(proto.has_payload_size());
- size_t total_length = proto.payload_size() + header_length + padding_length;
-
- RTPHeader header;
- RTC_CHECK(proto.has_marker());
- header.markerBit = proto.marker();
- RTC_CHECK(proto.has_payload_type());
- header.payloadType = proto.payload_type();
- RTC_CHECK(proto.has_sequence_number());
- header.sequenceNumber = proto.sequence_number();
- RTC_CHECK(proto.has_rtp_timestamp());
- header.timestamp = proto.rtp_timestamp();
- RTC_CHECK(proto.has_ssrc());
- header.ssrc = proto.ssrc();
-
- header.numCSRCs = 0; // TODO(terelius): Implement CSRC.
- header.paddingLength = padding_length;
- header.headerLength = header_length;
- // TODO(terelius): Should we implement payload_type_frequency?
-
- if (proto.has_transmission_time_offset()) {
- header.extension.hasTransmissionTimeOffset = true;
- header.extension.transmissionTimeOffset = proto.transmission_time_offset();
- }
- if (proto.has_absolute_send_time()) {
- header.extension.hasAbsoluteSendTime = true;
- header.extension.absoluteSendTime = proto.absolute_send_time();
- }
- if (proto.has_transport_sequence_number()) {
- header.extension.hasTransportSequenceNumber = true;
- header.extension.transportSequenceNumber =
- proto.transport_sequence_number();
- }
- if (proto.has_audio_level()) {
- header.extension.hasAudioLevel = true;
- header.extension.voiceActivity = (proto.audio_level() >> 7) != 0;
- header.extension.audioLevel = proto.audio_level() & 0x7Fu;
- }
- if (proto.has_video_rotation()) {
- header.extension.hasVideoRotation = true;
- header.extension.videoRotation =
- ConvertCVOByteToVideoRotation(proto.video_rotation());
- }
-
- outgoing_rtp_packets_map_[header.ssrc].push_back(LoggedRtpPacketOutgoing(
- timestamp_ms * 1000, header, header_length, total_length));
- // TODO(terelius): Parse deltas.
+ StoreRtpPackets(proto, &outgoing_rtp_packets_map_);
}
void ParsedRtcEventLogNew::StoreIncomingRtcpPackets(
const rtclog2::IncomingRtcpPackets& proto) {
- RTC_CHECK(proto.has_timestamp_ms());
- int64_t timestamp_ms = proto.timestamp_ms();
-
- RTC_CHECK(proto.has_raw_packet());
- incoming_rtcp_packets_.push_back(
- LoggedRtcpPacketIncoming(timestamp_ms * 1000, proto.raw_packet()));
-
- // TODO(terelius): Parse deltas.
+ StoreRtcpPackets(proto, &incoming_rtcp_packets_);
}
void ParsedRtcEventLogNew::StoreOutgoingRtcpPackets(
const rtclog2::OutgoingRtcpPackets& proto) {
- RTC_CHECK(proto.has_timestamp_ms());
- int64_t timestamp_ms = proto.timestamp_ms();
-
- RTC_CHECK(proto.has_raw_packet());
- outgoing_rtcp_packets_.push_back(
- LoggedRtcpPacketOutgoing(timestamp_ms * 1000, proto.raw_packet()));
-
- // TODO(terelius): Parse deltas.
+ StoreRtcpPackets(proto, &outgoing_rtcp_packets_);
}
void ParsedRtcEventLogNew::StoreStartEvent(
@@ -1971,15 +2112,62 @@
RTC_CHECK(proto.has_fraction_loss());
RTC_CHECK(proto.has_total_packets());
- LoggedBweLossBasedUpdate loss_update;
- loss_update.timestamp_us = proto.timestamp_ms() * 1000;
- loss_update.bitrate_bps = proto.bitrate_bps();
- loss_update.fraction_lost = proto.fraction_loss();
- loss_update.expected_packets = proto.total_packets();
+ // Base event
+ bwe_loss_updates_.emplace_back(1000 * proto.timestamp_ms(),
+ proto.bitrate_bps(), proto.fraction_loss(),
+ proto.total_packets());
- bwe_loss_updates_.push_back(loss_update);
+ const size_t number_of_deltas =
+ proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u;
+ if (number_of_deltas == 0) {
+ return;
+ }
- // TODO(terelius): Parse deltas.
+ // timestamp_ms
+ std::vector<absl::optional<uint64_t>> timestamp_ms_values = DecodeDeltas(
+ proto.timestamp_deltas_ms(), proto.timestamp_ms(), number_of_deltas);
+ RTC_CHECK_EQ(timestamp_ms_values.size(), number_of_deltas);
+
+ // bitrate_bps
+ std::vector<absl::optional<uint64_t>> bitrate_bps_values = DecodeDeltas(
+ proto.bitrate_deltas_bps(), proto.bitrate_bps(), number_of_deltas);
+ RTC_CHECK_EQ(bitrate_bps_values.size(), number_of_deltas);
+
+ // fraction_loss
+ std::vector<absl::optional<uint64_t>> fraction_loss_values = DecodeDeltas(
+ proto.fraction_loss_deltas(), proto.fraction_loss(), number_of_deltas);
+ RTC_CHECK_EQ(fraction_loss_values.size(), number_of_deltas);
+
+ // total_packets
+ std::vector<absl::optional<uint64_t>> total_packets_values = DecodeDeltas(
+ proto.total_packets_deltas(), proto.total_packets(), number_of_deltas);
+ RTC_CHECK_EQ(total_packets_values.size(), number_of_deltas);
+
+ // Delta decoding
+ for (size_t i = 0; i < number_of_deltas; ++i) {
+ RTC_CHECK(timestamp_ms_values[i].has_value());
+
+ RTC_CHECK(bitrate_bps_values[i].has_value());
+ RTC_CHECK_LE(bitrate_bps_values[i].value(),
+ std::numeric_limits<uint32_t>::max());
+ const uint32_t bitrate_bps =
+ static_cast<uint32_t>(bitrate_bps_values[i].value());
+
+ RTC_CHECK(fraction_loss_values[i].has_value());
+ RTC_CHECK_LE(fraction_loss_values[i].value(),
+ std::numeric_limits<uint32_t>::max());
+ const uint32_t fraction_loss =
+ static_cast<uint32_t>(fraction_loss_values[i].value());
+
+ RTC_CHECK(total_packets_values[i].has_value());
+ RTC_CHECK_LE(total_packets_values[i].value(),
+ std::numeric_limits<uint32_t>::max());
+ const uint32_t total_packets =
+ static_cast<uint32_t>(total_packets_values[i].value());
+
+ bwe_loss_updates_.emplace_back(1000 * timestamp_ms_values[i].value(),
+ bitrate_bps, fraction_loss, total_packets);
+ }
}
void ParsedRtcEventLogNew::StoreBweDelayBasedUpdate(
@@ -1988,14 +2176,53 @@
RTC_CHECK(proto.has_bitrate_bps());
RTC_CHECK(proto.has_detector_state());
- LoggedBweDelayBasedUpdate delay_update;
- delay_update.timestamp_us = proto.timestamp_ms() * 1000;
- delay_update.bitrate_bps = proto.bitrate_bps();
- delay_update.detector_state = GetRuntimeDetectorState(proto.detector_state());
+ // Base event
+ const BandwidthUsage base_detector_state =
+ GetRuntimeDetectorState(proto.detector_state());
+ bwe_delay_updates_.emplace_back(1000 * proto.timestamp_ms(),
+ proto.bitrate_bps(), base_detector_state);
- bwe_delay_updates_.push_back(delay_update);
+ const size_t number_of_deltas =
+ proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u;
+ if (number_of_deltas == 0) {
+ return;
+ }
- // TODO(terelius): Parse deltas.
+ // timestamp_ms
+ std::vector<absl::optional<uint64_t>> timestamp_ms_values = DecodeDeltas(
+ proto.timestamp_deltas_ms(), proto.timestamp_ms(), number_of_deltas);
+ RTC_CHECK_EQ(timestamp_ms_values.size(), number_of_deltas);
+
+ // bitrate_bps
+ std::vector<absl::optional<uint64_t>> bitrate_bps_values = DecodeDeltas(
+ proto.bitrate_deltas_bps(), proto.bitrate_bps(), number_of_deltas);
+ RTC_CHECK_EQ(bitrate_bps_values.size(), number_of_deltas);
+
+ // detector_state
+ std::vector<absl::optional<uint64_t>> detector_state_values = DecodeDeltas(
+ proto.detector_state_deltas(),
+ static_cast<uint64_t>(proto.detector_state()), number_of_deltas);
+ RTC_CHECK_EQ(detector_state_values.size(), number_of_deltas);
+
+ // Delta decoding
+ for (size_t i = 0; i < number_of_deltas; ++i) {
+ RTC_CHECK(timestamp_ms_values[i].has_value());
+
+ RTC_CHECK(bitrate_bps_values[i].has_value());
+ RTC_CHECK_LE(bitrate_bps_values[i].value(),
+ std::numeric_limits<uint32_t>::max());
+ const uint32_t bitrate_bps =
+ static_cast<uint32_t>(bitrate_bps_values[i].value());
+
+ RTC_CHECK(detector_state_values[i].has_value());
+ const auto detector_state =
+ static_cast<rtclog2::DelayBasedBweUpdates::DetectorState>(
+ detector_state_values[i].value());
+
+ bwe_delay_updates_.emplace_back(1000 * timestamp_ms_values[i].value(),
+ bitrate_bps,
+ GetRuntimeDetectorState(detector_state));
+ }
}
void ParsedRtcEventLogNew::StoreBweProbeClusterCreated(
@@ -2049,33 +2276,139 @@
void ParsedRtcEventLogNew::StoreAudioNetworkAdaptationEvent(
const rtclog2::AudioNetworkAdaptations& proto) {
- LoggedAudioNetworkAdaptationEvent ana_event;
RTC_CHECK(proto.has_timestamp_ms());
- ana_event.timestamp_us = proto.timestamp_ms() * 1000;
- if (proto.has_bitrate_bps()) {
- ana_event.config.bitrate_bps = proto.bitrate_bps();
- }
- if (proto.has_frame_length_ms()) {
- ana_event.config.frame_length_ms = proto.frame_length_ms();
- }
- if (proto.has_uplink_packet_loss_fraction()) {
- ana_event.config.uplink_packet_loss_fraction =
- proto.uplink_packet_loss_fraction();
- }
- if (proto.has_enable_fec()) {
- ana_event.config.enable_fec = proto.enable_fec();
- }
- if (proto.has_enable_dtx()) {
- ana_event.config.enable_dtx = proto.enable_dtx();
- }
- if (proto.has_num_channels()) {
- ana_event.config.num_channels = proto.num_channels();
+ // Base event
+ {
+ AudioEncoderRuntimeConfig runtime_config;
+ if (proto.has_bitrate_bps()) {
+ runtime_config.bitrate_bps = proto.bitrate_bps();
+ }
+ if (proto.has_frame_length_ms()) {
+ runtime_config.frame_length_ms = proto.frame_length_ms();
+ }
+ if (proto.has_uplink_packet_loss_fraction()) {
+ float uplink_packet_loss_fraction;
+ RTC_CHECK(ParsePacketLossFractionFromProtoFormat(
+ proto.uplink_packet_loss_fraction(), &uplink_packet_loss_fraction));
+ runtime_config.uplink_packet_loss_fraction = uplink_packet_loss_fraction;
+ }
+ if (proto.has_enable_fec()) {
+ runtime_config.enable_fec = proto.enable_fec();
+ }
+ if (proto.has_enable_dtx()) {
+ runtime_config.enable_dtx = proto.enable_dtx();
+ }
+ if (proto.has_num_channels()) {
+ runtime_config.num_channels = proto.num_channels();
+ }
+ audio_network_adaptation_events_.emplace_back(1000 * proto.timestamp_ms(),
+ runtime_config);
}
- audio_network_adaptation_events_.push_back(ana_event);
+ const size_t number_of_deltas =
+ proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u;
+ if (number_of_deltas == 0) {
+ return;
+ }
- // TODO(terelius): Parse deltas.
+ // timestamp_ms
+ std::vector<absl::optional<uint64_t>> timestamp_ms_values = DecodeDeltas(
+ proto.timestamp_deltas_ms(), proto.timestamp_ms(), number_of_deltas);
+ RTC_CHECK_EQ(timestamp_ms_values.size(), number_of_deltas);
+
+ // bitrate_bps
+ const absl::optional<uint64_t> unsigned_base_bitrate_bps =
+ proto.has_bitrate_bps()
+ ? absl::optional<uint64_t>(ToUnsigned(proto.bitrate_bps()))
+ : absl::optional<uint64_t>();
+ std::vector<absl::optional<uint64_t>> bitrate_bps_values = DecodeDeltas(
+ proto.bitrate_deltas_bps(), unsigned_base_bitrate_bps, number_of_deltas);
+ RTC_CHECK_EQ(bitrate_bps_values.size(), number_of_deltas);
+
+ // frame_length_ms
+ const absl::optional<uint64_t> unsigned_base_frame_length_ms =
+ proto.has_frame_length_ms()
+ ? absl::optional<uint64_t>(ToUnsigned(proto.frame_length_ms()))
+ : absl::optional<uint64_t>();
+ std::vector<absl::optional<uint64_t>> frame_length_ms_values =
+ DecodeDeltas(proto.frame_length_deltas_ms(),
+ unsigned_base_frame_length_ms, number_of_deltas);
+ RTC_CHECK_EQ(frame_length_ms_values.size(), number_of_deltas);
+
+ // uplink_packet_loss_fraction
+ const absl::optional<uint64_t> uplink_packet_loss_fraction =
+ proto.has_uplink_packet_loss_fraction()
+ ? absl::optional<uint64_t>(proto.uplink_packet_loss_fraction())
+ : absl::optional<uint64_t>();
+ std::vector<absl::optional<uint64_t>> uplink_packet_loss_fraction_values =
+ DecodeDeltas(proto.uplink_packet_loss_fraction_deltas(),
+ uplink_packet_loss_fraction, number_of_deltas);
+ RTC_CHECK_EQ(uplink_packet_loss_fraction_values.size(), number_of_deltas);
+
+ // enable_fec
+ const absl::optional<uint64_t> enable_fec =
+ proto.has_enable_fec() ? absl::optional<uint64_t>(proto.enable_fec())
+ : absl::optional<uint64_t>();
+ std::vector<absl::optional<uint64_t>> enable_fec_values =
+ DecodeDeltas(proto.enable_fec_deltas(), enable_fec, number_of_deltas);
+ RTC_CHECK_EQ(enable_fec_values.size(), number_of_deltas);
+
+ // enable_dtx
+ const absl::optional<uint64_t> enable_dtx =
+ proto.has_enable_dtx() ? absl::optional<uint64_t>(proto.enable_dtx())
+ : absl::optional<uint64_t>();
+ std::vector<absl::optional<uint64_t>> enable_dtx_values =
+ DecodeDeltas(proto.enable_dtx_deltas(), enable_dtx, number_of_deltas);
+ RTC_CHECK_EQ(enable_dtx_values.size(), number_of_deltas);
+
+ // num_channels
+ const absl::optional<uint64_t> num_channels =
+ proto.has_num_channels() ? absl::optional<uint64_t>(proto.num_channels())
+ : absl::optional<uint64_t>();
+ std::vector<absl::optional<uint64_t>> num_channels_values =
+ DecodeDeltas(proto.num_channels_deltas(), num_channels, number_of_deltas);
+ RTC_CHECK_EQ(num_channels_values.size(), number_of_deltas);
+
+ // Delta decoding
+ for (size_t i = 0; i < number_of_deltas; ++i) {
+ RTC_CHECK(timestamp_ms_values[i].has_value());
+
+ AudioEncoderRuntimeConfig runtime_config;
+ if (bitrate_bps_values[i].has_value()) {
+ int signed_bitrate_bps;
+ RTC_CHECK(ToSigned(bitrate_bps_values[i].value(), &signed_bitrate_bps));
+ runtime_config.bitrate_bps = signed_bitrate_bps;
+ }
+ if (frame_length_ms_values[i].has_value()) {
+ int signed_frame_length_ms;
+ RTC_CHECK(
+ ToSigned(frame_length_ms_values[i].value(), &signed_frame_length_ms));
+ runtime_config.frame_length_ms = signed_frame_length_ms;
+ }
+ if (uplink_packet_loss_fraction_values[i].has_value()) {
+ float uplink_packet_loss_fraction;
+ RTC_CHECK(ParsePacketLossFractionFromProtoFormat(
+ rtc::checked_cast<uint32_t>(
+ uplink_packet_loss_fraction_values[i].value()),
+ &uplink_packet_loss_fraction));
+ runtime_config.uplink_packet_loss_fraction = uplink_packet_loss_fraction;
+ }
+ if (enable_fec_values[i].has_value()) {
+ runtime_config.enable_fec =
+ rtc::checked_cast<bool>(enable_fec_values[i].value());
+ }
+ if (enable_dtx_values[i].has_value()) {
+ runtime_config.enable_dtx =
+ rtc::checked_cast<bool>(enable_dtx_values[i].value());
+ }
+ if (num_channels_values[i].has_value()) {
+ runtime_config.num_channels =
+ rtc::checked_cast<size_t>(num_channels_values[i].value());
+ }
+ audio_network_adaptation_events_.emplace_back(
+ 1000 * timestamp_ms_values[i].value(), runtime_config);
+ }
}
void ParsedRtcEventLogNew::StoreIceCandidatePairConfig(
diff --git a/logging/rtc_event_log/rtc_event_log_parser_new.h b/logging/rtc_event_log/rtc_event_log_parser_new.h
index c4293c7..d81f5f5 100644
--- a/logging/rtc_event_log/rtc_event_log_parser_new.h
+++ b/logging/rtc_event_log/rtc_event_log_parser_new.h
@@ -1013,9 +1013,8 @@
// Parsing functions for new format.
void StoreParsedNewFormatEvent(const rtclog2::EventStream& event);
-
void StoreIncomingRtpPackets(const rtclog2::IncomingRtpPackets& proto);
- void StoreOutgoingRtpPacket(const rtclog2::OutgoingRtpPackets& proto);
+ void StoreOutgoingRtpPackets(const rtclog2::OutgoingRtpPackets& proto);
void StoreIncomingRtcpPackets(const rtclog2::IncomingRtcpPackets& proto);
void StoreOutgoingRtcpPackets(const rtclog2::OutgoingRtcpPackets& proto);
void StoreAudioPlayoutEvent(const rtclog2::AudioPlayoutEvents& proto);
diff --git a/logging/rtc_event_log/rtc_event_log_unittest_helper.cc b/logging/rtc_event_log/rtc_event_log_unittest_helper.cc
index 581e791..9782107 100644
--- a/logging/rtc_event_log/rtc_event_log_unittest_helper.cc
+++ b/logging/rtc_event_log/rtc_event_log_unittest_helper.cc
@@ -12,6 +12,7 @@
#include <string.h> // memcmp
+#include <algorithm>
#include <limits>
#include <memory>
#include <numeric>
@@ -279,7 +280,8 @@
size_t padding_size,
uint32_t ssrc,
const RtpHeaderExtensionMap& extension_map,
- RtpPacket* rtp_packet) {
+ RtpPacket* rtp_packet,
+ bool all_configured_exts) {
constexpr int kMaxPayloadType = 127;
rtp_packet->SetPayloadType(prng_.Rand(kMaxPayloadType));
rtp_packet->SetMarker(prng_.Rand<bool>());
@@ -294,16 +296,30 @@
}
rtp_packet->SetCsrcs(csrcs);
- if (extension_map.IsRegistered(TransmissionOffset::kId))
+ if (extension_map.IsRegistered(TransmissionOffset::kId) &&
+ (all_configured_exts || prng_.Rand<bool>())) {
rtp_packet->SetExtension<TransmissionOffset>(prng_.Rand(0x00ffffff));
- if (extension_map.IsRegistered(AudioLevel::kId))
+ }
+
+ if (extension_map.IsRegistered(AudioLevel::kId) &&
+ (all_configured_exts || prng_.Rand<bool>())) {
rtp_packet->SetExtension<AudioLevel>(prng_.Rand<bool>(), prng_.Rand(127));
- if (extension_map.IsRegistered(AbsoluteSendTime::kId))
+ }
+
+ if (extension_map.IsRegistered(AbsoluteSendTime::kId) &&
+ (all_configured_exts || prng_.Rand<bool>())) {
rtp_packet->SetExtension<AbsoluteSendTime>(prng_.Rand(0x00ffffff));
- if (extension_map.IsRegistered(VideoOrientation::kId))
+ }
+
+ if (extension_map.IsRegistered(VideoOrientation::kId) &&
+ (all_configured_exts || prng_.Rand<bool>())) {
rtp_packet->SetExtension<VideoOrientation>(prng_.Rand(3));
- if (extension_map.IsRegistered(TransportSequenceNumber::kId))
+ }
+
+ if (extension_map.IsRegistered(TransportSequenceNumber::kId) &&
+ (all_configured_exts || prng_.Rand<bool>())) {
rtp_packet->SetExtension<TransportSequenceNumber>(prng_.Rand<uint16_t>());
+ }
RTC_CHECK_LE(rtp_packet->headers_size() + payload_size, IP_PACKET_SIZE);
@@ -317,7 +333,8 @@
std::unique_ptr<RtcEventRtpPacketIncoming> EventGenerator::NewRtpPacketIncoming(
uint32_t ssrc,
- const RtpHeaderExtensionMap& extension_map) {
+ const RtpHeaderExtensionMap& extension_map,
+ bool all_configured_exts) {
constexpr size_t kMaxPaddingLength = 224;
const bool padding = prng_.Rand(0, 9) == 0; // Let padding be 10% probable.
const size_t padding_size = !padding ? 0u : prng_.Rand(0u, kMaxPaddingLength);
@@ -338,14 +355,15 @@
RtpPacketReceived rtp_packet(&extension_map);
RandomizeRtpPacket(payload_size, padding_size, ssrc, extension_map,
- &rtp_packet);
+ &rtp_packet, all_configured_exts);
return absl::make_unique<RtcEventRtpPacketIncoming>(rtp_packet);
}
std::unique_ptr<RtcEventRtpPacketOutgoing> EventGenerator::NewRtpPacketOutgoing(
uint32_t ssrc,
- const RtpHeaderExtensionMap& extension_map) {
+ const RtpHeaderExtensionMap& extension_map,
+ bool all_configured_exts) {
constexpr size_t kMaxPaddingLength = 224;
const bool padding = prng_.Rand(0, 9) == 0; // Let padding be 10% probable.
const size_t padding_size = !padding ? 0u : prng_.Rand(0u, kMaxPaddingLength);
@@ -367,33 +385,34 @@
RtpPacketToSend rtp_packet(&extension_map,
kMaxHeaderSize + payload_size + padding_size);
RandomizeRtpPacket(payload_size, padding_size, ssrc, extension_map,
- &rtp_packet);
+ &rtp_packet, all_configured_exts);
int probe_cluster_id = prng_.Rand(0, 100000);
return absl::make_unique<RtcEventRtpPacketOutgoing>(rtp_packet,
probe_cluster_id);
}
-RtpHeaderExtensionMap EventGenerator::NewRtpHeaderExtensionMap() {
+RtpHeaderExtensionMap EventGenerator::NewRtpHeaderExtensionMap(
+ bool configure_all) {
RtpHeaderExtensionMap extension_map;
std::vector<int> id(RtpExtension::kOneByteHeaderExtensionMaxId -
RtpExtension::kMinId + 1);
std::iota(id.begin(), id.end(), RtpExtension::kMinId);
ShuffleInPlace(&prng_, rtc::ArrayView<int>(id));
- if (prng_.Rand<bool>()) {
+ if (configure_all || prng_.Rand<bool>()) {
extension_map.Register<AudioLevel>(id[0]);
}
- if (prng_.Rand<bool>()) {
+ if (configure_all || prng_.Rand<bool>()) {
extension_map.Register<TransmissionOffset>(id[1]);
}
- if (prng_.Rand<bool>()) {
+ if (configure_all || prng_.Rand<bool>()) {
extension_map.Register<AbsoluteSendTime>(id[2]);
}
- if (prng_.Rand<bool>()) {
+ if (configure_all || prng_.Rand<bool>()) {
extension_map.Register<VideoOrientation>(id[3]);
}
- if (prng_.Rand<bool>()) {
+ if (configure_all || prng_.Rand<bool>()) {
extension_map.Register<TransportSequenceNumber>(id[4]);
}
@@ -508,8 +527,18 @@
logged_event.config.frame_length_ms);
EXPECT_EQ(original_event.config_->num_channels,
logged_event.config.num_channels);
- EXPECT_EQ(original_event.config_->uplink_packet_loss_fraction,
- logged_event.config.uplink_packet_loss_fraction);
+
+ // uplink_packet_loss_fraction
+ ASSERT_EQ(original_event.config_->uplink_packet_loss_fraction.has_value(),
+ logged_event.config.uplink_packet_loss_fraction.has_value());
+ if (original_event.config_->uplink_packet_loss_fraction.has_value()) {
+ const float original =
+ original_event.config_->uplink_packet_loss_fraction.value();
+ const float logged =
+ logged_event.config.uplink_packet_loss_fraction.value();
+ const float uplink_packet_loss_fraction_delta = std::abs(original - logged);
+ EXPECT_LE(uplink_packet_loss_fraction_delta, 0.0001f);
+ }
}
void VerifyLoggedBweDelayBasedUpdate(
@@ -603,7 +632,7 @@
logged_header.extension.hasTransmissionTimeOffset);
if (logged_header.extension.hasTransmissionTimeOffset) {
int32_t offset;
- original_header.GetExtension<TransmissionOffset>(&offset);
+ ASSERT_TRUE(original_header.GetExtension<TransmissionOffset>(&offset));
EXPECT_EQ(offset, logged_header.extension.transmissionTimeOffset);
}
@@ -612,7 +641,7 @@
logged_header.extension.hasAbsoluteSendTime);
if (logged_header.extension.hasAbsoluteSendTime) {
uint32_t sendtime;
- original_header.GetExtension<AbsoluteSendTime>(&sendtime);
+ ASSERT_TRUE(original_header.GetExtension<AbsoluteSendTime>(&sendtime));
EXPECT_EQ(sendtime, logged_header.extension.absoluteSendTime);
}
@@ -621,7 +650,7 @@
logged_header.extension.hasTransportSequenceNumber);
if (logged_header.extension.hasTransportSequenceNumber) {
uint16_t seqnum;
- original_header.GetExtension<TransportSequenceNumber>(&seqnum);
+ ASSERT_TRUE(original_header.GetExtension<TransportSequenceNumber>(&seqnum));
EXPECT_EQ(seqnum, logged_header.extension.transportSequenceNumber);
}
@@ -631,7 +660,8 @@
if (logged_header.extension.hasAudioLevel) {
bool voice_activity;
uint8_t audio_level;
- original_header.GetExtension<AudioLevel>(&voice_activity, &audio_level);
+ ASSERT_TRUE(original_header.GetExtension<AudioLevel>(&voice_activity,
+ &audio_level));
EXPECT_EQ(voice_activity, logged_header.extension.voiceActivity);
EXPECT_EQ(audio_level, logged_header.extension.audioLevel);
}
@@ -641,7 +671,7 @@
logged_header.extension.hasVideoRotation);
if (logged_header.extension.hasVideoRotation) {
uint8_t rotation;
- original_header.GetExtension<VideoOrientation>(&rotation);
+ ASSERT_TRUE(original_header.GetExtension<VideoOrientation>(&rotation));
EXPECT_EQ(ConvertCVOByteToVideoRotation(rotation),
logged_header.extension.videoRotation);
}
diff --git a/logging/rtc_event_log/rtc_event_log_unittest_helper.h b/logging/rtc_event_log/rtc_event_log_unittest_helper.h
index aa75ffc..d25970c 100644
--- a/logging/rtc_event_log/rtc_event_log_unittest_helper.h
+++ b/logging/rtc_event_log/rtc_event_log_unittest_helper.h
@@ -71,21 +71,32 @@
std::unique_ptr<RtcEventRtcpPacketOutgoing> NewRtcpPacketOutgoing();
+ // |all_configured_exts| determines whether the RTP packet exhibits all
+ // configured extensions, or a random subset thereof.
void RandomizeRtpPacket(size_t payload_size,
size_t padding_size,
uint32_t ssrc,
const RtpHeaderExtensionMap& extension_map,
- RtpPacket* rtp_packet);
+ RtpPacket* rtp_packet,
+ bool all_configured_exts);
+ // |all_configured_exts| determines whether the RTP packet exhibits all
+ // configured extensions, or a random subset thereof.
std::unique_ptr<RtcEventRtpPacketIncoming> NewRtpPacketIncoming(
uint32_t ssrc,
- const RtpHeaderExtensionMap& extension_map);
+ const RtpHeaderExtensionMap& extension_map,
+ bool all_configured_exts = true);
+ // |all_configured_exts| determines whether the RTP packet exhibits all
+ // configured extensions, or a random subset thereof.
std::unique_ptr<RtcEventRtpPacketOutgoing> NewRtpPacketOutgoing(
uint32_t ssrc,
- const RtpHeaderExtensionMap& extension_map);
+ const RtpHeaderExtensionMap& extension_map,
+ bool all_configured_exts = true);
- RtpHeaderExtensionMap NewRtpHeaderExtensionMap();
+ // |configure_all| determines whether all supported extensions are configured,
+ // or a random subset.
+ RtpHeaderExtensionMap NewRtpHeaderExtensionMap(bool configure_all = false);
std::unique_ptr<RtcEventAudioReceiveStreamConfig> NewAudioReceiveStreamConfig(
uint32_t ssrc,