diff options
Diffstat (limited to 'third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc')
-rw-r--r-- | third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc | 1970 |
1 files changed, 1970 insertions, 0 deletions
diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc new file mode 100644 index 0000000000..2c9e42e064 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc @@ -0,0 +1,1970 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h" + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/network_state_predictor.h" +#include "logging/rtc_event_log/dependency_descriptor_encoder_decoder.h" +#include "logging/rtc_event_log/encoder/blob_encoding.h" +#include "logging/rtc_event_log/encoder/delta_encoding.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" +#include "logging/rtc_event_log/events/rtc_event_alr_state.h" +#include "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h" +#include "logging/rtc_event_log/events/rtc_event_audio_playout.h" +#include "logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h" +#include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" +#include "logging/rtc_event_log/events/rtc_event_frame_decoded.h" +#include "logging/rtc_event_log/events/rtc_event_generic_ack_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_sent.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" +#include "logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.h" +#include "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h" +#include "logging/rtc_event_log/events/rtc_event_probe_result_failure.h" +#include "logging/rtc_event_log/events/rtc_event_probe_result_success.h" +#include "logging/rtc_event_log/events/rtc_event_remote_estimate.h" +#include "logging/rtc_event_log/events/rtc_event_route_change.h" +#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h" +#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h" +#include "logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_video_send_stream_config.h" +#include "logging/rtc_event_log/rtc_stream_config.h" +#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" +#include "modules/rtp_rtcp/include/rtp_cvo.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/rtcp_packet/app.h" +#include "modules/rtp_rtcp/source/rtcp_packet/bye.h" +#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" +#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h" +#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h" +#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sdes.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" +#include "modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet.h" +#include "rtc_base/checks.h" +#include "rtc_base/ignore_wundef.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/field_trial.h" + +// *.pb.h files are generated at build-time by the protobuf compiler. +RTC_PUSH_IGNORING_WUNDEF() +#ifdef WEBRTC_ANDROID_PLATFORM_BUILD +#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log2.pb.h" +#else +#include "logging/rtc_event_log/rtc_event_log2.pb.h" +#endif +RTC_POP_IGNORING_WUNDEF() + +using webrtc_event_logging::ToUnsigned; + +namespace webrtc { + +namespace { +rtclog2::DelayBasedBweUpdates::DetectorState ConvertToProtoFormat( + BandwidthUsage state) { + switch (state) { + case BandwidthUsage::kBwNormal: + return rtclog2::DelayBasedBweUpdates::BWE_NORMAL; + case BandwidthUsage::kBwUnderusing: + return rtclog2::DelayBasedBweUpdates::BWE_UNDERUSING; + case BandwidthUsage::kBwOverusing: + return rtclog2::DelayBasedBweUpdates::BWE_OVERUSING; + case BandwidthUsage::kLast: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::DelayBasedBweUpdates::BWE_UNKNOWN_STATE; +} + +rtclog2::FrameDecodedEvents::Codec ConvertToProtoFormat(VideoCodecType codec) { + switch (codec) { + case VideoCodecType::kVideoCodecGeneric: + return rtclog2::FrameDecodedEvents::CODEC_GENERIC; + case VideoCodecType::kVideoCodecVP8: + return rtclog2::FrameDecodedEvents::CODEC_VP8; + case VideoCodecType::kVideoCodecVP9: + return rtclog2::FrameDecodedEvents::CODEC_VP9; + case VideoCodecType::kVideoCodecAV1: + return rtclog2::FrameDecodedEvents::CODEC_AV1; + case VideoCodecType::kVideoCodecH264: + return rtclog2::FrameDecodedEvents::CODEC_H264; + case VideoCodecType::kVideoCodecMultiplex: + // This codec type is afaik not used. + return rtclog2::FrameDecodedEvents::CODEC_UNKNOWN; + case VideoCodecType::kVideoCodecH265: + return rtclog2::FrameDecodedEvents::CODEC_H265; + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::FrameDecodedEvents::CODEC_UNKNOWN; +} + +rtclog2::BweProbeResultFailure::FailureReason ConvertToProtoFormat( + ProbeFailureReason failure_reason) { + switch (failure_reason) { + case ProbeFailureReason::kInvalidSendReceiveInterval: + return rtclog2::BweProbeResultFailure::INVALID_SEND_RECEIVE_INTERVAL; + case ProbeFailureReason::kInvalidSendReceiveRatio: + return rtclog2::BweProbeResultFailure::INVALID_SEND_RECEIVE_RATIO; + case ProbeFailureReason::kTimeout: + return rtclog2::BweProbeResultFailure::TIMEOUT; + case ProbeFailureReason::kLast: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::BweProbeResultFailure::UNKNOWN; +} + +// Returns true if there are recognized extensions that we should log +// and false if there are no extensions or all extensions are types we don't +// log. The protobuf representation of the header configs is written to +// `proto_config`. +bool ConvertToProtoFormat(const std::vector<RtpExtension>& extensions, + rtclog2::RtpHeaderExtensionConfig* proto_config) { + size_t unknown_extensions = 0; + for (auto& extension : extensions) { + if (extension.uri == RtpExtension::kAudioLevelUri) { + proto_config->set_audio_level_id(extension.id); + } else if (extension.uri == RtpExtension::kTimestampOffsetUri) { + proto_config->set_transmission_time_offset_id(extension.id); + } else if (extension.uri == RtpExtension::kAbsSendTimeUri) { + proto_config->set_absolute_send_time_id(extension.id); + } else if (extension.uri == RtpExtension::kTransportSequenceNumberUri) { + proto_config->set_transport_sequence_number_id(extension.id); + } else if (extension.uri == RtpExtension::kVideoRotationUri) { + proto_config->set_video_rotation_id(extension.id); + } else if (extension.uri == RtpExtension::kDependencyDescriptorUri) { + proto_config->set_dependency_descriptor_id(extension.id); + } else { + ++unknown_extensions; + } + } + return unknown_extensions < extensions.size(); +} + +rtclog2::DtlsTransportStateEvent::DtlsTransportState ConvertToProtoFormat( + webrtc::DtlsTransportState state) { + switch (state) { + case webrtc::DtlsTransportState::kNew: + return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_NEW; + case webrtc::DtlsTransportState::kConnecting: + return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CONNECTING; + case webrtc::DtlsTransportState::kConnected: + return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CONNECTED; + case webrtc::DtlsTransportState::kClosed: + return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CLOSED; + case webrtc::DtlsTransportState::kFailed: + return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_FAILED; + case webrtc::DtlsTransportState::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::DtlsTransportStateEvent::UNKNOWN_DTLS_TRANSPORT_STATE; +} + +rtclog2::IceCandidatePairConfig::IceCandidatePairConfigType +ConvertToProtoFormat(IceCandidatePairConfigType type) { + switch (type) { + case IceCandidatePairConfigType::kAdded: + return rtclog2::IceCandidatePairConfig::ADDED; + case IceCandidatePairConfigType::kUpdated: + return rtclog2::IceCandidatePairConfig::UPDATED; + case IceCandidatePairConfigType::kDestroyed: + return rtclog2::IceCandidatePairConfig::DESTROYED; + case IceCandidatePairConfigType::kSelected: + return rtclog2::IceCandidatePairConfig::SELECTED; + case IceCandidatePairConfigType::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::IceCandidatePairConfig::UNKNOWN_CONFIG_TYPE; +} + +rtclog2::IceCandidatePairConfig::IceCandidateType ConvertToProtoFormat( + IceCandidateType type) { + switch (type) { + case IceCandidateType::kUnknown: + return rtclog2::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE; + case IceCandidateType::kLocal: + return rtclog2::IceCandidatePairConfig::LOCAL; + case IceCandidateType::kStun: + return rtclog2::IceCandidatePairConfig::STUN; + case IceCandidateType::kPrflx: + return rtclog2::IceCandidatePairConfig::PRFLX; + case IceCandidateType::kRelay: + return rtclog2::IceCandidatePairConfig::RELAY; + case IceCandidateType::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE; +} + +rtclog2::IceCandidatePairConfig::Protocol ConvertToProtoFormat( + IceCandidatePairProtocol protocol) { + switch (protocol) { + case IceCandidatePairProtocol::kUnknown: + return rtclog2::IceCandidatePairConfig::UNKNOWN_PROTOCOL; + case IceCandidatePairProtocol::kUdp: + return rtclog2::IceCandidatePairConfig::UDP; + case IceCandidatePairProtocol::kTcp: + return rtclog2::IceCandidatePairConfig::TCP; + case IceCandidatePairProtocol::kSsltcp: + return rtclog2::IceCandidatePairConfig::SSLTCP; + case IceCandidatePairProtocol::kTls: + return rtclog2::IceCandidatePairConfig::TLS; + case IceCandidatePairProtocol::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::IceCandidatePairConfig::UNKNOWN_PROTOCOL; +} + +rtclog2::IceCandidatePairConfig::AddressFamily ConvertToProtoFormat( + IceCandidatePairAddressFamily address_family) { + switch (address_family) { + case IceCandidatePairAddressFamily::kUnknown: + return rtclog2::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY; + case IceCandidatePairAddressFamily::kIpv4: + return rtclog2::IceCandidatePairConfig::IPV4; + case IceCandidatePairAddressFamily::kIpv6: + return rtclog2::IceCandidatePairConfig::IPV6; + case IceCandidatePairAddressFamily::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY; +} + +rtclog2::IceCandidatePairConfig::NetworkType ConvertToProtoFormat( + IceCandidateNetworkType network_type) { + switch (network_type) { + case IceCandidateNetworkType::kUnknown: + return rtclog2::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE; + case IceCandidateNetworkType::kEthernet: + return rtclog2::IceCandidatePairConfig::ETHERNET; + case IceCandidateNetworkType::kLoopback: + return rtclog2::IceCandidatePairConfig::LOOPBACK; + case IceCandidateNetworkType::kWifi: + return rtclog2::IceCandidatePairConfig::WIFI; + case IceCandidateNetworkType::kVpn: + return rtclog2::IceCandidatePairConfig::VPN; + case IceCandidateNetworkType::kCellular: + return rtclog2::IceCandidatePairConfig::CELLULAR; + case IceCandidateNetworkType::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE; +} + +rtclog2::IceCandidatePairEvent::IceCandidatePairEventType ConvertToProtoFormat( + IceCandidatePairEventType type) { + switch (type) { + case IceCandidatePairEventType::kCheckSent: + return rtclog2::IceCandidatePairEvent::CHECK_SENT; + case IceCandidatePairEventType::kCheckReceived: + return rtclog2::IceCandidatePairEvent::CHECK_RECEIVED; + case IceCandidatePairEventType::kCheckResponseSent: + return rtclog2::IceCandidatePairEvent::CHECK_RESPONSE_SENT; + case IceCandidatePairEventType::kCheckResponseReceived: + return rtclog2::IceCandidatePairEvent::CHECK_RESPONSE_RECEIVED; + case IceCandidatePairEventType::kNumValues: + RTC_DCHECK_NOTREACHED(); + } + RTC_DCHECK_NOTREACHED(); + return rtclog2::IceCandidatePairEvent::UNKNOWN_CHECK_TYPE; +} + +// Copies all RTCP blocks except APP, SDES and unknown from `packet` to +// `buffer`. `buffer` must have space for at least `packet.size()` bytes. +size_t RemoveNonAllowlistedRtcpBlocks(const rtc::Buffer& packet, + uint8_t* buffer) { + RTC_DCHECK(buffer != nullptr); + rtcp::CommonHeader header; + const uint8_t* block_begin = packet.data(); + const uint8_t* packet_end = packet.data() + packet.size(); + size_t buffer_length = 0; + while (block_begin < packet_end) { + if (!header.Parse(block_begin, packet_end - block_begin)) { + break; // Incorrect message header. + } + const uint8_t* next_block = header.NextPacket(); + RTC_DCHECK_GT(next_block, block_begin); + RTC_DCHECK_LE(next_block, packet_end); + size_t block_size = next_block - block_begin; + switch (header.type()) { + case rtcp::Bye::kPacketType: + case rtcp::ExtendedReports::kPacketType: + case rtcp::Psfb::kPacketType: + case rtcp::ReceiverReport::kPacketType: + case rtcp::Rtpfb::kPacketType: + case rtcp::SenderReport::kPacketType: + // We log sender reports, receiver reports, bye messages, third-party + // loss reports, payload-specific feedback and extended reports. + // TODO(terelius): As an optimization, don't copy anything if all blocks + // in the packet are allowlisted types. + memcpy(buffer + buffer_length, block_begin, block_size); + buffer_length += block_size; + break; + case rtcp::App::kPacketType: + case rtcp::Sdes::kPacketType: + default: + // We don't log sender descriptions, application defined messages + // or message blocks of unknown type. + break; + } + + block_begin += block_size; + } + return buffer_length; +} + +template <typename EventType, typename ProtoType> +void EncodeRtcpPacket(rtc::ArrayView<const EventType*> batch, + ProtoType* proto_batch) { + if (batch.empty()) { + return; + } + + // Base event + const EventType* const base_event = batch[0]; + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + { + std::vector<uint8_t> buffer(base_event->packet().size()); + size_t buffer_length = + RemoveNonAllowlistedRtcpBlocks(base_event->packet(), buffer.data()); + proto_batch->set_raw_packet(buffer.data(), buffer_length); + } + + if (batch.size() == 1) { + return; + } + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // raw_packet + std::vector<std::string> scrubed_packets(batch.size() - 1); + for (size_t i = 0; i < scrubed_packets.size(); ++i) { + const EventType* event = batch[i + 1]; + scrubed_packets[i].resize(event->packet().size()); + static_assert(sizeof(std::string::value_type) == sizeof(uint8_t), ""); + const size_t buffer_length = RemoveNonAllowlistedRtcpBlocks( + event->packet(), reinterpret_cast<uint8_t*>(&scrubed_packets[i][0])); + if (buffer_length < event->packet().size()) { + scrubed_packets[i].resize(buffer_length); + } + } + proto_batch->set_raw_packet_blobs(EncodeBlobs(scrubed_packets)); +} + +template <typename EventType, typename ProtoType> +void EncodeRtpPacket(const std::vector<const EventType*>& batch, + ProtoType* proto_batch) { + if (batch.empty()) { + return; + } + + // Base event + const EventType* const base_event = batch[0]; + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_marker(base_event->Marker()); + // TODO(terelius): Is payload type needed? + proto_batch->set_payload_type(base_event->PayloadType()); + proto_batch->set_sequence_number(base_event->SequenceNumber()); + proto_batch->set_rtp_timestamp(base_event->Timestamp()); + proto_batch->set_ssrc(base_event->Ssrc()); + proto_batch->set_payload_size(base_event->payload_length()); + proto_batch->set_header_size(base_event->header_length()); + proto_batch->set_padding_size(base_event->padding_length()); + + // Add header extensions (base event). + absl::optional<uint64_t> base_transport_sequence_number; + { + uint16_t seqnum; + if (base_event->template GetExtension<TransportSequenceNumber>(&seqnum)) { + proto_batch->set_transport_sequence_number(seqnum); + base_transport_sequence_number = seqnum; + } + } + + absl::optional<uint64_t> unsigned_base_transmission_time_offset; + { + int32_t offset; + if (base_event->template GetExtension<TransmissionOffset>(&offset)) { + proto_batch->set_transmission_time_offset(offset); + unsigned_base_transmission_time_offset = ToUnsigned(offset); + } + } + + absl::optional<uint64_t> base_absolute_send_time; + { + uint32_t sendtime; + if (base_event->template GetExtension<AbsoluteSendTime>(&sendtime)) { + proto_batch->set_absolute_send_time(sendtime); + base_absolute_send_time = sendtime; + } + } + + absl::optional<uint64_t> base_video_rotation; + { + VideoRotation video_rotation; + if (base_event->template GetExtension<VideoOrientation>(&video_rotation)) { + proto_batch->set_video_rotation( + ConvertVideoRotationToCVOByte(video_rotation)); + base_video_rotation = ConvertVideoRotationToCVOByte(video_rotation); + } + } + + absl::optional<uint64_t> base_audio_level; + absl::optional<uint64_t> base_voice_activity; + { + bool voice_activity; + uint8_t audio_level; + if (base_event->template GetExtension<AudioLevel>(&voice_activity, + &audio_level)) { + RTC_DCHECK_LE(audio_level, 0x7Fu); + base_audio_level = audio_level; + proto_batch->set_audio_level(audio_level); + + base_voice_activity = voice_activity; + proto_batch->set_voice_activity(voice_activity); + } + } + + { + // TODO(webrtc:14975) Remove this kill switch after DD in RTC event log has + // been rolled out. + if (!webrtc::field_trial::IsDisabled( + "WebRTC-RtcEventLogEncodeDependencyDescriptor")) { + std::vector<rtc::ArrayView<const uint8_t>> raw_dds(batch.size()); + bool has_dd = false; + for (size_t i = 0; i < batch.size(); ++i) { + raw_dds[i] = + batch[i] + ->template GetRawExtension<RtpDependencyDescriptorExtension>(); + has_dd |= !raw_dds[i].empty(); + } + if (has_dd) { + if (auto dd_encoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + raw_dds)) { + *proto_batch->mutable_dependency_descriptor() = *dd_encoded; + } + } + } + } + + if (batch.size() == 1) { + return; + } + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms (event) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // marker (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->Marker(); + } + encoded_deltas = EncodeDeltas(base_event->Marker(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_marker_deltas(encoded_deltas); + } + + // payload_type (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->PayloadType(); + } + encoded_deltas = EncodeDeltas(base_event->PayloadType(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_payload_type_deltas(encoded_deltas); + } + + // sequence_number (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->SequenceNumber(); + } + encoded_deltas = EncodeDeltas(base_event->SequenceNumber(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_sequence_number_deltas(encoded_deltas); + } + + // rtp_timestamp (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->Timestamp(); + } + encoded_deltas = EncodeDeltas(base_event->Timestamp(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_rtp_timestamp_deltas(encoded_deltas); + } + + // ssrc (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->Ssrc(); + } + encoded_deltas = EncodeDeltas(base_event->Ssrc(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_ssrc_deltas(encoded_deltas); + } + + // payload_size (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->payload_length(); + } + encoded_deltas = EncodeDeltas(base_event->payload_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_payload_size_deltas(encoded_deltas); + } + + // header_size (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->header_length(); + } + encoded_deltas = EncodeDeltas(base_event->header_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_header_size_deltas(encoded_deltas); + } + + // padding_size (RTP base) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + values[i] = event->padding_length(); + } + encoded_deltas = EncodeDeltas(base_event->padding_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_padding_size_deltas(encoded_deltas); + } + + // transport_sequence_number (RTP extension) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + uint16_t seqnum; + if (event->template GetExtension<TransportSequenceNumber>(&seqnum)) { + values[i] = seqnum; + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_transport_sequence_number, values); + if (!encoded_deltas.empty()) { + proto_batch->set_transport_sequence_number_deltas(encoded_deltas); + } + + // transmission_time_offset (RTP extension) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + int32_t offset; + if (event->template GetExtension<TransmissionOffset>(&offset)) { + values[i] = ToUnsigned(offset); + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(unsigned_base_transmission_time_offset, values); + if (!encoded_deltas.empty()) { + proto_batch->set_transmission_time_offset_deltas(encoded_deltas); + } + + // absolute_send_time (RTP extension) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + uint32_t sendtime; + if (event->template GetExtension<AbsoluteSendTime>(&sendtime)) { + values[i] = sendtime; + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_absolute_send_time, values); + if (!encoded_deltas.empty()) { + proto_batch->set_absolute_send_time_deltas(encoded_deltas); + } + + // video_rotation (RTP extension) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + VideoRotation video_rotation; + if (event->template GetExtension<VideoOrientation>(&video_rotation)) { + values[i] = ConvertVideoRotationToCVOByte(video_rotation); + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_video_rotation, values); + if (!encoded_deltas.empty()) { + proto_batch->set_video_rotation_deltas(encoded_deltas); + } + + // audio_level (RTP extension) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + bool voice_activity; + uint8_t audio_level; + if (event->template GetExtension<AudioLevel>(&voice_activity, + &audio_level)) { + RTC_DCHECK_LE(audio_level, 0x7Fu); + values[i] = audio_level; + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_audio_level, values); + if (!encoded_deltas.empty()) { + proto_batch->set_audio_level_deltas(encoded_deltas); + } + + // voice_activity (RTP extension) + for (size_t i = 0; i < values.size(); ++i) { + const EventType* event = batch[i + 1]; + bool voice_activity; + uint8_t audio_level; + if (event->template GetExtension<AudioLevel>(&voice_activity, + &audio_level)) { + RTC_DCHECK_LE(audio_level, 0x7Fu); + values[i] = voice_activity; + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_voice_activity, values); + if (!encoded_deltas.empty()) { + proto_batch->set_voice_activity_deltas(encoded_deltas); + } +} +} // namespace + +RtcEventLogEncoderNewFormat::RtcEventLogEncoderNewFormat() { + encode_neteq_set_minimum_delay_kill_switch_ = false; + if (webrtc::field_trial::IsEnabled( + "WebRTC-RtcEventLogEncodeNetEqSetMinimumDelayKillSwitch")) { + encode_neteq_set_minimum_delay_kill_switch_ = true; + } +} + +std::string RtcEventLogEncoderNewFormat::EncodeLogStart(int64_t timestamp_us, + int64_t utc_time_us) { + rtclog2::EventStream event_stream; + rtclog2::BeginLogEvent* proto_batch = event_stream.add_begin_log_events(); + proto_batch->set_timestamp_ms(timestamp_us / 1000); + proto_batch->set_version(2); + proto_batch->set_utc_time_ms(utc_time_us / 1000); + return event_stream.SerializeAsString(); +} + +std::string RtcEventLogEncoderNewFormat::EncodeLogEnd(int64_t timestamp_us) { + rtclog2::EventStream event_stream; + rtclog2::EndLogEvent* proto_batch = event_stream.add_end_log_events(); + proto_batch->set_timestamp_ms(timestamp_us / 1000); + return event_stream.SerializeAsString(); +} + +std::string RtcEventLogEncoderNewFormat::EncodeBatch( + std::deque<std::unique_ptr<RtcEvent>>::const_iterator begin, + std::deque<std::unique_ptr<RtcEvent>>::const_iterator end) { + rtclog2::EventStream event_stream; + std::string encoded_output; + + { + std::vector<const RtcEventAlrState*> alr_state_events; + std::vector<const RtcEventAudioNetworkAdaptation*> + audio_network_adaptation_events; + std::vector<const RtcEventAudioPlayout*> audio_playout_events; + std::vector<const RtcEventNetEqSetMinimumDelay*> + neteq_set_minimum_delay_events; + std::vector<const RtcEventAudioReceiveStreamConfig*> + audio_recv_stream_configs; + std::vector<const RtcEventAudioSendStreamConfig*> audio_send_stream_configs; + std::vector<const RtcEventBweUpdateDelayBased*> bwe_delay_based_updates; + std::vector<const RtcEventBweUpdateLossBased*> bwe_loss_based_updates; + std::vector<const RtcEventDtlsTransportState*> dtls_transport_states; + std::vector<const RtcEventDtlsWritableState*> dtls_writable_states; + std::map<uint32_t /* SSRC */, std::vector<const RtcEventFrameDecoded*>> + frames_decoded; + std::vector<const RtcEventGenericAckReceived*> generic_acks_received; + std::vector<const RtcEventGenericPacketReceived*> generic_packets_received; + std::vector<const RtcEventGenericPacketSent*> generic_packets_sent; + std::vector<const RtcEventIceCandidatePair*> ice_candidate_events; + std::vector<const RtcEventIceCandidatePairConfig*> ice_candidate_configs; + std::vector<const RtcEventProbeClusterCreated*> + probe_cluster_created_events; + std::vector<const RtcEventProbeResultFailure*> probe_result_failure_events; + std::vector<const RtcEventProbeResultSuccess*> probe_result_success_events; + std::vector<const RtcEventRouteChange*> route_change_events; + std::vector<const RtcEventRemoteEstimate*> remote_estimate_events; + std::vector<const RtcEventRtcpPacketIncoming*> incoming_rtcp_packets; + std::vector<const RtcEventRtcpPacketOutgoing*> outgoing_rtcp_packets; + std::map<uint32_t /* SSRC */, std::vector<const RtcEventRtpPacketIncoming*>> + incoming_rtp_packets; + std::map<uint32_t /* SSRC */, std::vector<const RtcEventRtpPacketOutgoing*>> + outgoing_rtp_packets; + std::vector<const RtcEventVideoReceiveStreamConfig*> + video_recv_stream_configs; + std::vector<const RtcEventVideoSendStreamConfig*> video_send_stream_configs; + + for (auto it = begin; it != end; ++it) { + switch ((*it)->GetType()) { + case RtcEvent::Type::AlrStateEvent: { + auto* rtc_event = + static_cast<const RtcEventAlrState* const>(it->get()); + alr_state_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::AudioNetworkAdaptation: { + auto* rtc_event = + static_cast<const RtcEventAudioNetworkAdaptation* const>( + it->get()); + audio_network_adaptation_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::AudioPlayout: { + auto* rtc_event = + static_cast<const RtcEventAudioPlayout* const>(it->get()); + audio_playout_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::AudioReceiveStreamConfig: { + auto* rtc_event = + static_cast<const RtcEventAudioReceiveStreamConfig* const>( + it->get()); + audio_recv_stream_configs.push_back(rtc_event); + break; + } + case RtcEvent::Type::AudioSendStreamConfig: { + auto* rtc_event = + static_cast<const RtcEventAudioSendStreamConfig* const>( + it->get()); + audio_send_stream_configs.push_back(rtc_event); + break; + } + case RtcEvent::Type::BweUpdateDelayBased: { + auto* rtc_event = + static_cast<const RtcEventBweUpdateDelayBased* const>(it->get()); + bwe_delay_based_updates.push_back(rtc_event); + break; + } + case RtcEvent::Type::BweUpdateLossBased: { + auto* rtc_event = + static_cast<const RtcEventBweUpdateLossBased* const>(it->get()); + bwe_loss_based_updates.push_back(rtc_event); + break; + } + case RtcEvent::Type::DtlsTransportState: { + auto* rtc_event = + static_cast<const RtcEventDtlsTransportState* const>(it->get()); + dtls_transport_states.push_back(rtc_event); + break; + } + case RtcEvent::Type::DtlsWritableState: { + auto* rtc_event = + static_cast<const RtcEventDtlsWritableState* const>(it->get()); + dtls_writable_states.push_back(rtc_event); + break; + } + case RtcEvent::Type::ProbeClusterCreated: { + auto* rtc_event = + static_cast<const RtcEventProbeClusterCreated* const>(it->get()); + probe_cluster_created_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::ProbeResultFailure: { + auto* rtc_event = + static_cast<const RtcEventProbeResultFailure* const>(it->get()); + probe_result_failure_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::ProbeResultSuccess: { + auto* rtc_event = + static_cast<const RtcEventProbeResultSuccess* const>(it->get()); + probe_result_success_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::RouteChangeEvent: { + auto* rtc_event = + static_cast<const RtcEventRouteChange* const>(it->get()); + route_change_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::RemoteEstimateEvent: { + auto* rtc_event = + static_cast<const RtcEventRemoteEstimate* const>(it->get()); + remote_estimate_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::RtcpPacketIncoming: { + auto* rtc_event = + static_cast<const RtcEventRtcpPacketIncoming* const>(it->get()); + incoming_rtcp_packets.push_back(rtc_event); + break; + } + case RtcEvent::Type::RtcpPacketOutgoing: { + auto* rtc_event = + static_cast<const RtcEventRtcpPacketOutgoing* const>(it->get()); + outgoing_rtcp_packets.push_back(rtc_event); + break; + } + case RtcEvent::Type::RtpPacketIncoming: { + auto* rtc_event = + static_cast<const RtcEventRtpPacketIncoming* const>(it->get()); + auto& v = incoming_rtp_packets[rtc_event->Ssrc()]; + v.emplace_back(rtc_event); + break; + } + case RtcEvent::Type::RtpPacketOutgoing: { + auto* rtc_event = + static_cast<const RtcEventRtpPacketOutgoing* const>(it->get()); + auto& v = outgoing_rtp_packets[rtc_event->Ssrc()]; + v.emplace_back(rtc_event); + break; + } + case RtcEvent::Type::VideoReceiveStreamConfig: { + auto* rtc_event = + static_cast<const RtcEventVideoReceiveStreamConfig* const>( + it->get()); + video_recv_stream_configs.push_back(rtc_event); + break; + } + case RtcEvent::Type::VideoSendStreamConfig: { + auto* rtc_event = + static_cast<const RtcEventVideoSendStreamConfig* const>( + it->get()); + video_send_stream_configs.push_back(rtc_event); + break; + } + case RtcEvent::Type::IceCandidatePairConfig: { + auto* rtc_event = + static_cast<const RtcEventIceCandidatePairConfig* const>( + it->get()); + ice_candidate_configs.push_back(rtc_event); + break; + } + case RtcEvent::Type::IceCandidatePairEvent: { + auto* rtc_event = + static_cast<const RtcEventIceCandidatePair* const>(it->get()); + ice_candidate_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::GenericPacketReceived: { + auto* rtc_event = + static_cast<const RtcEventGenericPacketReceived* const>( + it->get()); + generic_packets_received.push_back(rtc_event); + break; + } + case RtcEvent::Type::GenericPacketSent: { + auto* rtc_event = + static_cast<const RtcEventGenericPacketSent* const>(it->get()); + generic_packets_sent.push_back(rtc_event); + break; + } + case RtcEvent::Type::GenericAckReceived: { + auto* rtc_event = + static_cast<const RtcEventGenericAckReceived* const>(it->get()); + generic_acks_received.push_back(rtc_event); + break; + } + case RtcEvent::Type::FrameDecoded: { + auto* rtc_event = + static_cast<const RtcEventFrameDecoded* const>(it->get()); + frames_decoded[rtc_event->ssrc()].emplace_back(rtc_event); + break; + } + case RtcEvent::Type::NetEqSetMinimumDelay: { + auto* rtc_event = + static_cast<const RtcEventNetEqSetMinimumDelay* const>(it->get()); + neteq_set_minimum_delay_events.push_back(rtc_event); + break; + } + case RtcEvent::Type::BeginV3Log: + case RtcEvent::Type::EndV3Log: + // These special events are written as part of starting + // and stopping the log, and only as part of version 3 of the format. + RTC_DCHECK_NOTREACHED(); + break; + case RtcEvent::Type::FakeEvent: + // Fake event used for unit test. + RTC_DCHECK_NOTREACHED(); + break; + } + } + + EncodeAlrState(alr_state_events, &event_stream); + EncodeAudioNetworkAdaptation(audio_network_adaptation_events, + &event_stream); + EncodeAudioPlayout(audio_playout_events, &event_stream); + EncodeAudioRecvStreamConfig(audio_recv_stream_configs, &event_stream); + EncodeAudioSendStreamConfig(audio_send_stream_configs, &event_stream); + EncodeNetEqSetMinimumDelay(neteq_set_minimum_delay_events, &event_stream); + EncodeBweUpdateDelayBased(bwe_delay_based_updates, &event_stream); + EncodeBweUpdateLossBased(bwe_loss_based_updates, &event_stream); + EncodeDtlsTransportState(dtls_transport_states, &event_stream); + EncodeDtlsWritableState(dtls_writable_states, &event_stream); + for (const auto& kv : frames_decoded) { + EncodeFramesDecoded(kv.second, &event_stream); + } + EncodeGenericAcksReceived(generic_acks_received, &event_stream); + EncodeGenericPacketsReceived(generic_packets_received, &event_stream); + EncodeGenericPacketsSent(generic_packets_sent, &event_stream); + EncodeIceCandidatePairConfig(ice_candidate_configs, &event_stream); + EncodeIceCandidatePairEvent(ice_candidate_events, &event_stream); + EncodeProbeClusterCreated(probe_cluster_created_events, &event_stream); + EncodeProbeResultFailure(probe_result_failure_events, &event_stream); + EncodeProbeResultSuccess(probe_result_success_events, &event_stream); + EncodeRouteChange(route_change_events, &event_stream); + EncodeRemoteEstimate(remote_estimate_events, &event_stream); + EncodeRtcpPacketIncoming(incoming_rtcp_packets, &event_stream); + EncodeRtcpPacketOutgoing(outgoing_rtcp_packets, &event_stream); + EncodeRtpPacketIncoming(incoming_rtp_packets, &event_stream); + EncodeRtpPacketOutgoing(outgoing_rtp_packets, &event_stream); + EncodeVideoRecvStreamConfig(video_recv_stream_configs, &event_stream); + EncodeVideoSendStreamConfig(video_send_stream_configs, &event_stream); + } // Deallocate the temporary vectors. + + return event_stream.SerializeAsString(); +} + +void RtcEventLogEncoderNewFormat::EncodeAlrState( + rtc::ArrayView<const RtcEventAlrState*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventAlrState* base_event : batch) { + rtclog2::AlrState* proto_batch = event_stream->add_alr_states(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_in_alr(base_event->in_alr()); + } + // TODO(terelius): Should we delta-compress this event type? +} + +void RtcEventLogEncoderNewFormat::EncodeAudioNetworkAdaptation( + rtc::ArrayView<const RtcEventAudioNetworkAdaptation*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) + return; + + // Base event + const RtcEventAudioNetworkAdaptation* const base_event = batch[0]; + rtclog2::AudioNetworkAdaptations* proto_batch = + event_stream->add_audio_network_adaptations(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + if (base_event->config().bitrate_bps.has_value()) + proto_batch->set_bitrate_bps(base_event->config().bitrate_bps.value()); + if (base_event->config().frame_length_ms.has_value()) { + proto_batch->set_frame_length_ms( + base_event->config().frame_length_ms.value()); + } + absl::optional<uint64_t> base_uplink_packet_loss_fraction; + if (base_event->config().uplink_packet_loss_fraction.has_value()) { + base_uplink_packet_loss_fraction = ConvertPacketLossFractionToProtoFormat( + base_event->config().uplink_packet_loss_fraction.value()); + proto_batch->set_uplink_packet_loss_fraction( + base_uplink_packet_loss_fraction.value()); + } + if (base_event->config().enable_fec.has_value()) + proto_batch->set_enable_fec(base_event->config().enable_fec.value()); + if (base_event->config().enable_dtx.has_value()) + proto_batch->set_enable_dtx(base_event->config().enable_dtx.value()); + // Note that `num_channels_deltas` encodes N as N-1, to keep deltas smaller, + // but there's no reason to do the same for the base event's value, since + // no bits will be spared. + if (base_event->config().num_channels.has_value()) + proto_batch->set_num_channels(base_event->config().num_channels.value()); + + if (batch.size() == 1) + return; + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // bitrate_bps + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + if (event->config().bitrate_bps.has_value()) { + values[i] = ToUnsigned(event->config().bitrate_bps.value()); + } else { + values[i].reset(); + } + } + const absl::optional<uint64_t> unsigned_base_bitrate_bps = + base_event->config().bitrate_bps.has_value() + ? ToUnsigned(base_event->config().bitrate_bps.value()) + : absl::optional<uint64_t>(); + encoded_deltas = EncodeDeltas(unsigned_base_bitrate_bps, values); + if (!encoded_deltas.empty()) { + proto_batch->set_bitrate_bps_deltas(encoded_deltas); + } + + // frame_length_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + if (event->config().frame_length_ms.has_value()) { + values[i] = ToUnsigned(event->config().frame_length_ms.value()); + } else { + values[i].reset(); + } + } + const absl::optional<uint64_t> unsigned_base_frame_length_ms = + base_event->config().frame_length_ms.has_value() + ? ToUnsigned(base_event->config().frame_length_ms.value()) + : absl::optional<uint64_t>(); + encoded_deltas = EncodeDeltas(unsigned_base_frame_length_ms, values); + if (!encoded_deltas.empty()) { + proto_batch->set_frame_length_ms_deltas(encoded_deltas); + } + + // uplink_packet_loss_fraction + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + if (event->config().uplink_packet_loss_fraction.has_value()) { + values[i] = ConvertPacketLossFractionToProtoFormat( + event->config().uplink_packet_loss_fraction.value()); + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_uplink_packet_loss_fraction, values); + if (!encoded_deltas.empty()) { + proto_batch->set_uplink_packet_loss_fraction_deltas(encoded_deltas); + } + + // enable_fec + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + values[i] = event->config().enable_fec; + } + encoded_deltas = EncodeDeltas(base_event->config().enable_fec, values); + if (!encoded_deltas.empty()) { + proto_batch->set_enable_fec_deltas(encoded_deltas); + } + + // enable_dtx + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + values[i] = event->config().enable_dtx; + } + encoded_deltas = EncodeDeltas(base_event->config().enable_dtx, values); + if (!encoded_deltas.empty()) { + proto_batch->set_enable_dtx_deltas(encoded_deltas); + } + + // num_channels + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioNetworkAdaptation* event = batch[i + 1]; + const absl::optional<size_t> num_channels = event->config().num_channels; + if (num_channels.has_value()) { + // Since the number of channels is always greater than 0, we can encode + // N channels as N-1, thereby making sure that we get smaller deltas. + // That is, a toggle of 1->2->1 can be encoded as deltas vector (1, 1), + // rather than as (1, 3) or (1, -1), either of which would require two + // bits per delta. + RTC_DCHECK_GT(num_channels.value(), 0u); + values[i] = num_channels.value() - 1; + } else { + values[i].reset(); + } + } + // In the base event, N channels encoded as N channels, but for delta + // compression purposes, also shifted down by 1. + absl::optional<size_t> shifted_base_num_channels; + if (base_event->config().num_channels.has_value()) { + RTC_DCHECK_GT(base_event->config().num_channels.value(), 0u); + shifted_base_num_channels = base_event->config().num_channels.value() - 1; + } + encoded_deltas = EncodeDeltas(shifted_base_num_channels, values); + if (!encoded_deltas.empty()) { + proto_batch->set_num_channels_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeAudioPlayout( + rtc::ArrayView<const RtcEventAudioPlayout*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) + return; + + // Base event + const RtcEventAudioPlayout* const base_event = batch[0]; + rtclog2::AudioPlayoutEvents* proto_batch = + event_stream->add_audio_playout_events(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_local_ssrc(base_event->ssrc()); + + if (batch.size() == 1) + return; + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioPlayout* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // local_ssrc + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventAudioPlayout* event = batch[i + 1]; + values[i] = event->ssrc(); + } + encoded_deltas = EncodeDeltas(base_event->ssrc(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_local_ssrc_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeNetEqSetMinimumDelay( + rtc::ArrayView<const RtcEventNetEqSetMinimumDelay*> batch, + rtclog2::EventStream* event_stream) { + if (encode_neteq_set_minimum_delay_kill_switch_) { + return; + } + if (batch.empty()) { + return; + } + + const RtcEventNetEqSetMinimumDelay* base_event = batch[0]; + + rtclog2::NetEqSetMinimumDelay* proto_batch = + event_stream->add_neteq_set_minimum_delay(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_remote_ssrc(base_event->remote_ssrc()); + proto_batch->set_minimum_delay_ms(base_event->minimum_delay_ms()); + + if (batch.size() == 1) + return; + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventNetEqSetMinimumDelay* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // remote_ssrc + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventNetEqSetMinimumDelay* event = batch[i + 1]; + values[i] = event->remote_ssrc(); + } + encoded_deltas = EncodeDeltas(base_event->remote_ssrc(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_remote_ssrc_deltas(encoded_deltas); + } + + // minimum_delay_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventNetEqSetMinimumDelay* event = batch[i + 1]; + values[i] = ToUnsigned(event->minimum_delay_ms()); + } + encoded_deltas = + EncodeDeltas(ToUnsigned(base_event->minimum_delay_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_minimum_delay_ms_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeAudioRecvStreamConfig( + rtc::ArrayView<const RtcEventAudioReceiveStreamConfig*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventAudioReceiveStreamConfig* base_event : batch) { + rtclog2::AudioRecvStreamConfig* proto_batch = + event_stream->add_audio_recv_stream_configs(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_remote_ssrc(base_event->config().remote_ssrc); + proto_batch->set_local_ssrc(base_event->config().local_ssrc); + + rtclog2::RtpHeaderExtensionConfig* proto_config = + proto_batch->mutable_header_extensions(); + bool has_recognized_extensions = + ConvertToProtoFormat(base_event->config().rtp_extensions, proto_config); + if (!has_recognized_extensions) + proto_batch->clear_header_extensions(); + } +} + +void RtcEventLogEncoderNewFormat::EncodeAudioSendStreamConfig( + rtc::ArrayView<const RtcEventAudioSendStreamConfig*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventAudioSendStreamConfig* base_event : batch) { + rtclog2::AudioSendStreamConfig* proto_batch = + event_stream->add_audio_send_stream_configs(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_ssrc(base_event->config().local_ssrc); + + rtclog2::RtpHeaderExtensionConfig* proto_config = + proto_batch->mutable_header_extensions(); + bool has_recognized_extensions = + ConvertToProtoFormat(base_event->config().rtp_extensions, proto_config); + if (!has_recognized_extensions) + proto_batch->clear_header_extensions(); + } +} + +void RtcEventLogEncoderNewFormat::EncodeBweUpdateDelayBased( + rtc::ArrayView<const RtcEventBweUpdateDelayBased*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) + return; + + // Base event + const RtcEventBweUpdateDelayBased* const base_event = batch[0]; + rtclog2::DelayBasedBweUpdates* proto_batch = + event_stream->add_delay_based_bwe_updates(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_bitrate_bps(base_event->bitrate_bps()); + proto_batch->set_detector_state( + ConvertToProtoFormat(base_event->detector_state())); + + if (batch.size() == 1) + return; + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateDelayBased* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // bitrate_bps + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateDelayBased* event = batch[i + 1]; + values[i] = event->bitrate_bps(); + } + encoded_deltas = EncodeDeltas(base_event->bitrate_bps(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_bitrate_bps_deltas(encoded_deltas); + } + + // detector_state + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateDelayBased* event = batch[i + 1]; + values[i] = + static_cast<uint64_t>(ConvertToProtoFormat(event->detector_state())); + } + encoded_deltas = EncodeDeltas( + static_cast<uint64_t>(ConvertToProtoFormat(base_event->detector_state())), + values); + if (!encoded_deltas.empty()) { + proto_batch->set_detector_state_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeBweUpdateLossBased( + rtc::ArrayView<const RtcEventBweUpdateLossBased*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) + return; + + // Base event + const RtcEventBweUpdateLossBased* const base_event = batch[0]; + rtclog2::LossBasedBweUpdates* proto_batch = + event_stream->add_loss_based_bwe_updates(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_bitrate_bps(base_event->bitrate_bps()); + proto_batch->set_fraction_loss(base_event->fraction_loss()); + proto_batch->set_total_packets(base_event->total_packets()); + + if (batch.size() == 1) + return; + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateLossBased* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // bitrate_bps + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateLossBased* event = batch[i + 1]; + values[i] = event->bitrate_bps(); + } + encoded_deltas = EncodeDeltas(base_event->bitrate_bps(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_bitrate_bps_deltas(encoded_deltas); + } + + // fraction_loss + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateLossBased* event = batch[i + 1]; + values[i] = event->fraction_loss(); + } + encoded_deltas = EncodeDeltas(base_event->fraction_loss(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_fraction_loss_deltas(encoded_deltas); + } + + // total_packets + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventBweUpdateLossBased* event = batch[i + 1]; + values[i] = event->total_packets(); + } + encoded_deltas = EncodeDeltas(base_event->total_packets(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_total_packets_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeDtlsTransportState( + rtc::ArrayView<const RtcEventDtlsTransportState*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventDtlsTransportState* base_event : batch) { + rtclog2::DtlsTransportStateEvent* proto_batch = + event_stream->add_dtls_transport_state_events(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_dtls_transport_state( + ConvertToProtoFormat(base_event->dtls_transport_state())); + } +} + +void RtcEventLogEncoderNewFormat::EncodeDtlsWritableState( + rtc::ArrayView<const RtcEventDtlsWritableState*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventDtlsWritableState* base_event : batch) { + rtclog2::DtlsWritableState* proto_batch = + event_stream->add_dtls_writable_states(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_writable(base_event->writable()); + } +} + +void RtcEventLogEncoderNewFormat::EncodeProbeClusterCreated( + rtc::ArrayView<const RtcEventProbeClusterCreated*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventProbeClusterCreated* base_event : batch) { + rtclog2::BweProbeCluster* proto_batch = event_stream->add_probe_clusters(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_id(base_event->id()); + proto_batch->set_bitrate_bps(base_event->bitrate_bps()); + proto_batch->set_min_packets(base_event->min_probes()); + proto_batch->set_min_bytes(base_event->min_bytes()); + } +} + +void RtcEventLogEncoderNewFormat::EncodeProbeResultFailure( + rtc::ArrayView<const RtcEventProbeResultFailure*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventProbeResultFailure* base_event : batch) { + rtclog2::BweProbeResultFailure* proto_batch = + event_stream->add_probe_failure(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_id(base_event->id()); + proto_batch->set_failure( + ConvertToProtoFormat(base_event->failure_reason())); + } + // TODO(terelius): Should we delta-compress this event type? +} + +void RtcEventLogEncoderNewFormat::EncodeProbeResultSuccess( + rtc::ArrayView<const RtcEventProbeResultSuccess*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventProbeResultSuccess* base_event : batch) { + rtclog2::BweProbeResultSuccess* proto_batch = + event_stream->add_probe_success(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_id(base_event->id()); + proto_batch->set_bitrate_bps(base_event->bitrate_bps()); + } + // TODO(terelius): Should we delta-compress this event type? +} + +void RtcEventLogEncoderNewFormat::EncodeRouteChange( + rtc::ArrayView<const RtcEventRouteChange*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventRouteChange* base_event : batch) { + rtclog2::RouteChange* proto_batch = event_stream->add_route_changes(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_connected(base_event->connected()); + proto_batch->set_overhead(base_event->overhead()); + } + // TODO(terelius): Should we delta-compress this event type? +} + +void RtcEventLogEncoderNewFormat::EncodeRemoteEstimate( + rtc::ArrayView<const RtcEventRemoteEstimate*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) + return; + + // Base event + const auto* const base_event = batch[0]; + rtclog2::RemoteEstimates* proto_batch = event_stream->add_remote_estimates(); + + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + + absl::optional<uint64_t> base_link_capacity_lower; + if (base_event->link_capacity_lower_.IsFinite()) { + base_link_capacity_lower = + base_event->link_capacity_lower_.kbps<uint32_t>(); + proto_batch->set_link_capacity_lower_kbps(*base_link_capacity_lower); + } + absl::optional<uint64_t> base_link_capacity_upper; + if (base_event->link_capacity_upper_.IsFinite()) { + base_link_capacity_upper = + base_event->link_capacity_upper_.kbps<uint32_t>(); + proto_batch->set_link_capacity_upper_kbps(*base_link_capacity_upper); + } + + if (batch.size() == 1) + return; + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const auto* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // link_capacity_lower_kbps + for (size_t i = 0; i < values.size(); ++i) { + const auto* event = batch[i + 1]; + if (event->link_capacity_lower_.IsFinite()) { + values[i] = event->link_capacity_lower_.kbps<uint32_t>(); + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_link_capacity_lower, values); + if (!encoded_deltas.empty()) { + proto_batch->set_link_capacity_lower_kbps_deltas(encoded_deltas); + } + + // link_capacity_upper_kbps + for (size_t i = 0; i < values.size(); ++i) { + const auto* event = batch[i + 1]; + if (event->link_capacity_upper_.IsFinite()) { + values[i] = event->link_capacity_upper_.kbps<uint32_t>(); + } else { + values[i].reset(); + } + } + encoded_deltas = EncodeDeltas(base_link_capacity_upper, values); + if (!encoded_deltas.empty()) { + proto_batch->set_link_capacity_upper_kbps_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeRtcpPacketIncoming( + rtc::ArrayView<const RtcEventRtcpPacketIncoming*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) { + return; + } + EncodeRtcpPacket(batch, event_stream->add_incoming_rtcp_packets()); +} + +void RtcEventLogEncoderNewFormat::EncodeRtcpPacketOutgoing( + rtc::ArrayView<const RtcEventRtcpPacketOutgoing*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) { + return; + } + EncodeRtcpPacket(batch, event_stream->add_outgoing_rtcp_packets()); +} + +void RtcEventLogEncoderNewFormat::EncodeRtpPacketIncoming( + const std::map<uint32_t, std::vector<const RtcEventRtpPacketIncoming*>>& + batch, + rtclog2::EventStream* event_stream) { + for (const auto& it : batch) { + RTC_DCHECK(!it.second.empty()); + EncodeRtpPacket(it.second, event_stream->add_incoming_rtp_packets()); + } +} + +void RtcEventLogEncoderNewFormat::EncodeFramesDecoded( + rtc::ArrayView<const RtcEventFrameDecoded* const> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) { + return; + } + const RtcEventFrameDecoded* const base_event = batch[0]; + rtclog2::FrameDecodedEvents* proto_batch = + event_stream->add_frame_decoded_events(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_ssrc(base_event->ssrc()); + proto_batch->set_render_time_ms(base_event->render_time_ms()); + proto_batch->set_width(base_event->width()); + proto_batch->set_height(base_event->height()); + proto_batch->set_codec(ConvertToProtoFormat(base_event->codec())); + proto_batch->set_qp(base_event->qp()); + + if (batch.size() == 1) { + return; + } + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // SSRC + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = event->ssrc(); + } + encoded_deltas = EncodeDeltas(base_event->ssrc(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_ssrc_deltas(encoded_deltas); + } + + // render_time_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = ToUnsigned(event->render_time_ms()); + } + encoded_deltas = + EncodeDeltas(ToUnsigned(base_event->render_time_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_render_time_ms_deltas(encoded_deltas); + } + + // width + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = ToUnsigned(event->width()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->width()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_width_deltas(encoded_deltas); + } + + // height + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = ToUnsigned(event->height()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->height()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_height_deltas(encoded_deltas); + } + + // codec + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = static_cast<uint64_t>(ConvertToProtoFormat(event->codec())); + } + encoded_deltas = EncodeDeltas( + static_cast<uint64_t>(ConvertToProtoFormat(base_event->codec())), values); + if (!encoded_deltas.empty()) { + proto_batch->set_codec_deltas(encoded_deltas); + } + + // qp + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventFrameDecoded* event = batch[i + 1]; + values[i] = event->qp(); + } + encoded_deltas = EncodeDeltas(base_event->qp(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_qp_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeGenericPacketsSent( + rtc::ArrayView<const RtcEventGenericPacketSent*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) { + return; + } + const RtcEventGenericPacketSent* const base_event = batch[0]; + rtclog2::GenericPacketSent* proto_batch = + event_stream->add_generic_packets_sent(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_packet_number(base_event->packet_number()); + proto_batch->set_overhead_length(base_event->overhead_length()); + proto_batch->set_payload_length(base_event->payload_length()); + proto_batch->set_padding_length(base_event->padding_length()); + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + if (batch.size() == 1) { + return; + } + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketSent* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // packet_number + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketSent* event = batch[i + 1]; + values[i] = ToUnsigned(event->packet_number()); + } + encoded_deltas = + EncodeDeltas(ToUnsigned(base_event->packet_number()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_packet_number_deltas(encoded_deltas); + } + + // overhead_length + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketSent* event = batch[i + 1]; + values[i] = event->overhead_length(); + } + encoded_deltas = EncodeDeltas(base_event->overhead_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_overhead_length_deltas(encoded_deltas); + } + + // payload_length + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketSent* event = batch[i + 1]; + values[i] = event->payload_length(); + } + encoded_deltas = EncodeDeltas(base_event->payload_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_payload_length_deltas(encoded_deltas); + } + + // padding_length + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketSent* event = batch[i + 1]; + values[i] = event->padding_length(); + } + encoded_deltas = EncodeDeltas(base_event->padding_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_padding_length_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeGenericPacketsReceived( + rtc::ArrayView<const RtcEventGenericPacketReceived*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) { + return; + } + const RtcEventGenericPacketReceived* const base_event = batch[0]; + rtclog2::GenericPacketReceived* proto_batch = + event_stream->add_generic_packets_received(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_packet_number(base_event->packet_number()); + proto_batch->set_packet_length(base_event->packet_length()); + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + if (batch.size() == 1) { + return; + } + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketReceived* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // packet_number + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketReceived* event = batch[i + 1]; + values[i] = ToUnsigned(event->packet_number()); + } + encoded_deltas = + EncodeDeltas(ToUnsigned(base_event->packet_number()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_packet_number_deltas(encoded_deltas); + } + + // packet_length + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericPacketReceived* event = batch[i + 1]; + values[i] = event->packet_length(); + } + encoded_deltas = EncodeDeltas(base_event->packet_length(), values); + if (!encoded_deltas.empty()) { + proto_batch->set_packet_length_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeGenericAcksReceived( + rtc::ArrayView<const RtcEventGenericAckReceived*> batch, + rtclog2::EventStream* event_stream) { + if (batch.empty()) { + return; + } + const RtcEventGenericAckReceived* const base_event = batch[0]; + rtclog2::GenericAckReceived* proto_batch = + event_stream->add_generic_acks_received(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_packet_number(base_event->packet_number()); + proto_batch->set_acked_packet_number(base_event->acked_packet_number()); + absl::optional<uint64_t> base_receive_timestamp; + if (base_event->receive_acked_packet_time_ms()) { + int64_t receive_acked_packet_time_ms = + base_event->receive_acked_packet_time_ms().value(); + base_receive_timestamp = ToUnsigned(receive_acked_packet_time_ms); + proto_batch->set_receive_acked_packet_time_ms(receive_acked_packet_time_ms); + } + + // Delta encoding + proto_batch->set_number_of_deltas(batch.size() - 1); + std::vector<absl::optional<uint64_t>> values(batch.size() - 1); + std::string encoded_deltas; + + if (batch.size() == 1) { + return; + } + + // timestamp_ms + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericAckReceived* event = batch[i + 1]; + values[i] = ToUnsigned(event->timestamp_ms()); + } + encoded_deltas = EncodeDeltas(ToUnsigned(base_event->timestamp_ms()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_timestamp_ms_deltas(encoded_deltas); + } + + // packet_number + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericAckReceived* event = batch[i + 1]; + values[i] = ToUnsigned(event->packet_number()); + } + encoded_deltas = + EncodeDeltas(ToUnsigned(base_event->packet_number()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_packet_number_deltas(encoded_deltas); + } + + // acked packet number + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericAckReceived* event = batch[i + 1]; + values[i] = ToUnsigned(event->acked_packet_number()); + } + encoded_deltas = + EncodeDeltas(ToUnsigned(base_event->acked_packet_number()), values); + if (!encoded_deltas.empty()) { + proto_batch->set_acked_packet_number_deltas(encoded_deltas); + } + + // receive timestamp + for (size_t i = 0; i < values.size(); ++i) { + const RtcEventGenericAckReceived* event = batch[i + 1]; + if (event->receive_acked_packet_time_ms()) { + values[i] = ToUnsigned(event->receive_acked_packet_time_ms().value()); + } else { + values[i] = absl::nullopt; + } + } + encoded_deltas = EncodeDeltas(base_receive_timestamp, values); + if (!encoded_deltas.empty()) { + proto_batch->set_receive_acked_packet_time_ms_deltas(encoded_deltas); + } +} + +void RtcEventLogEncoderNewFormat::EncodeRtpPacketOutgoing( + const std::map<uint32_t, std::vector<const RtcEventRtpPacketOutgoing*>>& + batch, + rtclog2::EventStream* event_stream) { + for (const auto& it : batch) { + RTC_DCHECK(!it.second.empty()); + EncodeRtpPacket(it.second, event_stream->add_outgoing_rtp_packets()); + } +} + +void RtcEventLogEncoderNewFormat::EncodeVideoRecvStreamConfig( + rtc::ArrayView<const RtcEventVideoReceiveStreamConfig*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventVideoReceiveStreamConfig* base_event : batch) { + rtclog2::VideoRecvStreamConfig* proto_batch = + event_stream->add_video_recv_stream_configs(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_remote_ssrc(base_event->config().remote_ssrc); + proto_batch->set_local_ssrc(base_event->config().local_ssrc); + proto_batch->set_rtx_ssrc(base_event->config().rtx_ssrc); + + rtclog2::RtpHeaderExtensionConfig* proto_config = + proto_batch->mutable_header_extensions(); + bool has_recognized_extensions = + ConvertToProtoFormat(base_event->config().rtp_extensions, proto_config); + if (!has_recognized_extensions) + proto_batch->clear_header_extensions(); + } +} + +void RtcEventLogEncoderNewFormat::EncodeVideoSendStreamConfig( + rtc::ArrayView<const RtcEventVideoSendStreamConfig*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventVideoSendStreamConfig* base_event : batch) { + rtclog2::VideoSendStreamConfig* proto_batch = + event_stream->add_video_send_stream_configs(); + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_ssrc(base_event->config().local_ssrc); + proto_batch->set_rtx_ssrc(base_event->config().rtx_ssrc); + + rtclog2::RtpHeaderExtensionConfig* proto_config = + proto_batch->mutable_header_extensions(); + bool has_recognized_extensions = + ConvertToProtoFormat(base_event->config().rtp_extensions, proto_config); + if (!has_recognized_extensions) + proto_batch->clear_header_extensions(); + } +} + +void RtcEventLogEncoderNewFormat::EncodeIceCandidatePairConfig( + rtc::ArrayView<const RtcEventIceCandidatePairConfig*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventIceCandidatePairConfig* base_event : batch) { + rtclog2::IceCandidatePairConfig* proto_batch = + event_stream->add_ice_candidate_configs(); + + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + proto_batch->set_config_type(ConvertToProtoFormat(base_event->type())); + proto_batch->set_candidate_pair_id(base_event->candidate_pair_id()); + const auto& desc = base_event->candidate_pair_desc(); + proto_batch->set_local_candidate_type( + ConvertToProtoFormat(desc.local_candidate_type)); + proto_batch->set_local_relay_protocol( + ConvertToProtoFormat(desc.local_relay_protocol)); + proto_batch->set_local_network_type( + ConvertToProtoFormat(desc.local_network_type)); + proto_batch->set_local_address_family( + ConvertToProtoFormat(desc.local_address_family)); + proto_batch->set_remote_candidate_type( + ConvertToProtoFormat(desc.remote_candidate_type)); + proto_batch->set_remote_address_family( + ConvertToProtoFormat(desc.remote_address_family)); + proto_batch->set_candidate_pair_protocol( + ConvertToProtoFormat(desc.candidate_pair_protocol)); + } + // TODO(terelius): Should we delta-compress this event type? +} + +void RtcEventLogEncoderNewFormat::EncodeIceCandidatePairEvent( + rtc::ArrayView<const RtcEventIceCandidatePair*> batch, + rtclog2::EventStream* event_stream) { + for (const RtcEventIceCandidatePair* base_event : batch) { + rtclog2::IceCandidatePairEvent* proto_batch = + event_stream->add_ice_candidate_events(); + + proto_batch->set_timestamp_ms(base_event->timestamp_ms()); + + proto_batch->set_event_type(ConvertToProtoFormat(base_event->type())); + proto_batch->set_candidate_pair_id(base_event->candidate_pair_id()); + proto_batch->set_transaction_id(base_event->transaction_id()); + } + // TODO(terelius): Should we delta-compress this event type? +} + +} // namespace webrtc |