diff --git a/logging/BUILD.gn b/logging/BUILD.gn index 27977d34ca..cc19ec6924 100644 --- a/logging/BUILD.gn +++ b/logging/BUILD.gn @@ -153,6 +153,8 @@ rtc_source_set("rtc_event_video") { ] } +# 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_static_library("rtc_event_log_impl_encoder") { "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 @@ if (rtc_enable_protobuf) { ":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 @@ if (rtc_enable_protobuf) { 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 0000000000..7aea47611d --- /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(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(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 0000000000..429f8ed2ad --- /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 + +#include +#include + +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(static_cast>(y)) +template +uint64_t ToUnsigned(T y) { + static_assert(std::is_integral::value, ""); + static_assert(std::is_signed::value, ""); + + // Note that a signed integer whose width is N bits, has N-1 digits. + static_assert(std::numeric_limits::digits < 64, ""); + + constexpr T MIN_T = std::numeric_limits::min(); + constexpr T MAX_T = std::numeric_limits::max(); + + static_assert(MAX_T + MIN_T + 1 >= 0, "MAX_T >= abs(MIN_T) - 1"); + + if (y >= 0) { + return static_cast(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(MAX_T) + 1 + static_cast(y - MIN_T); + } +} + +// Assuming x = ToUnsigned(y), return |y|. +// Note: static_cast(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 +bool ToSigned(uint64_t x, T* y) { + static_assert(std::is_integral::value, ""); + static_assert(std::is_signed::value, ""); + + // Note that a signed integer whose width is N bits, has N-1 digits. + static_assert(std::numeric_limits::digits < 64, ""); + + constexpr T MIN_T = std::numeric_limits::min(); + constexpr T MAX_T = std::numeric_limits::max(); + + using UNSIGNED_T = typename std::make_unsigned::type; + constexpr auto MAX_UNSIGNED_T = std::numeric_limits::max(); + if (x > static_cast(MAX_UNSIGNED_T)) { + return false; // |x| cannot be represented using a T. + } + + if (x <= static_cast(MAX_T)) { + // The original value was positive, so it is safe to just static_cast. + *y = static_cast(x); + } else { // x > static_cast(MAX_T) + const uint64_t neg_x = x - static_cast(MAX_T) - 1; + *y = static_cast(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 0000000000..a7d94406ae --- /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 +#include +#include + +#include "test/gtest.h" + +namespace webrtc_event_logging { +namespace { + +template +class SignednessConversionTest : public testing::Test { + public: + static_assert(std::is_integral::value, ""); + static_assert(std::is_signed::value, ""); +}; + +TYPED_TEST_CASE_P(SignednessConversionTest); + +TYPED_TEST_P(SignednessConversionTest, CorrectlyConvertsLegalValues) { + using T = TypeParam; + std::vector legal_values = {std::numeric_limits::min(), + std::numeric_limits::min() + 1, + -1, + 0, + 1, + std::numeric_limits::max() - 1, + std::numeric_limits::max()}; + for (T val : legal_values) { + const auto unsigned_val = ToUnsigned(val); + T signed_val; + ASSERT_TRUE(ToSigned(unsigned_val, &signed_val)) + << "Failed on " << static_cast(unsigned_val) << "."; + EXPECT_EQ(val, signed_val) + << "Failed on " << static_cast(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::digits == 63; + + if (width_is_64) { + return; // Test irrelevant; illegal values do not exist. + } + + const uint64_t max_legal_value = ToUnsigned(static_cast(-1)); + + const std::vector illegal_values = { + max_legal_value + 1u, max_legal_value + 2u, + std::numeric_limits::max() - 1u, + std::numeric_limits::max()}; + + for (uint64_t unsigned_val : illegal_values) { + T signed_val; + EXPECT_FALSE(ToSigned(unsigned_val, &signed_val)) + << "Failed on " << static_cast(unsigned_val) << "."; + } +} + +REGISTER_TYPED_TEST_CASE_P(SignednessConversionTest, + CorrectlyConvertsLegalValues, + FailsOnConvertingIllegalValues); + +using Types = ::testing::Types; + +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 8b2199bf85..46d99b9704 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 - +#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 @@ RTC_PUSH_IGNORING_WUNDEF() #endif RTC_POP_IGNORING_WUNDEF() +using webrtc_event_logging::ToUnsigned; + namespace webrtc { namespace { @@ -235,6 +239,13 @@ rtclog2::IceCandidatePairEvent::IceCandidatePairEventType ConvertToProtoFormat( return rtclog2::IceCandidatePairEvent::UNKNOWN_CHECK_TYPE; } +uint8_t ConvertAudioLevelToProtoFormat(bool voice_activity, + uint8_t audio_level) { + RTC_DCHECK_EQ(audio_level & static_cast(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 @@ size_t RemoveNonWhitelistedRtcpBlocks(const rtc::Buffer& packet, } return buffer_length; } + +template +void EncodeRtcpPacket(rtc::ArrayView 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> 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 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(&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 +void EncodeRtpPacket(const std::vector& 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 base_transport_sequence_number; + { + uint16_t seqnum; + if (base_event->header_.template GetExtension( + &seqnum)) { + proto_batch->set_transport_sequence_number(seqnum); + base_transport_sequence_number = seqnum; + } + } + + absl::optional unsigned_base_transmission_time_offset; + { + int32_t offset; + if (base_event->header_.template GetExtension( + &offset)) { + proto_batch->set_transmission_time_offset(offset); + unsigned_base_transmission_time_offset = ToUnsigned(offset); + } + } + + absl::optional base_absolute_send_time; + { + uint32_t sendtime; + if (base_event->header_.template GetExtension( + &sendtime)) { + proto_batch->set_absolute_send_time(sendtime); + base_absolute_send_time = sendtime; + } + } + + absl::optional base_video_rotation; + { + VideoRotation video_rotation; + if (base_event->header_.template GetExtension( + &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 base_audio_level; + { + bool voice_activity; + uint8_t audio_level; + if (base_event->header_.template GetExtension(&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> 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( + &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(&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(&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( + &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(&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::string RtcEventLogEncoderNewFormat::EncodeBatch( std::vector probe_result_success_events; std::vector incoming_rtcp_packets; std::vector outgoing_rtcp_packets; - std::vector incoming_rtp_packets; - std::vector outgoing_rtp_packets; + std::map> + incoming_rtp_packets; + std::map> + outgoing_rtp_packets; std::vector video_recv_stream_configs; std::vector video_send_stream_configs; @@ -408,13 +726,15 @@ std::string RtcEventLogEncoderNewFormat::EncodeBatch( case RtcEvent::Type::RtpPacketIncoming: { auto* rtc_event = static_cast(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(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 @@ void RtcEventLogEncoderNewFormat::EncodeAudioNetworkAdaptation( 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()); + } + absl::optional 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> 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 unsigned_base_bitrate_bps = + base_event->config_->bitrate_bps.has_value() + ? ToUnsigned(base_event->config_->bitrate_bps.value()) + : absl::optional(); + 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 unsigned_base_frame_length_ms = + base_event->config_->frame_length_ms.has_value() + ? ToUnsigned(base_event->config_->frame_length_ms.value()) + : absl::optional(); + 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); } - // TODO(terelius): Delta-compress rest of batch. } void RtcEventLogEncoderNewFormat::EncodeAudioPlayout( @@ -516,13 +938,41 @@ void RtcEventLogEncoderNewFormat::EncodeAudioPlayout( 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> 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; + } + 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); } - // TODO(terelius): Delta-compress rest of batch. } void RtcEventLogEncoderNewFormat::EncodeAudioRecvStreamConfig( @@ -571,15 +1021,56 @@ void RtcEventLogEncoderNewFormat::EncodeBweUpdateDelayBased( 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> 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; + } + 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(ConvertToProtoFormat(event->detector_state_)); + } + encoded_deltas = EncodeDeltas( + static_cast(ConvertToProtoFormat(base_event->detector_state_)), + values); + if (!encoded_deltas.empty()) { + proto_batch->set_detector_state_deltas(encoded_deltas); } - // TODO(terelius): Delta-compress rest of batch. } void RtcEventLogEncoderNewFormat::EncodeBweUpdateLossBased( @@ -587,15 +1078,63 @@ void RtcEventLogEncoderNewFormat::EncodeBweUpdateLossBased( 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> 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; + } + 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); } - // TODO(terelius): Delta-compress rest of batch. } void RtcEventLogEncoderNewFormat::EncodeProbeClusterCreated( @@ -640,149 +1179,39 @@ void RtcEventLogEncoderNewFormat::EncodeProbeResultSuccess( void RtcEventLogEncoderNewFormat::EncodeRtcpPacketIncoming( rtc::ArrayView 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 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 batch, + const std::map>& + 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()) { - int32_t offset; - base_event->header_.GetExtension(&offset); - proto_batch->set_transmission_time_offset(offset); - } - if (base_event->header_.HasExtension()) { - uint32_t sendtime; - base_event->header_.GetExtension(&sendtime); - proto_batch->set_absolute_send_time(sendtime); - } - if (base_event->header_.HasExtension()) { - uint16_t seqnum; - base_event->header_.GetExtension(&seqnum); - proto_batch->set_transport_sequence_number(seqnum); - } - if (base_event->header_.HasExtension()) { - bool voice_activity; - uint8_t audio_level; - base_event->header_.GetExtension(&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()) { - VideoRotation video_rotation; - base_event->header_.GetExtension(&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 batch, + const std::map>& + 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()) { - int32_t offset; - base_event->header_.GetExtension(&offset); - proto_batch->set_transmission_time_offset(offset); - } - if (base_event->header_.HasExtension()) { - uint32_t sendtime; - base_event->header_.GetExtension(&sendtime); - proto_batch->set_absolute_send_time(sendtime); - } - if (base_event->header_.HasExtension()) { - uint16_t seqnum; - base_event->header_.GetExtension(&seqnum); - proto_batch->set_transport_sequence_number(seqnum); - } - if (base_event->header_.HasExtension()) { - bool voice_activity; - uint8_t audio_level; - base_event->header_.GetExtension(&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()) { - VideoRotation video_rotation; - base_event->header_.GetExtension(&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 b49286dc8d..d9a3f0a337 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 +#include #include #include +#include #include "api/array_view.h" #include "logging/rtc_event_log/encoder/rtc_event_log_encoder.h" @@ -98,10 +100,12 @@ class RtcEventLogEncoderNewFormat final : public RtcEventLogEncoder { rtc::ArrayView batch, rtclog2::EventStream* event_stream); void EncodeRtpPacketIncoming( - rtc::ArrayView batch, + const std::map>& + batch, rtclog2::EventStream* event_stream); void EncodeRtpPacketOutgoing( - rtc::ArrayView batch, + const std::map>& + batch, rtclog2::EventStream* event_stream); void EncodeVideoRecvStreamConfig( rtc::ArrayView 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 726161e180..b29237e850 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> { + : public testing::TestWithParam> { 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(); else encoder_ = absl::make_unique(); @@ -58,140 +62,345 @@ class RtcEventLogEncoderTest // correct behavior both when all of the values are there, as well as when // only some. void TestRtcEventAudioNetworkAdaptation( - std::unique_ptr runtime_config); + const std::vector>&); + + template + std::unique_ptr NewRtpPacket( + uint32_t ssrc, + const RtpHeaderExtensionMap& extension_map); + + template + const std::vector& GetRtpPacketsBySsrc( + const ParsedRtcEventLogNew* parsed_log); + + template + void TestRtpPackets(); std::deque> history_; - // TODO(eladalon): Once we have more than one possible encoder, parameterize - // encoder selection. std::unique_ptr 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 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 runtime_config) { - // This function is called repeatedly. Clear state between calls. - history_.clear(); - auto original_runtime_config = *runtime_config; - auto event = absl::make_unique( - std::move(runtime_config)); - history_.push_back(event->Copy()); + const std::vector>& + 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 RtcEventLogEncoderTest::NewRtpPacket( + uint32_t ssrc, + const RtpHeaderExtensionMap& extension_map) { + return gen_.NewRtpPacketIncoming(ssrc, extension_map, false); +} + +template <> +std::unique_ptr RtcEventLogEncoderTest::NewRtpPacket( + uint32_t ssrc, + const RtpHeaderExtensionMap& extension_map) { + return gen_.NewRtpPacketOutgoing(ssrc, extension_map, false); +} + +template <> +const std::vector& +RtcEventLogEncoderTest::GetRtpPacketsBySsrc( + const ParsedRtcEventLogNew* parsed_log) { + return parsed_log->incoming_rtp_packets_by_ssrc(); +} + +template <> +const std::vector& +RtcEventLogEncoderTest::GetRtpPacketsBySsrc( + const ParsedRtcEventLogNew* parsed_log) { + return parsed_log->outgoing_rtp_packets_by_ssrc(); +} + +template +void CompareRtpPacketSequences( + const std::vector>& original_events, + const ParsedType& parsed_events); + +template <> +void CompareRtpPacketSequences( + const std::vector>& 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>& 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 +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 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>> 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 event = + (events_by_ssrc[ssrc].empty() || !force_repeated_fields_) + ? NewRtpPacket(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& parsed_rtp_packets = + GetRtpPacketsBySsrc(&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> 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(); - const int bitrate_bps = rtc::checked_cast( - prng_.Rand(0, std::numeric_limits::max())); - runtime_config->bitrate_bps = bitrate_bps; - TestRtcEventAudioNetworkAdaptation(std::move(runtime_config)); + std::vector> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + auto runtime_config = absl::make_unique(); + const int bitrate_bps = rtc::checked_cast( + prng_.Rand(0, std::numeric_limits::max())); + runtime_config->bitrate_bps = bitrate_bps; + events[i] = absl::make_unique( + std::move(runtime_config)); + } else { + events[i] = events[0]->Copy(); + } + } + TestRtcEventAudioNetworkAdaptation(events); } TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationFrameLength) { - auto runtime_config = absl::make_unique(); - const int frame_length_ms = prng_.Rand(1, 1000); - runtime_config->frame_length_ms = frame_length_ms; - TestRtcEventAudioNetworkAdaptation(std::move(runtime_config)); + std::vector> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + auto runtime_config = absl::make_unique(); + const int frame_length_ms = prng_.Rand(1, 1000); + runtime_config->frame_length_ms = frame_length_ms; + events[i] = absl::make_unique( + 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(); - runtime_config->uplink_packet_loss_fraction = plr; - TestRtcEventAudioNetworkAdaptation(std::move(runtime_config)); + std::vector> 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(); + runtime_config->uplink_packet_loss_fraction = plr; + events[i] = absl::make_unique( + 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(); - runtime_config->enable_fec = fec_enabled; - TestRtcEventAudioNetworkAdaptation(std::move(runtime_config)); + std::vector> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + auto runtime_config = absl::make_unique(); + runtime_config->enable_fec = prng_.Rand(); + events[i] = absl::make_unique( + 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(); - runtime_config->enable_dtx = dtx_enabled; - TestRtcEventAudioNetworkAdaptation(std::move(runtime_config)); + std::vector> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + auto runtime_config = absl::make_unique(); + runtime_config->enable_dtx = prng_.Rand(); + events[i] = absl::make_unique( + 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(); - runtime_config->num_channels = channels; - TestRtcEventAudioNetworkAdaptation(std::move(runtime_config)); + std::vector> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + auto runtime_config = absl::make_unique(); + runtime_config->num_channels = prng_.Rand(1, 2); + events[i] = absl::make_unique( + std::move(runtime_config)); + } else { + events[i] = events[0]->Copy(); + } } + TestRtcEventAudioNetworkAdaptation(events); } TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationAll) { - const int bitrate_bps = rtc::checked_cast( - prng_.Rand(0, std::numeric_limits::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(); - 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> events( + event_count_); + for (size_t i = 0; i < event_count_; ++i) { + if (i == 0 || !force_repeated_fields_) { + auto runtime_config = absl::make_unique(); + runtime_config->bitrate_bps = rtc::checked_cast( + prng_.Rand(0, std::numeric_limits::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(); + runtime_config->enable_dtx = prng_.Rand(); + runtime_config->num_channels = prng_.Rand(1, 2); + events[i] = absl::make_unique( + std::move(runtime_config)); + } else { + events[i] = events[0]->Copy(); } } + TestRtcEventAudioNetworkAdaptation(events); } TEST_P(RtcEventLogEncoderTest, RtcEventAudioPlayout) { - uint32_t ssrc = prng_.Rand(); - std::unique_ptr 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 kSsrcPool = {0x00000000, 0x12345678, 0xabcdef01, + 0xffffffff, 0x20171024, 0x19840730, + 0x19831230}; + + std::map>> + 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 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(); RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap(); @@ -207,6 +416,7 @@ TEST_P(RtcEventLogEncoderTest, RtcEventAudioReceiveStreamConfig) { 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(); RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap(); @@ -223,31 +433,47 @@ TEST_P(RtcEventLogEncoderTest, RtcEventAudioSendStreamConfig) { } TEST_P(RtcEventLogEncoderTest, RtcEventBweUpdateDelayBased) { - std::unique_ptr event = - gen_.NewBweUpdateDelayBased(); - history_.push_back(event->Copy()); + std::vector> 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 event = - gen_.NewBweUpdateLossBased(); - history_.push_back(event->Copy()); + std::vector> 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 event = gen_.NewIceCandidatePairConfig(); @@ -263,6 +489,7 @@ TEST_P(RtcEventLogEncoderTest, RtcEventIceCandidatePairConfig) { ice_candidate_pair_configs[0]); } +// TODO(eladalon/terelius): Test with multiple events in the batch. TEST_P(RtcEventLogEncoderTest, RtcEventIceCandidatePair) { std::unique_ptr event = gen_.NewIceCandidatePair(); history_.push_back(event->Copy()); @@ -276,6 +503,8 @@ TEST_P(RtcEventLogEncoderTest, RtcEventIceCandidatePair) { 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_P(RtcEventLogEncoderTest, RtcEventLoggingStarted) { 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_P(RtcEventLogEncoderTest, RtcEventLoggingStopped) { 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 event = gen_.NewProbeClusterCreated(); @@ -311,6 +543,7 @@ TEST_P(RtcEventLogEncoderTest, RtcEventProbeClusterCreated) { *event, bwe_probe_cluster_created_events[0]); } +// TODO(eladalon/terelius): Test with multiple events in the batch. TEST_P(RtcEventLogEncoderTest, RtcEventProbeResultFailure) { std::unique_ptr event = gen_.NewProbeResultFailure(); @@ -324,6 +557,7 @@ TEST_P(RtcEventLogEncoderTest, RtcEventProbeResultFailure) { 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 event = gen_.NewProbeResultSuccess(); @@ -338,73 +572,62 @@ TEST_P(RtcEventLogEncoderTest, RtcEventProbeResultSuccess) { } TEST_P(RtcEventLogEncoderTest, RtcEventRtcpPacketIncoming) { - std::unique_ptr 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> 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 event = - gen_.NewRtcpPacketOutgoing(); - history_.push_back(event->Copy()); + std::vector> 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(); - RtpHeaderExtensionMap extension_map; // TODO(terelius): Test extensions too. - std::unique_ptr 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(); } TEST_P(RtcEventLogEncoderTest, RtcEventRtpPacketOutgoing) { - uint32_t ssrc = prng_.Rand(); - RtpHeaderExtensionMap extension_map; // TODO(terelius): Test extensions too. - std::unique_ptr 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(); } +// TODO(eladalon/terelius): Test with multiple events in the batch. TEST_P(RtcEventLogEncoderTest, RtcEventVideoReceiveStreamConfig) { uint32_t ssrc = prng_.Rand(); RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap(); @@ -420,6 +643,7 @@ TEST_P(RtcEventLogEncoderTest, RtcEventVideoReceiveStreamConfig) { 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(); RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap(); @@ -435,9 +659,12 @@ TEST_P(RtcEventLogEncoderTest, RtcEventVideoSendStreamConfig) { 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 fb8f760ebe..fb649510fa 100644 --- a/logging/rtc_event_log/rtc_event_log2.proto +++ b/logging/rtc_event_log/rtc_event_log2.proto @@ -10,6 +10,8 @@ package webrtc.rtclog2; // 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 @@ message AudioNetworkAdaptations { // 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 d352817334..5b6b26a809 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 @@ void SortPacketFeedbackVectorWithLoss(std::vector* vec) { std::sort(vec->begin(), vec->end(), LossHandlingPacketFeedbackComparator()); } +template +void StoreRtpPackets( + const ProtoType& proto, + std::map>* 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(proto.marker()); + header.payloadType = rtc::checked_cast(proto.payload_type()); + header.sequenceNumber = + rtc::checked_cast(proto.sequence_number()); + header.timestamp = rtc::checked_cast(proto.rtp_timestamp()); + header.ssrc = rtc::checked_cast(proto.ssrc()); + header.numCSRCs = 0; // TODO(terelius): Implement CSRC. + header.paddingLength = rtc::checked_cast(proto.padding_size()); + header.headerLength = rtc::checked_cast(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(proto.transport_sequence_number()); + } + if (proto.has_transmission_time_offset()) { + header.extension.hasTransmissionTimeOffset = true; + header.extension.transmissionTimeOffset = + rtc::checked_cast(proto.transmission_time_offset()); + } + if (proto.has_absolute_send_time()) { + header.extension.hasAbsoluteSendTime = true; + header.extension.absoluteSendTime = + rtc::checked_cast(proto.absolute_send_time()); + } + if (proto.has_video_rotation()) { + header.extension.hasVideoRotation = true; + header.extension.videoRotation = ConvertCVOByteToVideoRotation( + rtc::checked_cast(proto.video_rotation())); + } + if (proto.has_audio_level()) { + header.extension.hasAudioLevel = true; + const uint8_t audio_level = + rtc::checked_cast(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> 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> 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> 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> 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> 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> 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> 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> 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> 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> transport_sequence_number_values; + { + const absl::optional base_transport_sequence_number = + proto.has_transport_sequence_number() + ? proto.transport_sequence_number() + : absl::optional(); + 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> transmission_time_offset_values; + { + const absl::optional unsigned_base_transmission_time_offset = + proto.has_transmission_time_offset() + ? ToUnsigned(proto.transmission_time_offset()) + : absl::optional(); + 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> absolute_send_time_values; + { + const absl::optional base_absolute_send_time = + proto.has_absolute_send_time() ? proto.absolute_send_time() + : absl::optional(); + 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> video_rotation_values; + { + const absl::optional base_video_rotation = + proto.has_video_rotation() ? proto.video_rotation() + : absl::optional(); + 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> audio_level_values; + { + const absl::optional base_audio_level = + proto.has_audio_level() ? proto.audio_level() + : absl::optional(); + 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(*marker_values[i]); + header.payloadType = rtc::checked_cast(*payload_type_values[i]); + header.sequenceNumber = + rtc::checked_cast(*sequence_number_values[i]); + header.timestamp = rtc::checked_cast(*rtp_timestamp_values[i]); + header.ssrc = rtc::checked_cast(*ssrc_values[i]); + header.numCSRCs = 0; // TODO(terelius): Implement CSRC. + header.paddingLength = rtc::checked_cast(*padding_size_values[i]); + header.headerLength = rtc::checked_cast(*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( + 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(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(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(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 +void StoreRtcpPackets(const ProtoType& proto, + std::vector* 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> 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 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(raw_packet_values[i].data()), + raw_packet_values[i].size()); + } +} + } // namespace LoggedRtcpPacket::LoggedRtcpPacket(uint64_t timestamp_us, @@ -1675,109 +1952,71 @@ const std::vector GetNetworkTrace( 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 @@ void ParsedRtcEventLogNew::StoreAudioPlayoutEvent( 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> 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> 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::max()); + const uint32_t local_ssrc = + static_cast(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 @@ void ParsedRtcEventLogNew::StoreBweLossBasedUpdate( 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> 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> 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> 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> 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::max()); + const uint32_t bitrate_bps = + static_cast(bitrate_bps_values[i].value()); + + RTC_CHECK(fraction_loss_values[i].has_value()); + RTC_CHECK_LE(fraction_loss_values[i].value(), + std::numeric_limits::max()); + const uint32_t fraction_loss = + static_cast(fraction_loss_values[i].value()); + + RTC_CHECK(total_packets_values[i].has_value()); + RTC_CHECK_LE(total_packets_values[i].value(), + std::numeric_limits::max()); + const uint32_t total_packets = + static_cast(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 @@ void ParsedRtcEventLogNew::StoreBweDelayBasedUpdate( 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> 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> 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> detector_state_values = DecodeDeltas( + proto.detector_state_deltas(), + static_cast(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::max()); + const uint32_t bitrate_bps = + static_cast(bitrate_bps_values[i].value()); + + RTC_CHECK(detector_state_values[i].has_value()); + const auto detector_state = + static_cast( + 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::StoreBweProbeFailureEvent( 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> 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 unsigned_base_bitrate_bps = + proto.has_bitrate_bps() + ? absl::optional(ToUnsigned(proto.bitrate_bps())) + : absl::optional(); + std::vector> 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 unsigned_base_frame_length_ms = + proto.has_frame_length_ms() + ? absl::optional(ToUnsigned(proto.frame_length_ms())) + : absl::optional(); + std::vector> 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 uplink_packet_loss_fraction = + proto.has_uplink_packet_loss_fraction() + ? absl::optional(proto.uplink_packet_loss_fraction()) + : absl::optional(); + std::vector> 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 enable_fec = + proto.has_enable_fec() ? absl::optional(proto.enable_fec()) + : absl::optional(); + std::vector> 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 enable_dtx = + proto.has_enable_dtx() ? absl::optional(proto.enable_dtx()) + : absl::optional(); + std::vector> 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 num_channels = + proto.has_num_channels() ? absl::optional(proto.num_channels()) + : absl::optional(); + std::vector> 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( + 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(enable_fec_values[i].value()); + } + if (enable_dtx_values[i].has_value()) { + runtime_config.enable_dtx = + rtc::checked_cast(enable_dtx_values[i].value()); + } + if (num_channels_values[i].has_value()) { + runtime_config.num_channels = + rtc::checked_cast(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 c4293c72d1..d81f5f53fa 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 @@ class ParsedRtcEventLogNew { // 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 581e79182b..97821071df 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 // memcmp +#include #include #include #include @@ -279,7 +280,8 @@ void EventGenerator::RandomizeRtpPacket( 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()); @@ -294,16 +296,30 @@ void EventGenerator::RandomizeRtpPacket( } rtp_packet->SetCsrcs(csrcs); - if (extension_map.IsRegistered(TransmissionOffset::kId)) + if (extension_map.IsRegistered(TransmissionOffset::kId) && + (all_configured_exts || prng_.Rand())) { rtp_packet->SetExtension(prng_.Rand(0x00ffffff)); - if (extension_map.IsRegistered(AudioLevel::kId)) + } + + if (extension_map.IsRegistered(AudioLevel::kId) && + (all_configured_exts || prng_.Rand())) { rtp_packet->SetExtension(prng_.Rand(), prng_.Rand(127)); - if (extension_map.IsRegistered(AbsoluteSendTime::kId)) + } + + if (extension_map.IsRegistered(AbsoluteSendTime::kId) && + (all_configured_exts || prng_.Rand())) { rtp_packet->SetExtension(prng_.Rand(0x00ffffff)); - if (extension_map.IsRegistered(VideoOrientation::kId)) + } + + if (extension_map.IsRegistered(VideoOrientation::kId) && + (all_configured_exts || prng_.Rand())) { rtp_packet->SetExtension(prng_.Rand(3)); - if (extension_map.IsRegistered(TransportSequenceNumber::kId)) + } + + if (extension_map.IsRegistered(TransportSequenceNumber::kId) && + (all_configured_exts || prng_.Rand())) { rtp_packet->SetExtension(prng_.Rand()); + } RTC_CHECK_LE(rtp_packet->headers_size() + payload_size, IP_PACKET_SIZE); @@ -317,7 +333,8 @@ void EventGenerator::RandomizeRtpPacket( std::unique_ptr 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 @@ std::unique_ptr EventGenerator::NewRtpPacketIncoming( RtpPacketReceived rtp_packet(&extension_map); RandomizeRtpPacket(payload_size, padding_size, ssrc, extension_map, - &rtp_packet); + &rtp_packet, all_configured_exts); return absl::make_unique(rtp_packet); } std::unique_ptr 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 @@ std::unique_ptr EventGenerator::NewRtpPacketOutgoing( 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(rtp_packet, probe_cluster_id); } -RtpHeaderExtensionMap EventGenerator::NewRtpHeaderExtensionMap() { +RtpHeaderExtensionMap EventGenerator::NewRtpHeaderExtensionMap( + bool configure_all) { RtpHeaderExtensionMap extension_map; std::vector id(RtpExtension::kOneByteHeaderExtensionMaxId - RtpExtension::kMinId + 1); std::iota(id.begin(), id.end(), RtpExtension::kMinId); ShuffleInPlace(&prng_, rtc::ArrayView(id)); - if (prng_.Rand()) { + if (configure_all || prng_.Rand()) { extension_map.Register(id[0]); } - if (prng_.Rand()) { + if (configure_all || prng_.Rand()) { extension_map.Register(id[1]); } - if (prng_.Rand()) { + if (configure_all || prng_.Rand()) { extension_map.Register(id[2]); } - if (prng_.Rand()) { + if (configure_all || prng_.Rand()) { extension_map.Register(id[3]); } - if (prng_.Rand()) { + if (configure_all || prng_.Rand()) { extension_map.Register(id[4]); } @@ -508,8 +527,18 @@ void VerifyLoggedAudioNetworkAdaptationEvent( 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 @@ void VerifyLoggedRtpHeader(const RtpPacket& original_header, logged_header.extension.hasTransmissionTimeOffset); if (logged_header.extension.hasTransmissionTimeOffset) { int32_t offset; - original_header.GetExtension(&offset); + ASSERT_TRUE(original_header.GetExtension(&offset)); EXPECT_EQ(offset, logged_header.extension.transmissionTimeOffset); } @@ -612,7 +641,7 @@ void VerifyLoggedRtpHeader(const RtpPacket& original_header, logged_header.extension.hasAbsoluteSendTime); if (logged_header.extension.hasAbsoluteSendTime) { uint32_t sendtime; - original_header.GetExtension(&sendtime); + ASSERT_TRUE(original_header.GetExtension(&sendtime)); EXPECT_EQ(sendtime, logged_header.extension.absoluteSendTime); } @@ -621,7 +650,7 @@ void VerifyLoggedRtpHeader(const RtpPacket& original_header, logged_header.extension.hasTransportSequenceNumber); if (logged_header.extension.hasTransportSequenceNumber) { uint16_t seqnum; - original_header.GetExtension(&seqnum); + ASSERT_TRUE(original_header.GetExtension(&seqnum)); EXPECT_EQ(seqnum, logged_header.extension.transportSequenceNumber); } @@ -631,7 +660,8 @@ void VerifyLoggedRtpHeader(const RtpPacket& original_header, if (logged_header.extension.hasAudioLevel) { bool voice_activity; uint8_t audio_level; - original_header.GetExtension(&voice_activity, &audio_level); + ASSERT_TRUE(original_header.GetExtension(&voice_activity, + &audio_level)); EXPECT_EQ(voice_activity, logged_header.extension.voiceActivity); EXPECT_EQ(audio_level, logged_header.extension.audioLevel); } @@ -641,7 +671,7 @@ void VerifyLoggedRtpHeader(const RtpPacket& original_header, logged_header.extension.hasVideoRotation); if (logged_header.extension.hasVideoRotation) { uint8_t rotation; - original_header.GetExtension(&rotation); + ASSERT_TRUE(original_header.GetExtension(&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 aa75ffc3d1..d25970cf01 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 @@ class EventGenerator { std::unique_ptr 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 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 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 NewAudioReceiveStreamConfig( uint32_t ssrc,