diff options
Diffstat (limited to 'third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.cc')
-rw-r--r-- | third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.cc | 3618 |
1 files changed, 3618 insertions, 0 deletions
diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.cc b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.cc new file mode 100644 index 0000000000..37bb70a69b --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_parser.cc @@ -0,0 +1,3618 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "logging/rtc_event_log/rtc_event_log_parser.h" + +#include <stdint.h> +#include <string.h> + +#include <algorithm> +#include <limits> +#include <map> +#include <utility> + +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/network_state_predictor.h" +#include "api/rtc_event_log/rtc_event_log.h" +#include "api/rtp_headers.h" +#include "api/rtp_parameters.h" +#include "logging/rtc_event_log/dependency_descriptor_encoder_decoder.h" +#include "logging/rtc_event_log/encoder/blob_encoding.h" +#include "logging/rtc_event_log/encoder/delta_encoding.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" +#include "logging/rtc_event_log/encoder/var_int.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/rtc_event_processor.h" +#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" +#include "modules/rtp_rtcp/include/rtp_cvo.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/byte_io.h" +#include "modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "rtc_base/checks.h" +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/numerics/sequence_number_unwrapper.h" +#include "rtc_base/protobuf_utils.h" +#include "rtc_base/system/file_wrapper.h" + +using webrtc_event_logging::ToSigned; +using webrtc_event_logging::ToUnsigned; + +namespace webrtc { + +namespace { +constexpr int64_t kMaxLogSize = 250000000; + +constexpr size_t kIpv4Overhead = 20; +constexpr size_t kIpv6Overhead = 40; +constexpr size_t kUdpOverhead = 8; +constexpr size_t kSrtpOverhead = 10; +constexpr size_t kStunOverhead = 4; +constexpr uint16_t kDefaultOverhead = + kUdpOverhead + kSrtpOverhead + kIpv4Overhead; + +constexpr char kIncompleteLogError[] = + "Could not parse the entire log. Only the beginning will be used."; + +struct MediaStreamInfo { + MediaStreamInfo() = default; + MediaStreamInfo(LoggedMediaType media_type, bool rtx) + : media_type(media_type), rtx(rtx) {} + LoggedMediaType media_type = LoggedMediaType::kUnknown; + bool rtx = false; + SeqNumUnwrapper<uint32_t> unwrap_capture_ticks; +}; + +template <typename Iterable> +void AddRecvStreamInfos(std::map<uint32_t, MediaStreamInfo>* streams, + const Iterable configs, + LoggedMediaType media_type) { + for (auto& conf : configs) { + streams->insert({conf.config.remote_ssrc, {media_type, false}}); + if (conf.config.rtx_ssrc != 0) + streams->insert({conf.config.rtx_ssrc, {media_type, true}}); + } +} +template <typename Iterable> +void AddSendStreamInfos(std::map<uint32_t, MediaStreamInfo>* streams, + const Iterable configs, + LoggedMediaType media_type) { + for (auto& conf : configs) { + streams->insert({conf.config.local_ssrc, {media_type, false}}); + if (conf.config.rtx_ssrc != 0) + streams->insert({conf.config.rtx_ssrc, {media_type, true}}); + } +} +struct OverheadChangeEvent { + Timestamp timestamp; + uint16_t overhead; +}; +std::vector<OverheadChangeEvent> GetOverheadChangingEvents( + const std::vector<InferredRouteChangeEvent>& route_changes, + PacketDirection direction) { + std::vector<OverheadChangeEvent> overheads; + for (auto& event : route_changes) { + uint16_t new_overhead = direction == PacketDirection::kIncomingPacket + ? event.return_overhead + : event.send_overhead; + if (overheads.empty() || new_overhead != overheads.back().overhead) { + overheads.push_back({event.log_time, new_overhead}); + } + } + return overheads; +} + +bool IdenticalRtcpContents(const std::vector<uint8_t>& last_rtcp, + absl::string_view new_rtcp) { + if (last_rtcp.size() != new_rtcp.size()) + return false; + return memcmp(last_rtcp.data(), new_rtcp.data(), new_rtcp.size()) == 0; +} + +// Conversion functions for legacy wire format. +RtcpMode GetRuntimeRtcpMode(rtclog::VideoReceiveConfig::RtcpMode rtcp_mode) { + switch (rtcp_mode) { + case rtclog::VideoReceiveConfig::RTCP_COMPOUND: + return RtcpMode::kCompound; + case rtclog::VideoReceiveConfig::RTCP_REDUCEDSIZE: + return RtcpMode::kReducedSize; + } + RTC_DCHECK_NOTREACHED(); + return RtcpMode::kOff; +} + +BandwidthUsage GetRuntimeDetectorState( + rtclog::DelayBasedBweUpdate::DetectorState detector_state) { + switch (detector_state) { + case rtclog::DelayBasedBweUpdate::BWE_NORMAL: + return BandwidthUsage::kBwNormal; + case rtclog::DelayBasedBweUpdate::BWE_UNDERUSING: + return BandwidthUsage::kBwUnderusing; + case rtclog::DelayBasedBweUpdate::BWE_OVERUSING: + return BandwidthUsage::kBwOverusing; + } + RTC_DCHECK_NOTREACHED(); + return BandwidthUsage::kBwNormal; +} + +IceCandidatePairConfigType GetRuntimeIceCandidatePairConfigType( + rtclog::IceCandidatePairConfig::IceCandidatePairConfigType type) { + switch (type) { + case rtclog::IceCandidatePairConfig::ADDED: + return IceCandidatePairConfigType::kAdded; + case rtclog::IceCandidatePairConfig::UPDATED: + return IceCandidatePairConfigType::kUpdated; + case rtclog::IceCandidatePairConfig::DESTROYED: + return IceCandidatePairConfigType::kDestroyed; + case rtclog::IceCandidatePairConfig::SELECTED: + return IceCandidatePairConfigType::kSelected; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairConfigType::kAdded; +} + +IceCandidateType GetRuntimeIceCandidateType( + rtclog::IceCandidatePairConfig::IceCandidateType type) { + switch (type) { + case rtclog::IceCandidatePairConfig::LOCAL: + return IceCandidateType::kLocal; + case rtclog::IceCandidatePairConfig::STUN: + return IceCandidateType::kStun; + case rtclog::IceCandidatePairConfig::PRFLX: + return IceCandidateType::kPrflx; + case rtclog::IceCandidatePairConfig::RELAY: + return IceCandidateType::kRelay; + case rtclog::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE: + return IceCandidateType::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidateType::kUnknown; +} + +IceCandidatePairProtocol GetRuntimeIceCandidatePairProtocol( + rtclog::IceCandidatePairConfig::Protocol protocol) { + switch (protocol) { + case rtclog::IceCandidatePairConfig::UDP: + return IceCandidatePairProtocol::kUdp; + case rtclog::IceCandidatePairConfig::TCP: + return IceCandidatePairProtocol::kTcp; + case rtclog::IceCandidatePairConfig::SSLTCP: + return IceCandidatePairProtocol::kSsltcp; + case rtclog::IceCandidatePairConfig::TLS: + return IceCandidatePairProtocol::kTls; + case rtclog::IceCandidatePairConfig::UNKNOWN_PROTOCOL: + return IceCandidatePairProtocol::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairProtocol::kUnknown; +} + +IceCandidatePairAddressFamily GetRuntimeIceCandidatePairAddressFamily( + rtclog::IceCandidatePairConfig::AddressFamily address_family) { + switch (address_family) { + case rtclog::IceCandidatePairConfig::IPV4: + return IceCandidatePairAddressFamily::kIpv4; + case rtclog::IceCandidatePairConfig::IPV6: + return IceCandidatePairAddressFamily::kIpv6; + case rtclog::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY: + return IceCandidatePairAddressFamily::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairAddressFamily::kUnknown; +} + +IceCandidateNetworkType GetRuntimeIceCandidateNetworkType( + rtclog::IceCandidatePairConfig::NetworkType network_type) { + switch (network_type) { + case rtclog::IceCandidatePairConfig::ETHERNET: + return IceCandidateNetworkType::kEthernet; + case rtclog::IceCandidatePairConfig::LOOPBACK: + return IceCandidateNetworkType::kLoopback; + case rtclog::IceCandidatePairConfig::WIFI: + return IceCandidateNetworkType::kWifi; + case rtclog::IceCandidatePairConfig::VPN: + return IceCandidateNetworkType::kVpn; + case rtclog::IceCandidatePairConfig::CELLULAR: + return IceCandidateNetworkType::kCellular; + case rtclog::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE: + return IceCandidateNetworkType::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidateNetworkType::kUnknown; +} + +IceCandidatePairEventType GetRuntimeIceCandidatePairEventType( + rtclog::IceCandidatePairEvent::IceCandidatePairEventType type) { + switch (type) { + case rtclog::IceCandidatePairEvent::CHECK_SENT: + return IceCandidatePairEventType::kCheckSent; + case rtclog::IceCandidatePairEvent::CHECK_RECEIVED: + return IceCandidatePairEventType::kCheckReceived; + case rtclog::IceCandidatePairEvent::CHECK_RESPONSE_SENT: + return IceCandidatePairEventType::kCheckResponseSent; + case rtclog::IceCandidatePairEvent::CHECK_RESPONSE_RECEIVED: + return IceCandidatePairEventType::kCheckResponseReceived; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairEventType::kCheckSent; +} + +VideoCodecType GetRuntimeCodecType(rtclog2::FrameDecodedEvents::Codec codec) { + switch (codec) { + case rtclog2::FrameDecodedEvents::CODEC_GENERIC: + return VideoCodecType::kVideoCodecGeneric; + case rtclog2::FrameDecodedEvents::CODEC_VP8: + return VideoCodecType::kVideoCodecVP8; + case rtclog2::FrameDecodedEvents::CODEC_VP9: + return VideoCodecType::kVideoCodecVP9; + case rtclog2::FrameDecodedEvents::CODEC_AV1: + return VideoCodecType::kVideoCodecAV1; + case rtclog2::FrameDecodedEvents::CODEC_H264: + return VideoCodecType::kVideoCodecH264; + case rtclog2::FrameDecodedEvents::CODEC_H265: + return VideoCodecType::kVideoCodecH265; + case rtclog2::FrameDecodedEvents::CODEC_UNKNOWN: + RTC_LOG(LS_ERROR) << "Unknown codec type. Assuming " + "VideoCodecType::kVideoCodecMultiplex"; + return VideoCodecType::kVideoCodecMultiplex; + } + RTC_DCHECK_NOTREACHED(); + return VideoCodecType::kVideoCodecMultiplex; +} + +ParsedRtcEventLog::ParseStatus GetHeaderExtensions( + std::vector<RtpExtension>* header_extensions, + const RepeatedPtrField<rtclog::RtpHeaderExtension>& + proto_header_extensions) { + header_extensions->clear(); + for (auto& p : proto_header_extensions) { + RTC_PARSE_CHECK_OR_RETURN(p.has_name()); + RTC_PARSE_CHECK_OR_RETURN(p.has_id()); + const std::string& name = p.name(); + int id = p.id(); + header_extensions->push_back(RtpExtension(name, id)); + } + return ParsedRtcEventLog::ParseStatus::Success(); +} + +template <typename ProtoType, typename LoggedType> +ParsedRtcEventLog::ParseStatus StoreRtpPackets( + const ProtoType& proto, + std::map<uint32_t, std::vector<LoggedType>>* rtp_packets_map) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_marker()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_payload_type()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_sequence_number()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_rtp_timestamp()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_ssrc()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_payload_size()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_header_size()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_padding_size()); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + const size_t total_packets = number_of_deltas + 1; + + std::vector<std::vector<uint8_t>> dependency_descriptor_wire_format( + total_packets); + if (proto.has_dependency_descriptor()) { + auto status_or_decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode( + proto.dependency_descriptor(), total_packets); + if (!status_or_decoded.ok()) { + return status_or_decoded.status(); + } + dependency_descriptor_wire_format = status_or_decoded.value(); + } + + // Base event + { + RTPHeader header; + header.markerBit = rtc::checked_cast<bool>(proto.marker()); + header.payloadType = rtc::checked_cast<uint8_t>(proto.payload_type()); + header.sequenceNumber = + rtc::checked_cast<uint16_t>(proto.sequence_number()); + header.timestamp = rtc::checked_cast<uint32_t>(proto.rtp_timestamp()); + header.ssrc = rtc::checked_cast<uint32_t>(proto.ssrc()); + header.numCSRCs = 0; // TODO(terelius): Implement CSRC. + header.paddingLength = rtc::checked_cast<size_t>(proto.padding_size()); + header.headerLength = rtc::checked_cast<size_t>(proto.header_size()); + // TODO(terelius): Should we implement payload_type_frequency? + if (proto.has_transport_sequence_number()) { + header.extension.hasTransportSequenceNumber = true; + header.extension.transportSequenceNumber = + rtc::checked_cast<uint16_t>(proto.transport_sequence_number()); + } + if (proto.has_transmission_time_offset()) { + header.extension.hasTransmissionTimeOffset = true; + header.extension.transmissionTimeOffset = + rtc::checked_cast<int32_t>(proto.transmission_time_offset()); + } + if (proto.has_absolute_send_time()) { + header.extension.hasAbsoluteSendTime = true; + header.extension.absoluteSendTime = + rtc::checked_cast<uint32_t>(proto.absolute_send_time()); + } + if (proto.has_video_rotation()) { + header.extension.hasVideoRotation = true; + header.extension.videoRotation = ConvertCVOByteToVideoRotation( + rtc::checked_cast<uint8_t>(proto.video_rotation())); + } + if (proto.has_audio_level()) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_voice_activity()); + header.extension.hasAudioLevel = true; + header.extension.voiceActivity = + rtc::checked_cast<bool>(proto.voice_activity()); + const uint8_t audio_level = + rtc::checked_cast<uint8_t>(proto.audio_level()); + RTC_PARSE_CHECK_OR_RETURN_LE(audio_level, 0x7Fu); + header.extension.audioLevel = audio_level; + } else { + RTC_PARSE_CHECK_OR_RETURN(!proto.has_voice_activity()); + } + LoggedType logged_packet( + Timestamp::Millis(proto.timestamp_ms()), header, proto.header_size(), + proto.payload_size() + header.headerLength + header.paddingLength); + if (!dependency_descriptor_wire_format[0].empty()) { + logged_packet.rtp.dependency_descriptor_wire_format = + dependency_descriptor_wire_format[0]; + } + (*rtp_packets_map)[header.ssrc].push_back(std::move(logged_packet)); + } + + if (number_of_deltas == 0) { + return ParsedRtcEventLog::ParseStatus::Success(); + } + + // timestamp_ms (event) + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // marker (RTP base) + std::vector<absl::optional<uint64_t>> marker_values = + DecodeDeltas(proto.marker_deltas(), proto.marker(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(marker_values.size(), number_of_deltas); + + // payload_type (RTP base) + std::vector<absl::optional<uint64_t>> payload_type_values = DecodeDeltas( + proto.payload_type_deltas(), proto.payload_type(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(payload_type_values.size(), number_of_deltas); + + // sequence_number (RTP base) + std::vector<absl::optional<uint64_t>> sequence_number_values = + DecodeDeltas(proto.sequence_number_deltas(), proto.sequence_number(), + number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(sequence_number_values.size(), number_of_deltas); + + // rtp_timestamp (RTP base) + std::vector<absl::optional<uint64_t>> rtp_timestamp_values = DecodeDeltas( + proto.rtp_timestamp_deltas(), proto.rtp_timestamp(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(rtp_timestamp_values.size(), number_of_deltas); + + // ssrc (RTP base) + std::vector<absl::optional<uint64_t>> ssrc_values = + DecodeDeltas(proto.ssrc_deltas(), proto.ssrc(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(ssrc_values.size(), number_of_deltas); + + // payload_size (RTP base) + std::vector<absl::optional<uint64_t>> payload_size_values = DecodeDeltas( + proto.payload_size_deltas(), proto.payload_size(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(payload_size_values.size(), number_of_deltas); + + // header_size (RTP base) + std::vector<absl::optional<uint64_t>> header_size_values = DecodeDeltas( + proto.header_size_deltas(), proto.header_size(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(header_size_values.size(), number_of_deltas); + + // padding_size (RTP base) + std::vector<absl::optional<uint64_t>> padding_size_values = DecodeDeltas( + proto.padding_size_deltas(), proto.padding_size(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(padding_size_values.size(), number_of_deltas); + + // transport_sequence_number (RTP extension) + std::vector<absl::optional<uint64_t>> transport_sequence_number_values; + { + const absl::optional<uint64_t> base_transport_sequence_number = + proto.has_transport_sequence_number() + ? proto.transport_sequence_number() + : absl::optional<uint64_t>(); + transport_sequence_number_values = + DecodeDeltas(proto.transport_sequence_number_deltas(), + base_transport_sequence_number, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(transport_sequence_number_values.size(), + number_of_deltas); + } + + // transmission_time_offset (RTP extension) + std::vector<absl::optional<uint64_t>> transmission_time_offset_values; + { + const absl::optional<uint64_t> unsigned_base_transmission_time_offset = + proto.has_transmission_time_offset() + ? ToUnsigned(proto.transmission_time_offset()) + : absl::optional<uint64_t>(); + transmission_time_offset_values = + DecodeDeltas(proto.transmission_time_offset_deltas(), + unsigned_base_transmission_time_offset, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(transmission_time_offset_values.size(), + number_of_deltas); + } + + // absolute_send_time (RTP extension) + std::vector<absl::optional<uint64_t>> absolute_send_time_values; + { + const absl::optional<uint64_t> base_absolute_send_time = + proto.has_absolute_send_time() ? proto.absolute_send_time() + : absl::optional<uint64_t>(); + absolute_send_time_values = + DecodeDeltas(proto.absolute_send_time_deltas(), base_absolute_send_time, + number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(absolute_send_time_values.size(), + number_of_deltas); + } + + // video_rotation (RTP extension) + std::vector<absl::optional<uint64_t>> video_rotation_values; + { + const absl::optional<uint64_t> base_video_rotation = + proto.has_video_rotation() ? proto.video_rotation() + : absl::optional<uint64_t>(); + video_rotation_values = DecodeDeltas(proto.video_rotation_deltas(), + base_video_rotation, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(video_rotation_values.size(), + number_of_deltas); + } + + // audio_level (RTP extension) + std::vector<absl::optional<uint64_t>> audio_level_values; + { + const absl::optional<uint64_t> base_audio_level = + proto.has_audio_level() ? proto.audio_level() + : absl::optional<uint64_t>(); + audio_level_values = DecodeDeltas(proto.audio_level_deltas(), + base_audio_level, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(audio_level_values.size(), number_of_deltas); + } + + // voice_activity (RTP extension) + std::vector<absl::optional<uint64_t>> voice_activity_values; + { + const absl::optional<uint64_t> base_voice_activity = + proto.has_voice_activity() ? proto.voice_activity() + : absl::optional<uint64_t>(); + voice_activity_values = DecodeDeltas(proto.voice_activity_deltas(), + base_voice_activity, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(voice_activity_values.size(), + number_of_deltas); + } + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(marker_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(payload_type_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(sequence_number_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(rtp_timestamp_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(ssrc_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(payload_size_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(header_size_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(padding_size_values[i].has_value()); + + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + RTPHeader header; + header.markerBit = rtc::checked_cast<bool>(*marker_values[i]); + header.payloadType = rtc::checked_cast<uint8_t>(*payload_type_values[i]); + header.sequenceNumber = + rtc::checked_cast<uint16_t>(*sequence_number_values[i]); + header.timestamp = rtc::checked_cast<uint32_t>(*rtp_timestamp_values[i]); + header.ssrc = rtc::checked_cast<uint32_t>(*ssrc_values[i]); + header.numCSRCs = 0; // TODO(terelius): Implement CSRC. + header.paddingLength = rtc::checked_cast<size_t>(*padding_size_values[i]); + header.headerLength = rtc::checked_cast<size_t>(*header_size_values[i]); + // TODO(terelius): Should we implement payload_type_frequency? + if (transport_sequence_number_values.size() > i && + transport_sequence_number_values[i].has_value()) { + header.extension.hasTransportSequenceNumber = true; + header.extension.transportSequenceNumber = rtc::checked_cast<uint16_t>( + transport_sequence_number_values[i].value()); + } + if (transmission_time_offset_values.size() > i && + transmission_time_offset_values[i].has_value()) { + header.extension.hasTransmissionTimeOffset = true; + int32_t transmission_time_offset; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(transmission_time_offset_values[i].value(), + &transmission_time_offset)); + header.extension.transmissionTimeOffset = transmission_time_offset; + } + if (absolute_send_time_values.size() > i && + absolute_send_time_values[i].has_value()) { + header.extension.hasAbsoluteSendTime = true; + header.extension.absoluteSendTime = + rtc::checked_cast<uint32_t>(absolute_send_time_values[i].value()); + } + if (video_rotation_values.size() > i && + video_rotation_values[i].has_value()) { + header.extension.hasVideoRotation = true; + header.extension.videoRotation = ConvertCVOByteToVideoRotation( + rtc::checked_cast<uint8_t>(video_rotation_values[i].value())); + } + if (audio_level_values.size() > i && audio_level_values[i].has_value()) { + RTC_PARSE_CHECK_OR_RETURN(voice_activity_values.size() > i && + voice_activity_values[i].has_value()); + header.extension.hasAudioLevel = true; + header.extension.voiceActivity = + rtc::checked_cast<bool>(voice_activity_values[i].value()); + const uint8_t audio_level = + rtc::checked_cast<uint8_t>(audio_level_values[i].value()); + RTC_PARSE_CHECK_OR_RETURN_LE(audio_level, 0x7Fu); + header.extension.audioLevel = audio_level; + } else { + RTC_PARSE_CHECK_OR_RETURN(voice_activity_values.size() <= i || + !voice_activity_values[i].has_value()); + } + LoggedType logged_packet(Timestamp::Millis(timestamp_ms), header, + header.headerLength, + payload_size_values[i].value() + + header.headerLength + header.paddingLength); + if (!dependency_descriptor_wire_format[i + 1].empty()) { + logged_packet.rtp.dependency_descriptor_wire_format = + dependency_descriptor_wire_format[i + 1]; + } + (*rtp_packets_map)[header.ssrc].push_back(std::move(logged_packet)); + } + return ParsedRtcEventLog::ParseStatus::Success(); +} + +template <typename ProtoType, typename LoggedType> +ParsedRtcEventLog::ParseStatus StoreRtcpPackets( + const ProtoType& proto, + std::vector<LoggedType>* rtcp_packets, + bool remove_duplicates) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_raw_packet()); + + // TODO(terelius): Incoming RTCP may be delivered once for audio and once + // for video. As a work around, we remove the duplicated packets since they + // cause problems when analyzing the log or feeding it into the transport + // feedback adapter. + if (!remove_duplicates || rtcp_packets->empty() || + !IdenticalRtcpContents(rtcp_packets->back().rtcp.raw_data, + proto.raw_packet())) { + // Base event + rtcp_packets->emplace_back(Timestamp::Millis(proto.timestamp_ms()), + proto.raw_packet()); + } + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParsedRtcEventLog::ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // raw_packet + RTC_PARSE_CHECK_OR_RETURN(proto.has_raw_packet_blobs()); + std::vector<absl::string_view> raw_packet_values = + DecodeBlobs(proto.raw_packet_blobs(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(raw_packet_values.size(), number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + // TODO(terelius): Incoming RTCP may be delivered once for audio and once + // for video. As a work around, we remove the duplicated packets since they + // cause problems when analyzing the log or feeding it into the transport + // feedback adapter. + if (remove_duplicates && !rtcp_packets->empty() && + IdenticalRtcpContents(rtcp_packets->back().rtcp.raw_data, + raw_packet_values[i])) { + continue; + } + std::string data(raw_packet_values[i]); + rtcp_packets->emplace_back(Timestamp::Millis(timestamp_ms), data); + } + return ParsedRtcEventLog::ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus StoreRtcpBlocks( + int64_t timestamp_us, + const uint8_t* packet_begin, + const uint8_t* packet_end, + std::vector<LoggedRtcpPacketSenderReport>* sr_list, + std::vector<LoggedRtcpPacketReceiverReport>* rr_list, + std::vector<LoggedRtcpPacketExtendedReports>* xr_list, + std::vector<LoggedRtcpPacketRemb>* remb_list, + std::vector<LoggedRtcpPacketNack>* nack_list, + std::vector<LoggedRtcpPacketFir>* fir_list, + std::vector<LoggedRtcpPacketPli>* pli_list, + std::vector<LoggedRtcpPacketBye>* bye_list, + std::vector<LoggedRtcpPacketTransportFeedback>* transport_feedback_list, + std::vector<LoggedRtcpPacketLossNotification>* loss_notification_list) { + Timestamp timestamp = Timestamp::Micros(timestamp_us); + rtcp::CommonHeader header; + for (const uint8_t* block = packet_begin; block < packet_end; + block = header.NextPacket()) { + RTC_PARSE_CHECK_OR_RETURN(header.Parse(block, packet_end - block)); + if (header.type() == rtcp::TransportFeedback::kPacketType && + header.fmt() == rtcp::TransportFeedback::kFeedbackMessageType) { + LoggedRtcpPacketTransportFeedback parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.transport_feedback.Parse(header)); + transport_feedback_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::SenderReport::kPacketType) { + LoggedRtcpPacketSenderReport parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.sr.Parse(header)); + sr_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::ReceiverReport::kPacketType) { + LoggedRtcpPacketReceiverReport parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.rr.Parse(header)); + rr_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::ExtendedReports::kPacketType) { + LoggedRtcpPacketExtendedReports parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.xr.Parse(header)); + xr_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::Fir::kPacketType && + header.fmt() == rtcp::Fir::kFeedbackMessageType) { + LoggedRtcpPacketFir parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.fir.Parse(header)); + fir_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::Pli::kPacketType && + header.fmt() == rtcp::Pli::kFeedbackMessageType) { + LoggedRtcpPacketPli parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.pli.Parse(header)); + pli_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::Bye::kPacketType) { + LoggedRtcpPacketBye parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.bye.Parse(header)); + bye_list->push_back(std::move(parsed_block)); + } else if (header.type() == rtcp::Psfb::kPacketType && + header.fmt() == rtcp::Psfb::kAfbMessageType) { + bool type_found = false; + if (!type_found) { + LoggedRtcpPacketRemb parsed_block; + parsed_block.timestamp = timestamp; + if (parsed_block.remb.Parse(header)) { + remb_list->push_back(std::move(parsed_block)); + type_found = true; + } + } + if (!type_found) { + LoggedRtcpPacketLossNotification parsed_block; + parsed_block.timestamp = timestamp; + if (parsed_block.loss_notification.Parse(header)) { + loss_notification_list->push_back(std::move(parsed_block)); + type_found = true; + } + } + // We ignore other application-layer feedback types. + } else if (header.type() == rtcp::Nack::kPacketType && + header.fmt() == rtcp::Nack::kFeedbackMessageType) { + LoggedRtcpPacketNack parsed_block; + parsed_block.timestamp = timestamp; + RTC_PARSE_CHECK_OR_RETURN(parsed_block.nack.Parse(header)); + nack_list->push_back(std::move(parsed_block)); + } + } + return ParsedRtcEventLog::ParseStatus::Success(); +} + +} // namespace + +// Conversion functions for version 2 of the wire format. +BandwidthUsage GetRuntimeDetectorState( + rtclog2::DelayBasedBweUpdates::DetectorState detector_state) { + switch (detector_state) { + case rtclog2::DelayBasedBweUpdates::BWE_NORMAL: + return BandwidthUsage::kBwNormal; + case rtclog2::DelayBasedBweUpdates::BWE_UNDERUSING: + return BandwidthUsage::kBwUnderusing; + case rtclog2::DelayBasedBweUpdates::BWE_OVERUSING: + return BandwidthUsage::kBwOverusing; + case rtclog2::DelayBasedBweUpdates::BWE_UNKNOWN_STATE: + break; + } + RTC_DCHECK_NOTREACHED(); + return BandwidthUsage::kBwNormal; +} + +ProbeFailureReason GetRuntimeProbeFailureReason( + rtclog2::BweProbeResultFailure::FailureReason failure) { + switch (failure) { + case rtclog2::BweProbeResultFailure::INVALID_SEND_RECEIVE_INTERVAL: + return ProbeFailureReason::kInvalidSendReceiveInterval; + case rtclog2::BweProbeResultFailure::INVALID_SEND_RECEIVE_RATIO: + return ProbeFailureReason::kInvalidSendReceiveRatio; + case rtclog2::BweProbeResultFailure::TIMEOUT: + return ProbeFailureReason::kTimeout; + case rtclog2::BweProbeResultFailure::UNKNOWN: + break; + } + RTC_DCHECK_NOTREACHED(); + return ProbeFailureReason::kTimeout; +} + +DtlsTransportState GetRuntimeDtlsTransportState( + rtclog2::DtlsTransportStateEvent::DtlsTransportState state) { + switch (state) { + case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_NEW: + return DtlsTransportState::kNew; + case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CONNECTING: + return DtlsTransportState::kConnecting; + case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CONNECTED: + return DtlsTransportState::kConnected; + case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CLOSED: + return DtlsTransportState::kClosed; + case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_FAILED: + return DtlsTransportState::kFailed; + case rtclog2::DtlsTransportStateEvent::UNKNOWN_DTLS_TRANSPORT_STATE: + RTC_DCHECK_NOTREACHED(); + return DtlsTransportState::kNumValues; + } + RTC_DCHECK_NOTREACHED(); + return DtlsTransportState::kNumValues; +} + +IceCandidatePairConfigType GetRuntimeIceCandidatePairConfigType( + rtclog2::IceCandidatePairConfig::IceCandidatePairConfigType type) { + switch (type) { + case rtclog2::IceCandidatePairConfig::ADDED: + return IceCandidatePairConfigType::kAdded; + case rtclog2::IceCandidatePairConfig::UPDATED: + return IceCandidatePairConfigType::kUpdated; + case rtclog2::IceCandidatePairConfig::DESTROYED: + return IceCandidatePairConfigType::kDestroyed; + case rtclog2::IceCandidatePairConfig::SELECTED: + return IceCandidatePairConfigType::kSelected; + case rtclog2::IceCandidatePairConfig::UNKNOWN_CONFIG_TYPE: + break; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairConfigType::kAdded; +} + +IceCandidateType GetRuntimeIceCandidateType( + rtclog2::IceCandidatePairConfig::IceCandidateType type) { + switch (type) { + case rtclog2::IceCandidatePairConfig::LOCAL: + return IceCandidateType::kLocal; + case rtclog2::IceCandidatePairConfig::STUN: + return IceCandidateType::kStun; + case rtclog2::IceCandidatePairConfig::PRFLX: + return IceCandidateType::kPrflx; + case rtclog2::IceCandidatePairConfig::RELAY: + return IceCandidateType::kRelay; + case rtclog2::IceCandidatePairConfig::UNKNOWN_CANDIDATE_TYPE: + return IceCandidateType::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidateType::kUnknown; +} + +IceCandidatePairProtocol GetRuntimeIceCandidatePairProtocol( + rtclog2::IceCandidatePairConfig::Protocol protocol) { + switch (protocol) { + case rtclog2::IceCandidatePairConfig::UDP: + return IceCandidatePairProtocol::kUdp; + case rtclog2::IceCandidatePairConfig::TCP: + return IceCandidatePairProtocol::kTcp; + case rtclog2::IceCandidatePairConfig::SSLTCP: + return IceCandidatePairProtocol::kSsltcp; + case rtclog2::IceCandidatePairConfig::TLS: + return IceCandidatePairProtocol::kTls; + case rtclog2::IceCandidatePairConfig::UNKNOWN_PROTOCOL: + return IceCandidatePairProtocol::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairProtocol::kUnknown; +} + +IceCandidatePairAddressFamily GetRuntimeIceCandidatePairAddressFamily( + rtclog2::IceCandidatePairConfig::AddressFamily address_family) { + switch (address_family) { + case rtclog2::IceCandidatePairConfig::IPV4: + return IceCandidatePairAddressFamily::kIpv4; + case rtclog2::IceCandidatePairConfig::IPV6: + return IceCandidatePairAddressFamily::kIpv6; + case rtclog2::IceCandidatePairConfig::UNKNOWN_ADDRESS_FAMILY: + return IceCandidatePairAddressFamily::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairAddressFamily::kUnknown; +} + +IceCandidateNetworkType GetRuntimeIceCandidateNetworkType( + rtclog2::IceCandidatePairConfig::NetworkType network_type) { + switch (network_type) { + case rtclog2::IceCandidatePairConfig::ETHERNET: + return IceCandidateNetworkType::kEthernet; + case rtclog2::IceCandidatePairConfig::LOOPBACK: + return IceCandidateNetworkType::kLoopback; + case rtclog2::IceCandidatePairConfig::WIFI: + return IceCandidateNetworkType::kWifi; + case rtclog2::IceCandidatePairConfig::VPN: + return IceCandidateNetworkType::kVpn; + case rtclog2::IceCandidatePairConfig::CELLULAR: + return IceCandidateNetworkType::kCellular; + case rtclog2::IceCandidatePairConfig::UNKNOWN_NETWORK_TYPE: + return IceCandidateNetworkType::kUnknown; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidateNetworkType::kUnknown; +} + +IceCandidatePairEventType GetRuntimeIceCandidatePairEventType( + rtclog2::IceCandidatePairEvent::IceCandidatePairEventType type) { + switch (type) { + case rtclog2::IceCandidatePairEvent::CHECK_SENT: + return IceCandidatePairEventType::kCheckSent; + case rtclog2::IceCandidatePairEvent::CHECK_RECEIVED: + return IceCandidatePairEventType::kCheckReceived; + case rtclog2::IceCandidatePairEvent::CHECK_RESPONSE_SENT: + return IceCandidatePairEventType::kCheckResponseSent; + case rtclog2::IceCandidatePairEvent::CHECK_RESPONSE_RECEIVED: + return IceCandidatePairEventType::kCheckResponseReceived; + case rtclog2::IceCandidatePairEvent::UNKNOWN_CHECK_TYPE: + break; + } + RTC_DCHECK_NOTREACHED(); + return IceCandidatePairEventType::kCheckSent; +} + +std::vector<RtpExtension> GetRuntimeRtpHeaderExtensionConfig( + const rtclog2::RtpHeaderExtensionConfig& proto_header_extensions) { + std::vector<RtpExtension> rtp_extensions; + if (proto_header_extensions.has_transmission_time_offset_id()) { + rtp_extensions.emplace_back( + RtpExtension::kTimestampOffsetUri, + proto_header_extensions.transmission_time_offset_id()); + } + if (proto_header_extensions.has_absolute_send_time_id()) { + rtp_extensions.emplace_back( + RtpExtension::kAbsSendTimeUri, + proto_header_extensions.absolute_send_time_id()); + } + if (proto_header_extensions.has_transport_sequence_number_id()) { + rtp_extensions.emplace_back( + RtpExtension::kTransportSequenceNumberUri, + proto_header_extensions.transport_sequence_number_id()); + } + if (proto_header_extensions.has_audio_level_id()) { + rtp_extensions.emplace_back(RtpExtension::kAudioLevelUri, + proto_header_extensions.audio_level_id()); + } + if (proto_header_extensions.has_video_rotation_id()) { + rtp_extensions.emplace_back(RtpExtension::kVideoRotationUri, + proto_header_extensions.video_rotation_id()); + } + if (proto_header_extensions.has_dependency_descriptor_id()) { + rtp_extensions.emplace_back( + RtpExtension::kDependencyDescriptorUri, + proto_header_extensions.dependency_descriptor_id()); + } + return rtp_extensions; +} +// End of conversion functions. + +LoggedPacketInfo::LoggedPacketInfo(const LoggedRtpPacket& rtp, + LoggedMediaType media_type, + bool rtx, + Timestamp capture_time) + : ssrc(rtp.header.ssrc), + stream_seq_no(rtp.header.sequenceNumber), + size(static_cast<uint16_t>(rtp.total_length)), + payload_size(static_cast<uint16_t>(rtp.total_length - + rtp.header.paddingLength - + rtp.header.headerLength)), + padding_size(static_cast<uint16_t>(rtp.header.paddingLength)), + payload_type(rtp.header.payloadType), + media_type(media_type), + rtx(rtx), + marker_bit(rtp.header.markerBit), + has_transport_seq_no(rtp.header.extension.hasTransportSequenceNumber), + transport_seq_no(static_cast<uint16_t>( + has_transport_seq_no ? rtp.header.extension.transportSequenceNumber + : 0)), + capture_time(capture_time), + log_packet_time(Timestamp::Micros(rtp.log_time_us())), + reported_send_time(rtp.header.extension.hasAbsoluteSendTime + ? rtp.header.extension.GetAbsoluteSendTimestamp() + : Timestamp::MinusInfinity()) {} + +LoggedPacketInfo::LoggedPacketInfo(const LoggedPacketInfo&) = default; + +LoggedPacketInfo::~LoggedPacketInfo() {} + +ParsedRtcEventLog::~ParsedRtcEventLog() = default; + +ParsedRtcEventLog::LoggedRtpStreamIncoming::LoggedRtpStreamIncoming() = default; +ParsedRtcEventLog::LoggedRtpStreamIncoming::LoggedRtpStreamIncoming( + const LoggedRtpStreamIncoming& rhs) = default; +ParsedRtcEventLog::LoggedRtpStreamIncoming::~LoggedRtpStreamIncoming() = + default; + +ParsedRtcEventLog::LoggedRtpStreamOutgoing::LoggedRtpStreamOutgoing() = default; +ParsedRtcEventLog::LoggedRtpStreamOutgoing::LoggedRtpStreamOutgoing( + const LoggedRtpStreamOutgoing& rhs) = default; +ParsedRtcEventLog::LoggedRtpStreamOutgoing::~LoggedRtpStreamOutgoing() = + default; + +ParsedRtcEventLog::LoggedRtpStreamView::LoggedRtpStreamView( + uint32_t ssrc, + const std::vector<LoggedRtpPacketIncoming>& packets) + : ssrc(ssrc), packet_view() { + for (const LoggedRtpPacketIncoming& packet : packets) { + packet_view.push_back(&(packet.rtp)); + } +} + +ParsedRtcEventLog::LoggedRtpStreamView::LoggedRtpStreamView( + uint32_t ssrc, + const std::vector<LoggedRtpPacketOutgoing>& packets) + : ssrc(ssrc), packet_view() { + for (const LoggedRtpPacketOutgoing& packet : packets) { + packet_view.push_back(&(packet.rtp)); + } +} + +ParsedRtcEventLog::LoggedRtpStreamView::LoggedRtpStreamView( + const LoggedRtpStreamView&) = default; + +// Return default values for header extensions, to use on streams without stored +// mapping data. Currently this only applies to audio streams, since the mapping +// is not stored in the event log. +// TODO(ivoc): Remove this once this mapping is stored in the event log for +// audio streams. Tracking bug: webrtc:6399 +webrtc::RtpHeaderExtensionMap +ParsedRtcEventLog::GetDefaultHeaderExtensionMap() { + // Values from before the default RTP header extension IDs were removed. + constexpr int kAudioLevelDefaultId = 1; + constexpr int kTimestampOffsetDefaultId = 2; + constexpr int kAbsSendTimeDefaultId = 3; + constexpr int kVideoRotationDefaultId = 4; + constexpr int kTransportSequenceNumberDefaultId = 5; + constexpr int kPlayoutDelayDefaultId = 6; + constexpr int kVideoContentTypeDefaultId = 7; + constexpr int kVideoTimingDefaultId = 8; + constexpr int kDependencyDescriptorDefaultId = 9; + + webrtc::RtpHeaderExtensionMap default_map(/*extmap_allow_mixed=*/true); + default_map.Register<AudioLevel>(kAudioLevelDefaultId); + default_map.Register<TransmissionOffset>(kTimestampOffsetDefaultId); + default_map.Register<AbsoluteSendTime>(kAbsSendTimeDefaultId); + default_map.Register<VideoOrientation>(kVideoRotationDefaultId); + default_map.Register<TransportSequenceNumber>( + kTransportSequenceNumberDefaultId); + default_map.Register<PlayoutDelayLimits>(kPlayoutDelayDefaultId); + default_map.Register<VideoContentTypeExtension>(kVideoContentTypeDefaultId); + default_map.Register<VideoTimingExtension>(kVideoTimingDefaultId); + default_map.Register<RtpDependencyDescriptorExtension>( + kDependencyDescriptorDefaultId); + return default_map; +} + +ParsedRtcEventLog::ParsedRtcEventLog( + UnconfiguredHeaderExtensions parse_unconfigured_header_extensions, + bool allow_incomplete_logs) + : parse_unconfigured_header_extensions_( + parse_unconfigured_header_extensions), + allow_incomplete_logs_(allow_incomplete_logs) { + Clear(); +} + +void ParsedRtcEventLog::Clear() { + default_extension_map_ = GetDefaultHeaderExtensionMap(); + + incoming_rtx_ssrcs_.clear(); + incoming_video_ssrcs_.clear(); + incoming_audio_ssrcs_.clear(); + outgoing_rtx_ssrcs_.clear(); + outgoing_video_ssrcs_.clear(); + outgoing_audio_ssrcs_.clear(); + + incoming_rtp_packets_map_.clear(); + outgoing_rtp_packets_map_.clear(); + incoming_rtp_packets_by_ssrc_.clear(); + outgoing_rtp_packets_by_ssrc_.clear(); + incoming_rtp_packet_views_by_ssrc_.clear(); + outgoing_rtp_packet_views_by_ssrc_.clear(); + + incoming_rtcp_packets_.clear(); + outgoing_rtcp_packets_.clear(); + + incoming_rr_.clear(); + outgoing_rr_.clear(); + incoming_sr_.clear(); + outgoing_sr_.clear(); + incoming_nack_.clear(); + outgoing_nack_.clear(); + incoming_remb_.clear(); + outgoing_remb_.clear(); + incoming_transport_feedback_.clear(); + outgoing_transport_feedback_.clear(); + incoming_loss_notification_.clear(); + outgoing_loss_notification_.clear(); + + start_log_events_.clear(); + stop_log_events_.clear(); + audio_playout_events_.clear(); + neteq_set_minimum_delay_events_.clear(); + audio_network_adaptation_events_.clear(); + bwe_probe_cluster_created_events_.clear(); + bwe_probe_failure_events_.clear(); + bwe_probe_success_events_.clear(); + bwe_delay_updates_.clear(); + bwe_loss_updates_.clear(); + dtls_transport_states_.clear(); + dtls_writable_states_.clear(); + decoded_frames_.clear(); + alr_state_events_.clear(); + ice_candidate_pair_configs_.clear(); + ice_candidate_pair_events_.clear(); + audio_recv_configs_.clear(); + audio_send_configs_.clear(); + video_recv_configs_.clear(); + video_send_configs_.clear(); + + last_incoming_rtcp_packet_.clear(); + + first_timestamp_ = Timestamp::PlusInfinity(); + last_timestamp_ = Timestamp::MinusInfinity(); + first_log_segment_ = LogSegment(0, std::numeric_limits<int64_t>::max()); + + incoming_rtp_extensions_maps_.clear(); + outgoing_rtp_extensions_maps_.clear(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseFile( + absl::string_view filename) { + FileWrapper file = FileWrapper::OpenReadOnly(filename); + if (!file.is_open()) { + RTC_LOG(LS_WARNING) << "Could not open file " << filename + << " for reading."; + RTC_PARSE_CHECK_OR_RETURN(file.is_open()); + } + + // Compute file size. + long signed_filesize = file.FileSize(); // NOLINT(runtime/int) + RTC_PARSE_CHECK_OR_RETURN_GE(signed_filesize, 0); + RTC_PARSE_CHECK_OR_RETURN_LE(signed_filesize, kMaxLogSize); + size_t filesize = rtc::checked_cast<size_t>(signed_filesize); + + // Read file into memory. + std::string buffer(filesize, '\0'); + size_t bytes_read = file.Read(&buffer[0], buffer.size()); + if (bytes_read != filesize) { + RTC_LOG(LS_WARNING) << "Failed to read file " << filename; + RTC_PARSE_CHECK_OR_RETURN_EQ(bytes_read, filesize); + } + + return ParseStream(buffer); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseString( + absl::string_view s) { + return ParseStream(s); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStream( + absl::string_view s) { + Clear(); + ParseStatus status = ParseStreamInternal(s); + + // Cache the configured SSRCs. + for (const auto& video_recv_config : video_recv_configs()) { + incoming_video_ssrcs_.insert(video_recv_config.config.remote_ssrc); + incoming_video_ssrcs_.insert(video_recv_config.config.rtx_ssrc); + incoming_rtx_ssrcs_.insert(video_recv_config.config.rtx_ssrc); + } + for (const auto& video_send_config : video_send_configs()) { + outgoing_video_ssrcs_.insert(video_send_config.config.local_ssrc); + outgoing_video_ssrcs_.insert(video_send_config.config.rtx_ssrc); + outgoing_rtx_ssrcs_.insert(video_send_config.config.rtx_ssrc); + } + for (const auto& audio_recv_config : audio_recv_configs()) { + incoming_audio_ssrcs_.insert(audio_recv_config.config.remote_ssrc); + } + for (const auto& audio_send_config : audio_send_configs()) { + outgoing_audio_ssrcs_.insert(audio_send_config.config.local_ssrc); + } + + // ParseStreamInternal stores the RTP packets in a map indexed by SSRC. + // Since we dont need rapid lookup based on SSRC after parsing, we move the + // packets_streams from map to vector. + incoming_rtp_packets_by_ssrc_.reserve(incoming_rtp_packets_map_.size()); + for (auto& kv : incoming_rtp_packets_map_) { + incoming_rtp_packets_by_ssrc_.emplace_back(LoggedRtpStreamIncoming()); + incoming_rtp_packets_by_ssrc_.back().ssrc = kv.first; + incoming_rtp_packets_by_ssrc_.back().incoming_packets = + std::move(kv.second); + } + incoming_rtp_packets_map_.clear(); + outgoing_rtp_packets_by_ssrc_.reserve(outgoing_rtp_packets_map_.size()); + for (auto& kv : outgoing_rtp_packets_map_) { + outgoing_rtp_packets_by_ssrc_.emplace_back(LoggedRtpStreamOutgoing()); + outgoing_rtp_packets_by_ssrc_.back().ssrc = kv.first; + outgoing_rtp_packets_by_ssrc_.back().outgoing_packets = + std::move(kv.second); + } + outgoing_rtp_packets_map_.clear(); + + // Build PacketViews for easier iteration over RTP packets. + for (const auto& stream : incoming_rtp_packets_by_ssrc_) { + incoming_rtp_packet_views_by_ssrc_.emplace_back( + LoggedRtpStreamView(stream.ssrc, stream.incoming_packets)); + } + for (const auto& stream : outgoing_rtp_packets_by_ssrc_) { + outgoing_rtp_packet_views_by_ssrc_.emplace_back( + LoggedRtpStreamView(stream.ssrc, stream.outgoing_packets)); + } + + // Set up convenience wrappers around the most commonly used RTCP types. + for (const auto& incoming : incoming_rtcp_packets_) { + const int64_t timestamp_us = incoming.rtcp.timestamp.us(); + const uint8_t* packet_begin = incoming.rtcp.raw_data.data(); + const uint8_t* packet_end = packet_begin + incoming.rtcp.raw_data.size(); + auto store_rtcp_status = StoreRtcpBlocks( + timestamp_us, packet_begin, packet_end, &incoming_sr_, &incoming_rr_, + &incoming_xr_, &incoming_remb_, &incoming_nack_, &incoming_fir_, + &incoming_pli_, &incoming_bye_, &incoming_transport_feedback_, + &incoming_loss_notification_); + RTC_RETURN_IF_ERROR(store_rtcp_status); + } + + for (const auto& outgoing : outgoing_rtcp_packets_) { + const int64_t timestamp_us = outgoing.rtcp.timestamp.us(); + const uint8_t* packet_begin = outgoing.rtcp.raw_data.data(); + const uint8_t* packet_end = packet_begin + outgoing.rtcp.raw_data.size(); + auto store_rtcp_status = StoreRtcpBlocks( + timestamp_us, packet_begin, packet_end, &outgoing_sr_, &outgoing_rr_, + &outgoing_xr_, &outgoing_remb_, &outgoing_nack_, &outgoing_fir_, + &outgoing_pli_, &outgoing_bye_, &outgoing_transport_feedback_, + &outgoing_loss_notification_); + RTC_RETURN_IF_ERROR(store_rtcp_status); + } + + // Store first and last timestamp events that might happen before the call is + // connected or after the call is disconnected. Typical examples are + // stream configurations and starting/stopping the log. + // TODO(terelius): Figure out if we actually need to find the first and last + // timestamp in the parser. It seems like this could be done by the caller. + first_timestamp_ = Timestamp::PlusInfinity(); + last_timestamp_ = Timestamp::MinusInfinity(); + StoreFirstAndLastTimestamp(alr_state_events()); + StoreFirstAndLastTimestamp(route_change_events()); + for (const auto& audio_stream : audio_playout_events()) { + // Audio playout events are grouped by SSRC. + StoreFirstAndLastTimestamp(audio_stream.second); + } + for (const auto& set_minimum_delay : neteq_set_minimum_delay_events()) { + // NetEq SetMinimumDelay grouped by SSRC. + StoreFirstAndLastTimestamp(set_minimum_delay.second); + } + StoreFirstAndLastTimestamp(audio_network_adaptation_events()); + StoreFirstAndLastTimestamp(bwe_probe_cluster_created_events()); + StoreFirstAndLastTimestamp(bwe_probe_failure_events()); + StoreFirstAndLastTimestamp(bwe_probe_success_events()); + StoreFirstAndLastTimestamp(bwe_delay_updates()); + StoreFirstAndLastTimestamp(bwe_loss_updates()); + for (const auto& frame_stream : decoded_frames()) { + StoreFirstAndLastTimestamp(frame_stream.second); + } + StoreFirstAndLastTimestamp(dtls_transport_states()); + StoreFirstAndLastTimestamp(dtls_writable_states()); + StoreFirstAndLastTimestamp(ice_candidate_pair_configs()); + StoreFirstAndLastTimestamp(ice_candidate_pair_events()); + for (const auto& rtp_stream : incoming_rtp_packets_by_ssrc()) { + StoreFirstAndLastTimestamp(rtp_stream.incoming_packets); + } + for (const auto& rtp_stream : outgoing_rtp_packets_by_ssrc()) { + StoreFirstAndLastTimestamp(rtp_stream.outgoing_packets); + } + StoreFirstAndLastTimestamp(incoming_rtcp_packets()); + StoreFirstAndLastTimestamp(outgoing_rtcp_packets()); + StoreFirstAndLastTimestamp(generic_packets_sent_); + StoreFirstAndLastTimestamp(generic_packets_received_); + StoreFirstAndLastTimestamp(generic_acks_received_); + StoreFirstAndLastTimestamp(remote_estimate_events_); + + // Stop events could be missing due to file size limits. If so, use the + // last event, or the next start timestamp if available. + // TODO(terelius): This could be improved. Instead of using the next start + // event, we could use the timestamp of the the last previous regular event. + auto start_iter = start_log_events().begin(); + auto stop_iter = stop_log_events().begin(); + int64_t start_us = + first_timestamp().us_or(std::numeric_limits<int64_t>::max()); + int64_t next_start_us = std::numeric_limits<int64_t>::max(); + int64_t stop_us = std::numeric_limits<int64_t>::max(); + if (start_iter != start_log_events().end()) { + start_us = std::min(start_us, start_iter->log_time_us()); + ++start_iter; + if (start_iter != start_log_events().end()) + next_start_us = start_iter->log_time_us(); + } + if (stop_iter != stop_log_events().end()) { + stop_us = stop_iter->log_time_us(); + } + stop_us = std::min(stop_us, next_start_us); + if (stop_us == std::numeric_limits<int64_t>::max() && + !last_timestamp().IsMinusInfinity()) { + stop_us = last_timestamp().us(); + } + RTC_PARSE_CHECK_OR_RETURN_LE(start_us, stop_us); + first_log_segment_ = LogSegment(start_us, stop_us); + + if (first_timestamp_ > last_timestamp_) { + first_timestamp_ = last_timestamp_ = Timestamp::Zero(); + } + + return status; +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStreamInternal( + absl::string_view s) { + constexpr uint64_t kMaxEventSize = 10000000; // Sanity check. + // Protobuf defines the message tag as + // (field_number << 3) | wire_type. In the legacy encoding, the field number + // is supposed to be 1 and the wire type for a length-delimited field is 2. + // In the new encoding we still expect the wire type to be 2, but the field + // number will be greater than 1. + constexpr uint64_t kExpectedV1Tag = (1 << 3) | 2; + bool success = false; + + // "Peek" at the first varint. + absl::string_view event_start = s; + uint64_t tag = 0; + std::tie(success, std::ignore) = DecodeVarInt(s, &tag); + if (!success) { + RTC_LOG(LS_WARNING) << "Failed to read varint from beginning of event log."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + return ParseStatus::Error("Failed to read field tag varint", __FILE__, + __LINE__); + } + s = event_start; + + if (tag >> 1 == static_cast<uint64_t>(RtcEvent::Type::BeginV3Log)) { + return ParseStreamInternalV3(s); + } + + while (!s.empty()) { + // If not, "reset" event_start and read the field tag for the next event. + event_start = s; + std::tie(success, s) = DecodeVarInt(s, &tag); + if (!success) { + RTC_LOG(LS_WARNING) + << "Failed to read field tag from beginning of protobuf event."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + return ParseStatus::Error("Failed to read field tag varint", __FILE__, + __LINE__); + } + + constexpr uint64_t kWireTypeMask = 0x07; + const uint64_t wire_type = tag & kWireTypeMask; + if (wire_type != 2) { + RTC_LOG(LS_WARNING) << "Expected field tag with wire type 2 (length " + "delimited message). Found wire type " + << wire_type; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + RTC_PARSE_CHECK_OR_RETURN_EQ(wire_type, 2); + } + + // Read the length field. + uint64_t message_length = 0; + std::tie(success, s) = DecodeVarInt(s, &message_length); + if (!success) { + RTC_LOG(LS_WARNING) << "Missing message length after protobuf field tag."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + return ParseStatus::Error("Failed to read message length varint", + __FILE__, __LINE__); + } + + if (message_length > s.size()) { + RTC_LOG(LS_WARNING) << "Protobuf message length is larger than the " + "remaining bytes in the proto."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + return ParseStatus::Error( + "Incomplete message: the length of the next message is larger than " + "the remaining bytes in the proto", + __FILE__, __LINE__); + } + + RTC_PARSE_CHECK_OR_RETURN_LE(message_length, kMaxEventSize); + // Skip forward to the start of the next event. + s = s.substr(message_length); + size_t total_event_size = event_start.size() - s.size(); + RTC_CHECK_LE(total_event_size, event_start.size()); + + if (tag == kExpectedV1Tag) { + // Parse the protobuf event from the buffer. + rtclog::EventStream event_stream; + if (!event_stream.ParseFromArray(event_start.data(), total_event_size)) { + RTC_LOG(LS_WARNING) + << "Failed to parse legacy-format protobuf message."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + RTC_PARSE_CHECK_OR_RETURN(false); + } + + RTC_PARSE_CHECK_OR_RETURN_EQ(event_stream.stream_size(), 1); + auto status = StoreParsedLegacyEvent(event_stream.stream(0)); + RTC_RETURN_IF_ERROR(status); + } else { + // Parse the protobuf event from the buffer. + rtclog2::EventStream event_stream; + if (!event_stream.ParseFromArray(event_start.data(), total_event_size)) { + RTC_LOG(LS_WARNING) << "Failed to parse new-format protobuf message."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + RTC_PARSE_CHECK_OR_RETURN(false); + } + auto status = StoreParsedNewFormatEvent(event_stream); + RTC_RETURN_IF_ERROR(status); + } + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStreamInternalV3( + absl::string_view s) { + constexpr uint64_t kMaxEventSize = 10000000; // Sanity check. + bool expect_begin_log_event = true; + bool success = false; + + while (!s.empty()) { + // Read event type. + uint64_t event_tag = 0; + std::tie(success, s) = DecodeVarInt(s, &event_tag); + RTC_PARSE_CHECK_OR_RETURN_MESSAGE(success, "Failed to read event type."); + bool batched = event_tag & 1; + uint64_t event_type = event_tag >> 1; + + // Read event size + uint64_t event_size_bytes = 0; + std::tie(success, s) = DecodeVarInt(s, &event_size_bytes); + RTC_PARSE_CHECK_OR_RETURN_MESSAGE(success, "Failed to read event size."); + if (event_size_bytes > kMaxEventSize || event_size_bytes > s.size()) { + RTC_LOG(LS_WARNING) << "Event size is too large."; + RTC_PARSE_CHECK_OR_RETURN_LE(event_size_bytes, kMaxEventSize); + RTC_PARSE_CHECK_OR_RETURN_LE(event_size_bytes, s.size()); + } + + // Read remaining event fields into a buffer. + absl::string_view event_fields = s.substr(0, event_size_bytes); + s = s.substr(event_size_bytes); + + if (expect_begin_log_event) { + RTC_PARSE_CHECK_OR_RETURN_EQ( + event_type, static_cast<uint32_t>(RtcEvent::Type::BeginV3Log)); + expect_begin_log_event = false; + } + + switch (event_type) { + case static_cast<uint32_t>(RtcEvent::Type::BeginV3Log): + RtcEventBeginLog::Parse(event_fields, batched, start_log_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::EndV3Log): + RtcEventEndLog::Parse(event_fields, batched, stop_log_events_); + expect_begin_log_event = true; + break; + case static_cast<uint32_t>(RtcEvent::Type::AlrStateEvent): + RtcEventAlrState::Parse(event_fields, batched, alr_state_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::AudioPlayout): + RtcEventAudioPlayout::Parse(event_fields, batched, + audio_playout_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::BweUpdateDelayBased): + RtcEventBweUpdateDelayBased::Parse(event_fields, batched, + bwe_delay_updates_); + break; + case static_cast<uint32_t>(RtcEvent::Type::AudioNetworkAdaptation): + RtcEventAudioNetworkAdaptation::Parse(event_fields, batched, + audio_network_adaptation_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::AudioReceiveStreamConfig): + RtcEventAudioReceiveStreamConfig::Parse(event_fields, batched, + audio_recv_configs_); + break; + case static_cast<uint32_t>(RtcEvent::Type::AudioSendStreamConfig): + RtcEventAudioSendStreamConfig::Parse(event_fields, batched, + audio_send_configs_); + break; + case static_cast<uint32_t>(RtcEvent::Type::BweUpdateLossBased): + RtcEventBweUpdateLossBased::Parse(event_fields, batched, + bwe_loss_updates_); + break; + case static_cast<uint32_t>(RtcEvent::Type::DtlsTransportState): + RtcEventDtlsTransportState::Parse(event_fields, batched, + dtls_transport_states_); + break; + case static_cast<uint32_t>(RtcEvent::Type::DtlsWritableState): + RtcEventDtlsWritableState::Parse(event_fields, batched, + dtls_writable_states_); + break; + case static_cast<uint32_t>(RtcEvent::Type::FrameDecoded): + RtcEventFrameDecoded::Parse(event_fields, batched, decoded_frames_); + break; + case static_cast<uint32_t>(RtcEvent::Type::GenericAckReceived): + RtcEventGenericAckReceived::Parse(event_fields, batched, + generic_acks_received_); + break; + case static_cast<uint32_t>(RtcEvent::Type::GenericPacketReceived): + RtcEventGenericPacketReceived::Parse(event_fields, batched, + generic_packets_received_); + break; + case static_cast<uint32_t>(RtcEvent::Type::GenericPacketSent): + RtcEventGenericPacketSent::Parse(event_fields, batched, + generic_packets_sent_); + break; + case static_cast<uint32_t>(RtcEvent::Type::IceCandidatePairConfig): + RtcEventIceCandidatePairConfig::Parse(event_fields, batched, + ice_candidate_pair_configs_); + break; + case static_cast<uint32_t>(RtcEvent::Type::IceCandidatePairEvent): + RtcEventIceCandidatePair::Parse(event_fields, batched, + ice_candidate_pair_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::ProbeClusterCreated): + RtcEventProbeClusterCreated::Parse(event_fields, batched, + bwe_probe_cluster_created_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::ProbeResultFailure): + RtcEventProbeResultFailure::Parse(event_fields, batched, + bwe_probe_failure_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::ProbeResultSuccess): + RtcEventProbeResultSuccess::Parse(event_fields, batched, + bwe_probe_success_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::RemoteEstimateEvent): + RtcEventRemoteEstimate::Parse(event_fields, batched, + remote_estimate_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::RouteChangeEvent): + RtcEventRouteChange::Parse(event_fields, batched, route_change_events_); + break; + case static_cast<uint32_t>(RtcEvent::Type::RtcpPacketIncoming): + RtcEventRtcpPacketIncoming::Parse(event_fields, batched, + incoming_rtcp_packets_); + break; + case static_cast<uint32_t>(RtcEvent::Type::RtcpPacketOutgoing): + RtcEventRtcpPacketOutgoing::Parse(event_fields, batched, + outgoing_rtcp_packets_); + break; + case static_cast<uint32_t>(RtcEvent::Type::RtpPacketIncoming): + RtcEventRtpPacketIncoming::Parse(event_fields, batched, + incoming_rtp_packets_map_); + break; + case static_cast<uint32_t>(RtcEvent::Type::RtpPacketOutgoing): + RtcEventRtpPacketOutgoing::Parse(event_fields, batched, + outgoing_rtp_packets_map_); + break; + case static_cast<uint32_t>(RtcEvent::Type::VideoReceiveStreamConfig): + RtcEventVideoReceiveStreamConfig::Parse(event_fields, batched, + video_recv_configs_); + break; + case static_cast<uint32_t>(RtcEvent::Type::VideoSendStreamConfig): + RtcEventVideoSendStreamConfig::Parse(event_fields, batched, + video_send_configs_); + break; + } + } + + return ParseStatus::Success(); +} + +template <typename T> +void ParsedRtcEventLog::StoreFirstAndLastTimestamp(const std::vector<T>& v) { + if (v.empty()) + return; + first_timestamp_ = std::min(first_timestamp_, v.front().log_time()); + last_timestamp_ = std::max(last_timestamp_, v.back().log_time()); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreParsedLegacyEvent( + const rtclog::Event& event) { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + switch (event.type()) { + case rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT: { + auto config = GetVideoReceiveConfig(event); + if (!config.ok()) + return config.status(); + + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + video_recv_configs_.emplace_back(Timestamp::Micros(timestamp_us), + config.value()); + incoming_rtp_extensions_maps_[config.value().remote_ssrc] = + RtpHeaderExtensionMap(config.value().rtp_extensions); + incoming_rtp_extensions_maps_[config.value().rtx_ssrc] = + RtpHeaderExtensionMap(config.value().rtp_extensions); + break; + } + case rtclog::Event::VIDEO_SENDER_CONFIG_EVENT: { + auto config = GetVideoSendConfig(event); + if (!config.ok()) + return config.status(); + + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + video_send_configs_.emplace_back(Timestamp::Micros(timestamp_us), + config.value()); + outgoing_rtp_extensions_maps_[config.value().local_ssrc] = + RtpHeaderExtensionMap(config.value().rtp_extensions); + outgoing_rtp_extensions_maps_[config.value().rtx_ssrc] = + RtpHeaderExtensionMap(config.value().rtp_extensions); + break; + } + case rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT: { + auto config = GetAudioReceiveConfig(event); + if (!config.ok()) + return config.status(); + + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + audio_recv_configs_.emplace_back(Timestamp::Micros(timestamp_us), + config.value()); + incoming_rtp_extensions_maps_[config.value().remote_ssrc] = + RtpHeaderExtensionMap(config.value().rtp_extensions); + break; + } + case rtclog::Event::AUDIO_SENDER_CONFIG_EVENT: { + auto config = GetAudioSendConfig(event); + if (!config.ok()) + return config.status(); + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + audio_send_configs_.emplace_back(Timestamp::Micros(timestamp_us), + config.value()); + outgoing_rtp_extensions_maps_[config.value().local_ssrc] = + RtpHeaderExtensionMap(config.value().rtp_extensions); + break; + } + case rtclog::Event::RTP_EVENT: { + RTC_PARSE_CHECK_OR_RETURN(event.has_rtp_packet()); + const rtclog::RtpPacket& rtp_packet = event.rtp_packet(); + RTC_PARSE_CHECK_OR_RETURN(rtp_packet.has_header()); + RTC_PARSE_CHECK_OR_RETURN(rtp_packet.has_incoming()); + RTC_PARSE_CHECK_OR_RETURN(rtp_packet.has_packet_length()); + size_t total_length = rtp_packet.packet_length(); + + // Use RtpPacketReceived instead of more generic RtpPacket because former + // has a buildin convertion to RTPHeader. + RtpPacketReceived rtp_header; + RTC_PARSE_CHECK_OR_RETURN( + rtp_header.Parse(rtc::CopyOnWriteBuffer(rtp_packet.header()))); + + if (const RtpHeaderExtensionMap* extension_map = GetRtpHeaderExtensionMap( + rtp_packet.incoming(), rtp_header.Ssrc())) { + rtp_header.IdentifyExtensions(*extension_map); + } + + RTPHeader parsed_header; + rtp_header.GetHeader(&parsed_header); + + // Since we give the parser only a header, there is no way for it to know + // the padding length. The best solution would be to log the padding + // length in RTC event log. In absence of it, we assume the RTP packet to + // contain only padding, if the padding bit is set. + // TODO(webrtc:9730): Use a generic way to obtain padding length. + if (rtp_header.has_padding()) + parsed_header.paddingLength = total_length - rtp_header.size(); + + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + if (rtp_packet.incoming()) { + incoming_rtp_packets_map_[parsed_header.ssrc].push_back( + LoggedRtpPacketIncoming(Timestamp::Micros(timestamp_us), + parsed_header, rtp_header.size(), + total_length)); + } else { + outgoing_rtp_packets_map_[parsed_header.ssrc].push_back( + LoggedRtpPacketOutgoing(Timestamp::Micros(timestamp_us), + parsed_header, rtp_header.size(), + total_length)); + } + break; + } + case rtclog::Event::RTCP_EVENT: { + PacketDirection direction; + std::vector<uint8_t> packet; + auto status = GetRtcpPacket(event, &direction, &packet); + RTC_RETURN_IF_ERROR(status); + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + if (direction == kIncomingPacket) { + // Currently incoming RTCP packets are logged twice, both for audio and + // video. Only act on one of them. Compare against the previous parsed + // incoming RTCP packet. + if (packet == last_incoming_rtcp_packet_) + break; + incoming_rtcp_packets_.push_back( + LoggedRtcpPacketIncoming(Timestamp::Micros(timestamp_us), packet)); + last_incoming_rtcp_packet_ = packet; + } else { + outgoing_rtcp_packets_.push_back( + LoggedRtcpPacketOutgoing(Timestamp::Micros(timestamp_us), packet)); + } + break; + } + case rtclog::Event::LOG_START: { + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + start_log_events_.push_back( + LoggedStartEvent(Timestamp::Micros(timestamp_us))); + break; + } + case rtclog::Event::LOG_END: { + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + int64_t timestamp_us = event.timestamp_us(); + stop_log_events_.push_back( + LoggedStopEvent(Timestamp::Micros(timestamp_us))); + break; + } + case rtclog::Event::AUDIO_PLAYOUT_EVENT: { + auto status_or_value = GetAudioPlayout(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + LoggedAudioPlayoutEvent playout_event = status_or_value.value(); + audio_playout_events_[playout_event.ssrc].push_back(playout_event); + break; + } + case rtclog::Event::LOSS_BASED_BWE_UPDATE: { + auto status_or_value = GetLossBasedBweUpdate(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + bwe_loss_updates_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::DELAY_BASED_BWE_UPDATE: { + auto status_or_value = GetDelayBasedBweUpdate(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + bwe_delay_updates_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT: { + auto status_or_value = GetAudioNetworkAdaptation(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + LoggedAudioNetworkAdaptationEvent ana_event = status_or_value.value(); + audio_network_adaptation_events_.push_back(ana_event); + break; + } + case rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT: { + auto status_or_value = GetBweProbeClusterCreated(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + bwe_probe_cluster_created_events_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::BWE_PROBE_RESULT_EVENT: { + // Probe successes and failures are currently stored in the same proto + // message, we are moving towards separate messages. Probe results + // therefore need special treatment in the parser. + RTC_PARSE_CHECK_OR_RETURN(event.has_probe_result()); + RTC_PARSE_CHECK_OR_RETURN(event.probe_result().has_result()); + if (event.probe_result().result() == rtclog::BweProbeResult::SUCCESS) { + auto status_or_value = GetBweProbeSuccess(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + bwe_probe_success_events_.push_back(status_or_value.value()); + } else { + auto status_or_value = GetBweProbeFailure(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + bwe_probe_failure_events_.push_back(status_or_value.value()); + } + break; + } + case rtclog::Event::ALR_STATE_EVENT: { + auto status_or_value = GetAlrState(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + alr_state_events_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::ICE_CANDIDATE_PAIR_CONFIG: { + auto status_or_value = GetIceCandidatePairConfig(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + ice_candidate_pair_configs_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::ICE_CANDIDATE_PAIR_EVENT: { + auto status_or_value = GetIceCandidatePairEvent(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + ice_candidate_pair_events_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::REMOTE_ESTIMATE: { + auto status_or_value = GetRemoteEstimateEvent(event); + RTC_RETURN_IF_ERROR(status_or_value.status()); + remote_estimate_events_.push_back(status_or_value.value()); + break; + } + case rtclog::Event::UNKNOWN_EVENT: { + break; + } + } + return ParseStatus::Success(); +} + +const RtpHeaderExtensionMap* ParsedRtcEventLog::GetRtpHeaderExtensionMap( + bool incoming, + uint32_t ssrc) { + auto& extensions_maps = + incoming ? incoming_rtp_extensions_maps_ : outgoing_rtp_extensions_maps_; + auto it = extensions_maps.find(ssrc); + if (it != extensions_maps.end()) { + return &(it->second); + } + if (parse_unconfigured_header_extensions_ == + UnconfiguredHeaderExtensions::kAttemptWebrtcDefaultConfig) { + RTC_DLOG(LS_WARNING) << "Using default header extension map for SSRC " + << ssrc; + extensions_maps.insert(std::make_pair(ssrc, default_extension_map_)); + return &default_extension_map_; + } + RTC_DLOG(LS_WARNING) << "Not parsing header extensions for SSRC " << ssrc + << ". No header extension map found."; + return nullptr; +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::GetRtcpPacket( + const rtclog::Event& event, + PacketDirection* incoming, + std::vector<uint8_t>* packet) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), rtclog::Event::RTCP_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_rtcp_packet()); + const rtclog::RtcpPacket& rtcp_packet = event.rtcp_packet(); + // Get direction of packet. + RTC_PARSE_CHECK_OR_RETURN(rtcp_packet.has_incoming()); + if (incoming != nullptr) { + *incoming = rtcp_packet.incoming() ? kIncomingPacket : kOutgoingPacket; + } + // Get packet contents. + RTC_PARSE_CHECK_OR_RETURN(rtcp_packet.has_packet_data()); + if (packet != nullptr) { + packet->resize(rtcp_packet.packet_data().size()); + memcpy(packet->data(), rtcp_packet.packet_data().data(), + rtcp_packet.packet_data().size()); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatusOr<rtclog::StreamConfig> +ParsedRtcEventLog::GetVideoReceiveConfig(const rtclog::Event& event) const { + rtclog::StreamConfig config; + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_video_receiver_config()); + const rtclog::VideoReceiveConfig& receiver_config = + event.video_receiver_config(); + // Get SSRCs. + RTC_PARSE_CHECK_OR_RETURN(receiver_config.has_remote_ssrc()); + config.remote_ssrc = receiver_config.remote_ssrc(); + RTC_PARSE_CHECK_OR_RETURN(receiver_config.has_local_ssrc()); + config.local_ssrc = receiver_config.local_ssrc(); + config.rtx_ssrc = 0; + // Get RTCP settings. + RTC_PARSE_CHECK_OR_RETURN(receiver_config.has_rtcp_mode()); + config.rtcp_mode = GetRuntimeRtcpMode(receiver_config.rtcp_mode()); + RTC_PARSE_CHECK_OR_RETURN(receiver_config.has_remb()); + config.remb = receiver_config.remb(); + + // Get RTX map. + std::map<uint32_t, const rtclog::RtxConfig> rtx_map; + for (int i = 0; i < receiver_config.rtx_map_size(); i++) { + const rtclog::RtxMap& map = receiver_config.rtx_map(i); + RTC_PARSE_CHECK_OR_RETURN(map.has_payload_type()); + RTC_PARSE_CHECK_OR_RETURN(map.has_config()); + RTC_PARSE_CHECK_OR_RETURN(map.config().has_rtx_ssrc()); + RTC_PARSE_CHECK_OR_RETURN(map.config().has_rtx_payload_type()); + rtx_map.insert(std::make_pair(map.payload_type(), map.config())); + } + + // Get header extensions. + auto status = GetHeaderExtensions(&config.rtp_extensions, + receiver_config.header_extensions()); + RTC_RETURN_IF_ERROR(status); + + // Get decoders. + config.codecs.clear(); + for (int i = 0; i < receiver_config.decoders_size(); i++) { + RTC_PARSE_CHECK_OR_RETURN(receiver_config.decoders(i).has_name()); + RTC_PARSE_CHECK_OR_RETURN(receiver_config.decoders(i).has_payload_type()); + int rtx_payload_type = 0; + auto rtx_it = rtx_map.find(receiver_config.decoders(i).payload_type()); + if (rtx_it != rtx_map.end()) { + rtx_payload_type = rtx_it->second.rtx_payload_type(); + if (config.rtx_ssrc != 0 && + config.rtx_ssrc != rtx_it->second.rtx_ssrc()) { + RTC_LOG(LS_WARNING) + << "RtcEventLog protobuf contained different SSRCs for " + "different received RTX payload types. Will only use " + "rtx_ssrc = " + << config.rtx_ssrc << "."; + } else { + config.rtx_ssrc = rtx_it->second.rtx_ssrc(); + } + } + config.codecs.emplace_back(receiver_config.decoders(i).name(), + receiver_config.decoders(i).payload_type(), + rtx_payload_type); + } + return config; +} + +ParsedRtcEventLog::ParseStatusOr<rtclog::StreamConfig> +ParsedRtcEventLog::GetVideoSendConfig(const rtclog::Event& event) const { + rtclog::StreamConfig config; + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::VIDEO_SENDER_CONFIG_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_video_sender_config()); + const rtclog::VideoSendConfig& sender_config = event.video_sender_config(); + + // Get SSRCs. + // VideoSendStreamConfig no longer stores multiple SSRCs. If you are + // analyzing a very old log, try building the parser from the same + // WebRTC version. + RTC_PARSE_CHECK_OR_RETURN_EQ(sender_config.ssrcs_size(), 1); + config.local_ssrc = sender_config.ssrcs(0); + RTC_PARSE_CHECK_OR_RETURN_LE(sender_config.rtx_ssrcs_size(), 1); + if (sender_config.rtx_ssrcs_size() == 1) { + config.rtx_ssrc = sender_config.rtx_ssrcs(0); + } + + // Get header extensions. + auto status = GetHeaderExtensions(&config.rtp_extensions, + sender_config.header_extensions()); + RTC_RETURN_IF_ERROR(status); + + // Get the codec. + RTC_PARSE_CHECK_OR_RETURN(sender_config.has_encoder()); + RTC_PARSE_CHECK_OR_RETURN(sender_config.encoder().has_name()); + RTC_PARSE_CHECK_OR_RETURN(sender_config.encoder().has_payload_type()); + config.codecs.emplace_back( + sender_config.encoder().name(), sender_config.encoder().payload_type(), + sender_config.has_rtx_payload_type() ? sender_config.rtx_payload_type() + : 0); + return config; +} + +ParsedRtcEventLog::ParseStatusOr<rtclog::StreamConfig> +ParsedRtcEventLog::GetAudioReceiveConfig(const rtclog::Event& event) const { + rtclog::StreamConfig config; + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_audio_receiver_config()); + const rtclog::AudioReceiveConfig& receiver_config = + event.audio_receiver_config(); + // Get SSRCs. + RTC_PARSE_CHECK_OR_RETURN(receiver_config.has_remote_ssrc()); + config.remote_ssrc = receiver_config.remote_ssrc(); + RTC_PARSE_CHECK_OR_RETURN(receiver_config.has_local_ssrc()); + config.local_ssrc = receiver_config.local_ssrc(); + // Get header extensions. + auto status = GetHeaderExtensions(&config.rtp_extensions, + receiver_config.header_extensions()); + RTC_RETURN_IF_ERROR(status); + + return config; +} + +ParsedRtcEventLog::ParseStatusOr<rtclog::StreamConfig> +ParsedRtcEventLog::GetAudioSendConfig(const rtclog::Event& event) const { + rtclog::StreamConfig config; + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::AUDIO_SENDER_CONFIG_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_audio_sender_config()); + const rtclog::AudioSendConfig& sender_config = event.audio_sender_config(); + // Get SSRCs. + RTC_PARSE_CHECK_OR_RETURN(sender_config.has_ssrc()); + config.local_ssrc = sender_config.ssrc(); + // Get header extensions. + auto status = GetHeaderExtensions(&config.rtp_extensions, + sender_config.header_extensions()); + RTC_RETURN_IF_ERROR(status); + + return config; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedAudioPlayoutEvent> +ParsedRtcEventLog::GetAudioPlayout(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::AUDIO_PLAYOUT_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_audio_playout_event()); + const rtclog::AudioPlayoutEvent& playout_event = event.audio_playout_event(); + LoggedAudioPlayoutEvent res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(playout_event.has_local_ssrc()); + res.ssrc = playout_event.local_ssrc(); + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedBweLossBasedUpdate> +ParsedRtcEventLog::GetLossBasedBweUpdate(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::LOSS_BASED_BWE_UPDATE); + RTC_PARSE_CHECK_OR_RETURN(event.has_loss_based_bwe_update()); + const rtclog::LossBasedBweUpdate& loss_event = event.loss_based_bwe_update(); + + LoggedBweLossBasedUpdate bwe_update; + RTC_CHECK(event.has_timestamp_us()); + bwe_update.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(loss_event.has_bitrate_bps()); + bwe_update.bitrate_bps = loss_event.bitrate_bps(); + RTC_PARSE_CHECK_OR_RETURN(loss_event.has_fraction_loss()); + bwe_update.fraction_lost = loss_event.fraction_loss(); + RTC_PARSE_CHECK_OR_RETURN(loss_event.has_total_packets()); + bwe_update.expected_packets = loss_event.total_packets(); + return bwe_update; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedBweDelayBasedUpdate> +ParsedRtcEventLog::GetDelayBasedBweUpdate(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::DELAY_BASED_BWE_UPDATE); + RTC_PARSE_CHECK_OR_RETURN(event.has_delay_based_bwe_update()); + const rtclog::DelayBasedBweUpdate& delay_event = + event.delay_based_bwe_update(); + + LoggedBweDelayBasedUpdate res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(delay_event.has_bitrate_bps()); + res.bitrate_bps = delay_event.bitrate_bps(); + RTC_PARSE_CHECK_OR_RETURN(delay_event.has_detector_state()); + res.detector_state = GetRuntimeDetectorState(delay_event.detector_state()); + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedAudioNetworkAdaptationEvent> +ParsedRtcEventLog::GetAudioNetworkAdaptation(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_audio_network_adaptation()); + const rtclog::AudioNetworkAdaptation& ana_event = + event.audio_network_adaptation(); + + LoggedAudioNetworkAdaptationEvent res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + if (ana_event.has_bitrate_bps()) + res.config.bitrate_bps = ana_event.bitrate_bps(); + if (ana_event.has_enable_fec()) + res.config.enable_fec = ana_event.enable_fec(); + if (ana_event.has_enable_dtx()) + res.config.enable_dtx = ana_event.enable_dtx(); + if (ana_event.has_frame_length_ms()) + res.config.frame_length_ms = ana_event.frame_length_ms(); + if (ana_event.has_num_channels()) + res.config.num_channels = ana_event.num_channels(); + if (ana_event.has_uplink_packet_loss_fraction()) + res.config.uplink_packet_loss_fraction = + ana_event.uplink_packet_loss_fraction(); + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedBweProbeClusterCreatedEvent> +ParsedRtcEventLog::GetBweProbeClusterCreated(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_probe_cluster()); + const rtclog::BweProbeCluster& pcc_event = event.probe_cluster(); + LoggedBweProbeClusterCreatedEvent res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(pcc_event.has_id()); + res.id = pcc_event.id(); + RTC_PARSE_CHECK_OR_RETURN(pcc_event.has_bitrate_bps()); + res.bitrate_bps = pcc_event.bitrate_bps(); + RTC_PARSE_CHECK_OR_RETURN(pcc_event.has_min_packets()); + res.min_packets = pcc_event.min_packets(); + RTC_PARSE_CHECK_OR_RETURN(pcc_event.has_min_bytes()); + res.min_bytes = pcc_event.min_bytes(); + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedBweProbeFailureEvent> +ParsedRtcEventLog::GetBweProbeFailure(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::BWE_PROBE_RESULT_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_probe_result()); + const rtclog::BweProbeResult& pr_event = event.probe_result(); + RTC_PARSE_CHECK_OR_RETURN(pr_event.has_result()); + RTC_PARSE_CHECK_OR_RETURN_NE(pr_event.result(), + rtclog::BweProbeResult::SUCCESS); + + LoggedBweProbeFailureEvent res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(pr_event.has_id()); + res.id = pr_event.id(); + RTC_PARSE_CHECK_OR_RETURN(pr_event.has_result()); + if (pr_event.result() == + rtclog::BweProbeResult::INVALID_SEND_RECEIVE_INTERVAL) { + res.failure_reason = ProbeFailureReason::kInvalidSendReceiveInterval; + } else if (pr_event.result() == + rtclog::BweProbeResult::INVALID_SEND_RECEIVE_RATIO) { + res.failure_reason = ProbeFailureReason::kInvalidSendReceiveRatio; + } else if (pr_event.result() == rtclog::BweProbeResult::TIMEOUT) { + res.failure_reason = ProbeFailureReason::kTimeout; + } else { + RTC_DCHECK_NOTREACHED(); + } + RTC_PARSE_CHECK_OR_RETURN(!pr_event.has_bitrate_bps()); + + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedBweProbeSuccessEvent> +ParsedRtcEventLog::GetBweProbeSuccess(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), + rtclog::Event::BWE_PROBE_RESULT_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_probe_result()); + const rtclog::BweProbeResult& pr_event = event.probe_result(); + RTC_PARSE_CHECK_OR_RETURN(pr_event.has_result()); + RTC_PARSE_CHECK_OR_RETURN_EQ(pr_event.result(), + rtclog::BweProbeResult::SUCCESS); + + LoggedBweProbeSuccessEvent res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(pr_event.has_id()); + res.id = pr_event.id(); + RTC_PARSE_CHECK_OR_RETURN(pr_event.has_bitrate_bps()); + res.bitrate_bps = pr_event.bitrate_bps(); + + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedAlrStateEvent> +ParsedRtcEventLog::GetAlrState(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), rtclog::Event::ALR_STATE_EVENT); + RTC_PARSE_CHECK_OR_RETURN(event.has_alr_state()); + const rtclog::AlrState& alr_event = event.alr_state(); + LoggedAlrStateEvent res; + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(alr_event.has_in_alr()); + res.in_alr = alr_event.in_alr(); + + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedIceCandidatePairConfig> +ParsedRtcEventLog::GetIceCandidatePairConfig( + const rtclog::Event& rtc_event) const { + RTC_PARSE_CHECK_OR_RETURN(rtc_event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(rtc_event.type(), + rtclog::Event::ICE_CANDIDATE_PAIR_CONFIG); + LoggedIceCandidatePairConfig res; + const rtclog::IceCandidatePairConfig& config = + rtc_event.ice_candidate_pair_config(); + RTC_PARSE_CHECK_OR_RETURN(rtc_event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(rtc_event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(config.has_config_type()); + res.type = GetRuntimeIceCandidatePairConfigType(config.config_type()); + RTC_PARSE_CHECK_OR_RETURN(config.has_candidate_pair_id()); + res.candidate_pair_id = config.candidate_pair_id(); + RTC_PARSE_CHECK_OR_RETURN(config.has_local_candidate_type()); + res.local_candidate_type = + GetRuntimeIceCandidateType(config.local_candidate_type()); + RTC_PARSE_CHECK_OR_RETURN(config.has_local_relay_protocol()); + res.local_relay_protocol = + GetRuntimeIceCandidatePairProtocol(config.local_relay_protocol()); + RTC_PARSE_CHECK_OR_RETURN(config.has_local_network_type()); + res.local_network_type = + GetRuntimeIceCandidateNetworkType(config.local_network_type()); + RTC_PARSE_CHECK_OR_RETURN(config.has_local_address_family()); + res.local_address_family = + GetRuntimeIceCandidatePairAddressFamily(config.local_address_family()); + RTC_PARSE_CHECK_OR_RETURN(config.has_remote_candidate_type()); + res.remote_candidate_type = + GetRuntimeIceCandidateType(config.remote_candidate_type()); + RTC_PARSE_CHECK_OR_RETURN(config.has_remote_address_family()); + res.remote_address_family = + GetRuntimeIceCandidatePairAddressFamily(config.remote_address_family()); + RTC_PARSE_CHECK_OR_RETURN(config.has_candidate_pair_protocol()); + res.candidate_pair_protocol = + GetRuntimeIceCandidatePairProtocol(config.candidate_pair_protocol()); + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedIceCandidatePairEvent> +ParsedRtcEventLog::GetIceCandidatePairEvent( + const rtclog::Event& rtc_event) const { + RTC_PARSE_CHECK_OR_RETURN(rtc_event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(rtc_event.type(), + rtclog::Event::ICE_CANDIDATE_PAIR_EVENT); + LoggedIceCandidatePairEvent res; + const rtclog::IceCandidatePairEvent& event = + rtc_event.ice_candidate_pair_event(); + RTC_PARSE_CHECK_OR_RETURN(rtc_event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(rtc_event.timestamp_us()); + RTC_PARSE_CHECK_OR_RETURN(event.has_event_type()); + res.type = GetRuntimeIceCandidatePairEventType(event.event_type()); + RTC_PARSE_CHECK_OR_RETURN(event.has_candidate_pair_id()); + res.candidate_pair_id = event.candidate_pair_id(); + // transaction_id is not supported by rtclog::Event + res.transaction_id = 0; + return res; +} + +ParsedRtcEventLog::ParseStatusOr<LoggedRemoteEstimateEvent> +ParsedRtcEventLog::GetRemoteEstimateEvent(const rtclog::Event& event) const { + RTC_PARSE_CHECK_OR_RETURN(event.has_type()); + RTC_PARSE_CHECK_OR_RETURN_EQ(event.type(), rtclog::Event::REMOTE_ESTIMATE); + LoggedRemoteEstimateEvent res; + const rtclog::RemoteEstimate& remote_estimate_event = event.remote_estimate(); + RTC_PARSE_CHECK_OR_RETURN(event.has_timestamp_us()); + res.timestamp = Timestamp::Micros(event.timestamp_us()); + if (remote_estimate_event.has_link_capacity_lower_kbps()) + res.link_capacity_lower = DataRate::KilobitsPerSec( + remote_estimate_event.link_capacity_lower_kbps()); + if (remote_estimate_event.has_link_capacity_upper_kbps()) + res.link_capacity_upper = DataRate::KilobitsPerSec( + remote_estimate_event.link_capacity_upper_kbps()); + return res; +} + +// Returns the MediaType for registered SSRCs. Search from the end to use last +// registered types first. +ParsedRtcEventLog::MediaType ParsedRtcEventLog::GetMediaType( + uint32_t ssrc, + PacketDirection direction) const { + if (direction == kIncomingPacket) { + if (std::find(incoming_video_ssrcs_.begin(), incoming_video_ssrcs_.end(), + ssrc) != incoming_video_ssrcs_.end()) { + return MediaType::VIDEO; + } + if (std::find(incoming_audio_ssrcs_.begin(), incoming_audio_ssrcs_.end(), + ssrc) != incoming_audio_ssrcs_.end()) { + return MediaType::AUDIO; + } + } else { + if (std::find(outgoing_video_ssrcs_.begin(), outgoing_video_ssrcs_.end(), + ssrc) != outgoing_video_ssrcs_.end()) { + return MediaType::VIDEO; + } + if (std::find(outgoing_audio_ssrcs_.begin(), outgoing_audio_ssrcs_.end(), + ssrc) != outgoing_audio_ssrcs_.end()) { + return MediaType::AUDIO; + } + } + return MediaType::ANY; +} + +std::vector<InferredRouteChangeEvent> ParsedRtcEventLog::GetRouteChanges() + const { + std::vector<InferredRouteChangeEvent> route_changes; + for (auto& candidate : ice_candidate_pair_configs()) { + if (candidate.type == IceCandidatePairConfigType::kSelected) { + InferredRouteChangeEvent route; + route.route_id = candidate.candidate_pair_id; + route.log_time = Timestamp::Millis(candidate.log_time_ms()); + + route.send_overhead = kUdpOverhead + kSrtpOverhead + kIpv4Overhead; + if (candidate.remote_address_family == + IceCandidatePairAddressFamily::kIpv6) + route.send_overhead += kIpv6Overhead - kIpv4Overhead; + if (candidate.remote_candidate_type != IceCandidateType::kLocal) + route.send_overhead += kStunOverhead; + route.return_overhead = kUdpOverhead + kSrtpOverhead + kIpv4Overhead; + if (candidate.remote_address_family == + IceCandidatePairAddressFamily::kIpv6) + route.return_overhead += kIpv6Overhead - kIpv4Overhead; + if (candidate.remote_candidate_type != IceCandidateType::kLocal) + route.return_overhead += kStunOverhead; + route_changes.push_back(route); + } + } + return route_changes; +} + +std::vector<LoggedPacketInfo> ParsedRtcEventLog::GetPacketInfos( + PacketDirection direction) const { + std::map<uint32_t, MediaStreamInfo> streams; + if (direction == PacketDirection::kIncomingPacket) { + AddRecvStreamInfos(&streams, audio_recv_configs(), LoggedMediaType::kAudio); + AddRecvStreamInfos(&streams, video_recv_configs(), LoggedMediaType::kVideo); + } else if (direction == PacketDirection::kOutgoingPacket) { + AddSendStreamInfos(&streams, audio_send_configs(), LoggedMediaType::kAudio); + AddSendStreamInfos(&streams, video_send_configs(), LoggedMediaType::kVideo); + } + + std::vector<OverheadChangeEvent> overheads = + GetOverheadChangingEvents(GetRouteChanges(), direction); + auto overhead_iter = overheads.begin(); + std::vector<LoggedPacketInfo> packets; + std::map<int64_t, size_t> indices; + uint16_t current_overhead = kDefaultOverhead; + Timestamp last_log_time = Timestamp::Zero(); + RtpSequenceNumberUnwrapper seq_num_unwrapper; + + auto advance_time = [&](Timestamp new_log_time) { + if (overhead_iter != overheads.end() && + new_log_time >= overhead_iter->timestamp) { + current_overhead = overhead_iter->overhead; + ++overhead_iter; + } + // If we have a large time delta, it can be caused by a gap in logging, + // therefore we don't want to match up sequence numbers as we might have had + // a wraparound. + if (new_log_time - last_log_time > TimeDelta::Seconds(30)) { + seq_num_unwrapper.Reset(); + indices.clear(); + } + RTC_DCHECK_GE(new_log_time, last_log_time); + last_log_time = new_log_time; + }; + + auto rtp_handler = [&](const LoggedRtpPacket& rtp) { + advance_time(rtp.log_time()); + MediaStreamInfo* stream = &streams[rtp.header.ssrc]; + Timestamp capture_time = Timestamp::MinusInfinity(); + if (!stream->rtx) { + // RTX copy the timestamp of the retransmitted packets. This means that + // RTX streams don't have a unique clock offset and frequency, so + // the RTP timstamps can't be unwrapped. + + // Add an offset to avoid `capture_ticks` to become negative in the case + // of reordering. + constexpr int64_t kStartingCaptureTimeTicks = 90 * 48 * 10000; + int64_t capture_ticks = + kStartingCaptureTimeTicks + + stream->unwrap_capture_ticks.Unwrap(rtp.header.timestamp); + // TODO(srte): Use logged sample rate when it is added to the format. + capture_time = Timestamp::Seconds( + capture_ticks / + (stream->media_type == LoggedMediaType::kAudio ? 48000.0 : 90000.0)); + } + LoggedPacketInfo logged(rtp, stream->media_type, stream->rtx, capture_time); + logged.overhead = current_overhead; + if (logged.has_transport_seq_no) { + logged.log_feedback_time = Timestamp::PlusInfinity(); + int64_t unwrapped_seq_num = + seq_num_unwrapper.Unwrap(logged.transport_seq_no); + if (indices.find(unwrapped_seq_num) != indices.end()) { + auto prev = packets[indices[unwrapped_seq_num]]; + RTC_LOG(LS_WARNING) + << "Repeated sent packet sequence number: " << unwrapped_seq_num + << " Packet time:" << prev.log_packet_time.seconds() << "s vs " + << logged.log_packet_time.seconds() + << "s at:" << rtp.log_time_ms() / 1000; + } + indices[unwrapped_seq_num] = packets.size(); + } + packets.push_back(logged); + }; + + Timestamp feedback_base_time = Timestamp::MinusInfinity(); + Timestamp last_feedback_base_time = Timestamp::MinusInfinity(); + + auto feedback_handler = + [&](const LoggedRtcpPacketTransportFeedback& logged_rtcp) { + auto log_feedback_time = logged_rtcp.log_time(); + advance_time(log_feedback_time); + const auto& feedback = logged_rtcp.transport_feedback; + // Add timestamp deltas to a local time base selected on first packet + // arrival. This won't be the true time base, but makes it easier to + // manually inspect time stamps. + if (!last_feedback_base_time.IsFinite()) { + feedback_base_time = log_feedback_time; + } else { + feedback_base_time += feedback.GetBaseDelta(last_feedback_base_time); + } + last_feedback_base_time = feedback.BaseTime(); + + std::vector<LoggedPacketInfo*> packet_feedbacks; + packet_feedbacks.reserve(feedback.GetPacketStatusCount()); + std::vector<int64_t> unknown_seq_nums; + feedback.ForAllPackets([&](uint16_t sequence_number, + TimeDelta delta_since_base) { + int64_t unwrapped_seq_num = seq_num_unwrapper.Unwrap(sequence_number); + auto it = indices.find(unwrapped_seq_num); + if (it == indices.end()) { + unknown_seq_nums.push_back(unwrapped_seq_num); + return; + } + LoggedPacketInfo* sent = &packets[it->second]; + if (log_feedback_time - sent->log_packet_time > + TimeDelta::Seconds(60)) { + RTC_LOG(LS_WARNING) + << "Received very late feedback, possibly due to wraparound."; + return; + } + if (delta_since_base.IsFinite()) { + if (sent->reported_recv_time.IsInfinite()) { + sent->reported_recv_time = feedback_base_time + delta_since_base; + sent->log_feedback_time = log_feedback_time; + } + } else { + if (sent->reported_recv_time.IsInfinite() && + sent->log_feedback_time.IsInfinite()) { + sent->reported_recv_time = Timestamp::PlusInfinity(); + sent->log_feedback_time = log_feedback_time; + } + } + packet_feedbacks.push_back(sent); + }); + if (!unknown_seq_nums.empty()) { + RTC_LOG(LS_WARNING) + << "Received feedback for unknown packets: " + << unknown_seq_nums.front() << " - " << unknown_seq_nums.back(); + } + if (packet_feedbacks.empty()) + return; + LoggedPacketInfo* last = packet_feedbacks.back(); + last->last_in_feedback = true; + for (LoggedPacketInfo* fb : packet_feedbacks) { + if (direction == PacketDirection::kOutgoingPacket) { + if (last->reported_recv_time.IsFinite() && + fb->reported_recv_time.IsFinite()) { + fb->feedback_hold_duration = + last->reported_recv_time - fb->reported_recv_time; + } + } else { + fb->feedback_hold_duration = + log_feedback_time - fb->log_packet_time; + } + } + }; + + RtcEventProcessor process; + for (const auto& rtp_packets : rtp_packets_by_ssrc(direction)) { + process.AddEvents(rtp_packets.packet_view, rtp_handler, direction); + } + if (direction == PacketDirection::kOutgoingPacket) { + process.AddEvents(incoming_transport_feedback_, feedback_handler, + PacketDirection::kIncomingPacket); + } else { + process.AddEvents(outgoing_transport_feedback_, feedback_handler, + PacketDirection::kOutgoingPacket); + } + process.ProcessEventsInOrder(); + return packets; +} + +std::vector<LoggedIceCandidatePairConfig> ParsedRtcEventLog::GetIceCandidates() + const { + std::vector<LoggedIceCandidatePairConfig> candidates; + std::set<uint32_t> added; + for (auto& candidate : ice_candidate_pair_configs()) { + if (added.find(candidate.candidate_pair_id) == added.end()) { + candidates.push_back(candidate); + added.insert(candidate.candidate_pair_id); + } + } + return candidates; +} + +std::vector<LoggedIceEvent> ParsedRtcEventLog::GetIceEvents() const { + using CheckType = IceCandidatePairEventType; + using ConfigType = IceCandidatePairConfigType; + using Combined = LoggedIceEventType; + std::map<CheckType, Combined> check_map( + {{CheckType::kCheckSent, Combined::kCheckSent}, + {CheckType::kCheckReceived, Combined::kCheckReceived}, + {CheckType::kCheckResponseSent, Combined::kCheckResponseSent}, + {CheckType::kCheckResponseReceived, Combined::kCheckResponseReceived}}); + std::map<ConfigType, Combined> config_map( + {{ConfigType::kAdded, Combined::kAdded}, + {ConfigType::kUpdated, Combined::kUpdated}, + {ConfigType::kDestroyed, Combined::kDestroyed}, + {ConfigType::kSelected, Combined::kSelected}}); + std::vector<LoggedIceEvent> log_events; + auto handle_check = [&](const LoggedIceCandidatePairEvent& check) { + log_events.push_back(LoggedIceEvent{check.candidate_pair_id, + Timestamp::Millis(check.log_time_ms()), + check_map[check.type]}); + }; + auto handle_config = [&](const LoggedIceCandidatePairConfig& conf) { + log_events.push_back(LoggedIceEvent{conf.candidate_pair_id, + Timestamp::Millis(conf.log_time_ms()), + config_map[conf.type]}); + }; + RtcEventProcessor process; + process.AddEvents(ice_candidate_pair_events(), handle_check); + process.AddEvents(ice_candidate_pair_configs(), handle_config); + process.ProcessEventsInOrder(); + return log_events; +} + +const std::vector<MatchedSendArrivalTimes> GetNetworkTrace( + const ParsedRtcEventLog& parsed_log) { + std::vector<MatchedSendArrivalTimes> rtp_rtcp_matched; + for (auto& packet : + parsed_log.GetPacketInfos(PacketDirection::kOutgoingPacket)) { + if (packet.log_feedback_time.IsFinite()) { + rtp_rtcp_matched.emplace_back(packet.log_feedback_time.ms(), + packet.log_packet_time.ms(), + packet.reported_recv_time.ms_or( + MatchedSendArrivalTimes::kNotReceived), + packet.size); + } + } + return rtp_rtcp_matched; +} + +// Helper functions for new format start here +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreParsedNewFormatEvent( + const rtclog2::EventStream& stream) { + RTC_DCHECK_EQ(stream.stream_size(), 0); // No legacy format event. + + RTC_DCHECK_EQ( + stream.incoming_rtp_packets_size() + stream.outgoing_rtp_packets_size() + + stream.incoming_rtcp_packets_size() + + stream.outgoing_rtcp_packets_size() + + stream.audio_playout_events_size() + stream.begin_log_events_size() + + stream.end_log_events_size() + stream.loss_based_bwe_updates_size() + + stream.delay_based_bwe_updates_size() + + stream.dtls_transport_state_events_size() + + stream.dtls_writable_states_size() + + stream.audio_network_adaptations_size() + + stream.probe_clusters_size() + stream.probe_success_size() + + stream.probe_failure_size() + stream.alr_states_size() + + stream.route_changes_size() + stream.remote_estimates_size() + + stream.ice_candidate_configs_size() + + stream.ice_candidate_events_size() + + stream.audio_recv_stream_configs_size() + + stream.audio_send_stream_configs_size() + + stream.video_recv_stream_configs_size() + + stream.video_send_stream_configs_size() + + stream.generic_packets_sent_size() + + stream.generic_packets_received_size() + + stream.generic_acks_received_size() + + stream.frame_decoded_events_size() + + stream.neteq_set_minimum_delay_size(), + 1u); + + if (stream.incoming_rtp_packets_size() == 1) { + return StoreIncomingRtpPackets(stream.incoming_rtp_packets(0)); + } else if (stream.outgoing_rtp_packets_size() == 1) { + return StoreOutgoingRtpPackets(stream.outgoing_rtp_packets(0)); + } else if (stream.incoming_rtcp_packets_size() == 1) { + return StoreIncomingRtcpPackets(stream.incoming_rtcp_packets(0)); + } else if (stream.outgoing_rtcp_packets_size() == 1) { + return StoreOutgoingRtcpPackets(stream.outgoing_rtcp_packets(0)); + } else if (stream.audio_playout_events_size() == 1) { + return StoreAudioPlayoutEvent(stream.audio_playout_events(0)); + } else if (stream.begin_log_events_size() == 1) { + return StoreStartEvent(stream.begin_log_events(0)); + } else if (stream.end_log_events_size() == 1) { + return StoreStopEvent(stream.end_log_events(0)); + } else if (stream.loss_based_bwe_updates_size() == 1) { + return StoreBweLossBasedUpdate(stream.loss_based_bwe_updates(0)); + } else if (stream.delay_based_bwe_updates_size() == 1) { + return StoreBweDelayBasedUpdate(stream.delay_based_bwe_updates(0)); + } else if (stream.dtls_transport_state_events_size() == 1) { + return StoreDtlsTransportState(stream.dtls_transport_state_events(0)); + } else if (stream.dtls_writable_states_size() == 1) { + return StoreDtlsWritableState(stream.dtls_writable_states(0)); + } else if (stream.audio_network_adaptations_size() == 1) { + return StoreAudioNetworkAdaptationEvent( + stream.audio_network_adaptations(0)); + } else if (stream.probe_clusters_size() == 1) { + return StoreBweProbeClusterCreated(stream.probe_clusters(0)); + } else if (stream.probe_success_size() == 1) { + return StoreBweProbeSuccessEvent(stream.probe_success(0)); + } else if (stream.probe_failure_size() == 1) { + return StoreBweProbeFailureEvent(stream.probe_failure(0)); + } else if (stream.alr_states_size() == 1) { + return StoreAlrStateEvent(stream.alr_states(0)); + } else if (stream.route_changes_size() == 1) { + return StoreRouteChangeEvent(stream.route_changes(0)); + } else if (stream.remote_estimates_size() == 1) { + return StoreRemoteEstimateEvent(stream.remote_estimates(0)); + } else if (stream.ice_candidate_configs_size() == 1) { + return StoreIceCandidatePairConfig(stream.ice_candidate_configs(0)); + } else if (stream.ice_candidate_events_size() == 1) { + return StoreIceCandidateEvent(stream.ice_candidate_events(0)); + } else if (stream.audio_recv_stream_configs_size() == 1) { + return StoreAudioRecvConfig(stream.audio_recv_stream_configs(0)); + } else if (stream.audio_send_stream_configs_size() == 1) { + return StoreAudioSendConfig(stream.audio_send_stream_configs(0)); + } else if (stream.video_recv_stream_configs_size() == 1) { + return StoreVideoRecvConfig(stream.video_recv_stream_configs(0)); + } else if (stream.video_send_stream_configs_size() == 1) { + return StoreVideoSendConfig(stream.video_send_stream_configs(0)); + } else if (stream.generic_packets_received_size() == 1) { + return StoreGenericPacketReceivedEvent(stream.generic_packets_received(0)); + } else if (stream.generic_packets_sent_size() == 1) { + return StoreGenericPacketSentEvent(stream.generic_packets_sent(0)); + } else if (stream.generic_acks_received_size() == 1) { + return StoreGenericAckReceivedEvent(stream.generic_acks_received(0)); + } else if (stream.frame_decoded_events_size() == 1) { + return StoreFrameDecodedEvents(stream.frame_decoded_events(0)); + } else if (stream.neteq_set_minimum_delay_size() == 1) { + return StoreNetEqSetMinimumDelay(stream.neteq_set_minimum_delay(0)); + } else { + RTC_DCHECK_NOTREACHED(); + return ParseStatus::Success(); + } +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreAlrStateEvent( + const rtclog2::AlrState& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_in_alr()); + LoggedAlrStateEvent alr_event; + alr_event.timestamp = Timestamp::Millis(proto.timestamp_ms()); + alr_event.in_alr = proto.in_alr(); + + alr_state_events_.push_back(alr_event); + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreRouteChangeEvent( + const rtclog2::RouteChange& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_connected()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_overhead()); + LoggedRouteChangeEvent route_event; + route_event.timestamp = Timestamp::Millis(proto.timestamp_ms()); + route_event.connected = proto.connected(); + route_event.overhead = proto.overhead(); + + route_change_events_.push_back(route_event); + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreRemoteEstimateEvent( + const rtclog2::RemoteEstimates& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + // Base event + LoggedRemoteEstimateEvent base_event; + base_event.timestamp = Timestamp::Millis(proto.timestamp_ms()); + + absl::optional<uint64_t> base_link_capacity_lower_kbps; + if (proto.has_link_capacity_lower_kbps()) { + base_link_capacity_lower_kbps = proto.link_capacity_lower_kbps(); + base_event.link_capacity_lower = + DataRate::KilobitsPerSec(proto.link_capacity_lower_kbps()); + } + + absl::optional<uint64_t> base_link_capacity_upper_kbps; + if (proto.has_link_capacity_upper_kbps()) { + base_link_capacity_upper_kbps = proto.link_capacity_upper_kbps(); + base_event.link_capacity_upper = + DataRate::KilobitsPerSec(proto.link_capacity_upper_kbps()); + } + + remote_estimate_events_.push_back(base_event); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + auto timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // link_capacity_lower_kbps + auto link_capacity_lower_kbps_values = + DecodeDeltas(proto.link_capacity_lower_kbps_deltas(), + base_link_capacity_lower_kbps, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(link_capacity_lower_kbps_values.size(), + number_of_deltas); + + // link_capacity_upper_kbps + auto link_capacity_upper_kbps_values = + DecodeDeltas(proto.link_capacity_upper_kbps_deltas(), + base_link_capacity_upper_kbps, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(link_capacity_upper_kbps_values.size(), + number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + LoggedRemoteEstimateEvent event; + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + event.timestamp = Timestamp::Millis(*timestamp_ms_values[i]); + if (link_capacity_lower_kbps_values[i]) + event.link_capacity_lower = + DataRate::KilobitsPerSec(*link_capacity_lower_kbps_values[i]); + if (link_capacity_upper_kbps_values[i]) + event.link_capacity_upper = + DataRate::KilobitsPerSec(*link_capacity_upper_kbps_values[i]); + remote_estimate_events_.push_back(event); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreAudioPlayoutEvent( + const rtclog2::AudioPlayoutEvents& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_ssrc()); + + // Base event + audio_playout_events_[proto.local_ssrc()].emplace_back( + Timestamp::Millis(proto.timestamp_ms()), proto.local_ssrc()); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // local_ssrc + std::vector<absl::optional<uint64_t>> local_ssrc_values = DecodeDeltas( + proto.local_ssrc_deltas(), proto.local_ssrc(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(local_ssrc_values.size(), number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(local_ssrc_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(local_ssrc_values[i].value(), + std::numeric_limits<uint32_t>::max()); + + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + const uint32_t local_ssrc = + static_cast<uint32_t>(local_ssrc_values[i].value()); + audio_playout_events_[local_ssrc].emplace_back( + Timestamp::Millis(timestamp_ms), local_ssrc); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreNetEqSetMinimumDelay( + const rtclog2::NetEqSetMinimumDelay& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_remote_ssrc()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_minimum_delay_ms()); + + // Base event + neteq_set_minimum_delay_events_[proto.remote_ssrc()].emplace_back( + Timestamp::Millis(proto.timestamp_ms()), proto.remote_ssrc(), + static_cast<int>(proto.minimum_delay_ms())); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // remote_ssrc + std::vector<absl::optional<uint64_t>> remote_ssrc_values = DecodeDeltas( + proto.remote_ssrc_deltas(), proto.remote_ssrc(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(remote_ssrc_values.size(), number_of_deltas); + + // minimum_delay_ms + std::vector<absl::optional<uint64_t>> minimum_delay_ms_values = + DecodeDeltas(proto.minimum_delay_ms_deltas(), + ToUnsigned(proto.minimum_delay_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(minimum_delay_ms_values.size(), + number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(remote_ssrc_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(remote_ssrc_values[i].value(), + std::numeric_limits<uint32_t>::max()); + RTC_PARSE_CHECK_OR_RETURN(minimum_delay_ms_values[i].has_value()); + + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + const uint32_t remote_ssrc = + static_cast<uint32_t>(remote_ssrc_values[i].value()); + int minimum_delay_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(minimum_delay_ms_values[i].value(), &minimum_delay_ms)); + neteq_set_minimum_delay_events_[remote_ssrc].emplace_back( + Timestamp::Millis(timestamp_ms), remote_ssrc, minimum_delay_ms); + } + + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreIncomingRtpPackets( + const rtclog2::IncomingRtpPackets& proto) { + return StoreRtpPackets(proto, &incoming_rtp_packets_map_); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreOutgoingRtpPackets( + const rtclog2::OutgoingRtpPackets& proto) { + return StoreRtpPackets(proto, &outgoing_rtp_packets_map_); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreIncomingRtcpPackets( + const rtclog2::IncomingRtcpPackets& proto) { + return StoreRtcpPackets(proto, &incoming_rtcp_packets_, + /*remove_duplicates=*/true); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreOutgoingRtcpPackets( + const rtclog2::OutgoingRtcpPackets& proto) { + return StoreRtcpPackets(proto, &outgoing_rtcp_packets_, + /*remove_duplicates=*/false); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreStartEvent( + const rtclog2::BeginLogEvent& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_version()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_utc_time_ms()); + RTC_PARSE_CHECK_OR_RETURN_EQ(proto.version(), 2); + LoggedStartEvent start_event(Timestamp::Millis(proto.timestamp_ms()), + Timestamp::Millis(proto.utc_time_ms())); + + start_log_events_.push_back(start_event); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreStopEvent( + const rtclog2::EndLogEvent& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + LoggedStopEvent stop_event(Timestamp::Millis(proto.timestamp_ms())); + + stop_log_events_.push_back(stop_event); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreBweLossBasedUpdate( + const rtclog2::LossBasedBweUpdates& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_bitrate_bps()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_fraction_loss()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_total_packets()); + + // Base event + bwe_loss_updates_.emplace_back(Timestamp::Millis(proto.timestamp_ms()), + proto.bitrate_bps(), proto.fraction_loss(), + proto.total_packets()); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // bitrate_bps + std::vector<absl::optional<uint64_t>> bitrate_bps_values = DecodeDeltas( + proto.bitrate_bps_deltas(), proto.bitrate_bps(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(bitrate_bps_values.size(), number_of_deltas); + + // fraction_loss + std::vector<absl::optional<uint64_t>> fraction_loss_values = DecodeDeltas( + proto.fraction_loss_deltas(), proto.fraction_loss(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(fraction_loss_values.size(), number_of_deltas); + + // total_packets + std::vector<absl::optional<uint64_t>> total_packets_values = DecodeDeltas( + proto.total_packets_deltas(), proto.total_packets(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(total_packets_values.size(), number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + RTC_PARSE_CHECK_OR_RETURN(bitrate_bps_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(bitrate_bps_values[i].value(), + std::numeric_limits<uint32_t>::max()); + const uint32_t bitrate_bps = + static_cast<uint32_t>(bitrate_bps_values[i].value()); + + RTC_PARSE_CHECK_OR_RETURN(fraction_loss_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(fraction_loss_values[i].value(), + std::numeric_limits<uint32_t>::max()); + const uint32_t fraction_loss = + static_cast<uint32_t>(fraction_loss_values[i].value()); + + RTC_PARSE_CHECK_OR_RETURN(total_packets_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(total_packets_values[i].value(), + std::numeric_limits<uint32_t>::max()); + const uint32_t total_packets = + static_cast<uint32_t>(total_packets_values[i].value()); + + bwe_loss_updates_.emplace_back(Timestamp::Millis(timestamp_ms), bitrate_bps, + fraction_loss, total_packets); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreBweDelayBasedUpdate( + const rtclog2::DelayBasedBweUpdates& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_bitrate_bps()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_detector_state()); + + // Base event + const BandwidthUsage base_detector_state = + GetRuntimeDetectorState(proto.detector_state()); + bwe_delay_updates_.emplace_back(Timestamp::Millis(proto.timestamp_ms()), + proto.bitrate_bps(), base_detector_state); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // bitrate_bps + std::vector<absl::optional<uint64_t>> bitrate_bps_values = DecodeDeltas( + proto.bitrate_bps_deltas(), proto.bitrate_bps(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(bitrate_bps_values.size(), number_of_deltas); + + // detector_state + std::vector<absl::optional<uint64_t>> detector_state_values = DecodeDeltas( + proto.detector_state_deltas(), + static_cast<uint64_t>(proto.detector_state()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(detector_state_values.size(), number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + RTC_PARSE_CHECK_OR_RETURN(bitrate_bps_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(bitrate_bps_values[i].value(), + std::numeric_limits<uint32_t>::max()); + const uint32_t bitrate_bps = + static_cast<uint32_t>(bitrate_bps_values[i].value()); + + RTC_PARSE_CHECK_OR_RETURN(detector_state_values[i].has_value()); + const auto detector_state = + static_cast<rtclog2::DelayBasedBweUpdates::DetectorState>( + detector_state_values[i].value()); + + bwe_delay_updates_.emplace_back(Timestamp::Millis(timestamp_ms), + bitrate_bps, + GetRuntimeDetectorState(detector_state)); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreBweProbeClusterCreated( + const rtclog2::BweProbeCluster& proto) { + LoggedBweProbeClusterCreatedEvent probe_cluster; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + probe_cluster.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_id()); + probe_cluster.id = proto.id(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_bitrate_bps()); + probe_cluster.bitrate_bps = proto.bitrate_bps(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_min_packets()); + probe_cluster.min_packets = proto.min_packets(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_min_bytes()); + probe_cluster.min_bytes = proto.min_bytes(); + + bwe_probe_cluster_created_events_.push_back(probe_cluster); + + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreBweProbeSuccessEvent( + const rtclog2::BweProbeResultSuccess& proto) { + LoggedBweProbeSuccessEvent probe_result; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + probe_result.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_id()); + probe_result.id = proto.id(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_bitrate_bps()); + probe_result.bitrate_bps = proto.bitrate_bps(); + + bwe_probe_success_events_.push_back(probe_result); + + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreBweProbeFailureEvent( + const rtclog2::BweProbeResultFailure& proto) { + LoggedBweProbeFailureEvent probe_result; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + probe_result.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_id()); + probe_result.id = proto.id(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_failure()); + probe_result.failure_reason = GetRuntimeProbeFailureReason(proto.failure()); + + bwe_probe_failure_events_.push_back(probe_result); + + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreFrameDecodedEvents( + const rtclog2::FrameDecodedEvents& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_ssrc()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_render_time_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_width()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_height()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_codec()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_qp()); + + LoggedFrameDecoded base_frame; + base_frame.timestamp = Timestamp::Millis(proto.timestamp_ms()); + base_frame.ssrc = proto.ssrc(); + base_frame.render_time_ms = proto.render_time_ms(); + base_frame.width = proto.width(); + base_frame.height = proto.height(); + base_frame.codec = GetRuntimeCodecType(proto.codec()); + RTC_PARSE_CHECK_OR_RETURN_GE(proto.qp(), 0); + RTC_PARSE_CHECK_OR_RETURN_LE(proto.qp(), 255); + base_frame.qp = static_cast<uint8_t>(proto.qp()); + + decoded_frames_[base_frame.ssrc].push_back(base_frame); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // SSRC + std::vector<absl::optional<uint64_t>> ssrc_values = + DecodeDeltas(proto.ssrc_deltas(), proto.ssrc(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(ssrc_values.size(), number_of_deltas); + + // render_time_ms + std::vector<absl::optional<uint64_t>> render_time_ms_values = + DecodeDeltas(proto.render_time_ms_deltas(), + ToUnsigned(proto.render_time_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(render_time_ms_values.size(), number_of_deltas); + + // width + std::vector<absl::optional<uint64_t>> width_values = DecodeDeltas( + proto.width_deltas(), ToUnsigned(proto.width()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(width_values.size(), number_of_deltas); + + // height + std::vector<absl::optional<uint64_t>> height_values = DecodeDeltas( + proto.height_deltas(), ToUnsigned(proto.height()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(height_values.size(), number_of_deltas); + + // codec + std::vector<absl::optional<uint64_t>> codec_values = + DecodeDeltas(proto.codec_deltas(), static_cast<uint64_t>(proto.codec()), + number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(codec_values.size(), number_of_deltas); + + // qp + std::vector<absl::optional<uint64_t>> qp_values = + DecodeDeltas(proto.qp_deltas(), proto.qp(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(qp_values.size(), number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + LoggedFrameDecoded frame; + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + frame.timestamp = Timestamp::Millis(timestamp_ms); + + RTC_PARSE_CHECK_OR_RETURN(ssrc_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(ssrc_values[i].value(), + std::numeric_limits<uint32_t>::max()); + frame.ssrc = static_cast<uint32_t>(ssrc_values[i].value()); + + RTC_PARSE_CHECK_OR_RETURN(render_time_ms_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(render_time_ms_values[i].value(), &frame.render_time_ms)); + + RTC_PARSE_CHECK_OR_RETURN(width_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(ToSigned(width_values[i].value(), &frame.width)); + + RTC_PARSE_CHECK_OR_RETURN(height_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(height_values[i].value(), &frame.height)); + + RTC_PARSE_CHECK_OR_RETURN(codec_values[i].has_value()); + frame.codec = + GetRuntimeCodecType(static_cast<rtclog2::FrameDecodedEvents::Codec>( + codec_values[i].value())); + + RTC_PARSE_CHECK_OR_RETURN(qp_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN_LE(qp_values[i].value(), + std::numeric_limits<uint8_t>::max()); + frame.qp = static_cast<uint8_t>(qp_values[i].value()); + + decoded_frames_[frame.ssrc].push_back(frame); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreGenericAckReceivedEvent( + const rtclog2::GenericAckReceived& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_packet_number()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_acked_packet_number()); + // receive_acked_packet_time_ms is optional. + + absl::optional<int64_t> base_receive_acked_packet_time_ms; + if (proto.has_receive_acked_packet_time_ms()) { + base_receive_acked_packet_time_ms = proto.receive_acked_packet_time_ms(); + } + generic_acks_received_.push_back( + {Timestamp::Millis(proto.timestamp_ms()), proto.packet_number(), + proto.acked_packet_number(), base_receive_acked_packet_time_ms}); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // packet_number + std::vector<absl::optional<uint64_t>> packet_number_values = + DecodeDeltas(proto.packet_number_deltas(), + ToUnsigned(proto.packet_number()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(packet_number_values.size(), number_of_deltas); + + // acked_packet_number + std::vector<absl::optional<uint64_t>> acked_packet_number_values = + DecodeDeltas(proto.acked_packet_number_deltas(), + ToUnsigned(proto.acked_packet_number()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(acked_packet_number_values.size(), + number_of_deltas); + + // optional receive_acked_packet_time_ms + const absl::optional<uint64_t> unsigned_receive_acked_packet_time_ms_base = + proto.has_receive_acked_packet_time_ms() + ? absl::optional<uint64_t>( + ToUnsigned(proto.receive_acked_packet_time_ms())) + : absl::optional<uint64_t>(); + std::vector<absl::optional<uint64_t>> receive_acked_packet_time_ms_values = + DecodeDeltas(proto.receive_acked_packet_time_ms_deltas(), + unsigned_receive_acked_packet_time_ms_base, + number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(receive_acked_packet_time_ms_values.size(), + number_of_deltas); + + for (size_t i = 0; i < number_of_deltas; i++) { + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + int64_t packet_number; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(packet_number_values[i].value(), &packet_number)); + int64_t acked_packet_number; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(acked_packet_number_values[i].value(), &acked_packet_number)); + absl::optional<int64_t> receive_acked_packet_time_ms; + + if (receive_acked_packet_time_ms_values[i].has_value()) { + int64_t value; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(receive_acked_packet_time_ms_values[i].value(), &value)); + receive_acked_packet_time_ms = value; + } + generic_acks_received_.push_back({Timestamp::Millis(timestamp_ms), + packet_number, acked_packet_number, + receive_acked_packet_time_ms}); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreGenericPacketSentEvent( + const rtclog2::GenericPacketSent& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + + // Base event + RTC_PARSE_CHECK_OR_RETURN(proto.has_packet_number()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_overhead_length()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_payload_length()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_padding_length()); + + generic_packets_sent_.push_back( + {Timestamp::Millis(proto.timestamp_ms()), proto.packet_number(), + static_cast<size_t>(proto.overhead_length()), + static_cast<size_t>(proto.payload_length()), + static_cast<size_t>(proto.padding_length())}); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // packet_number + std::vector<absl::optional<uint64_t>> packet_number_values = + DecodeDeltas(proto.packet_number_deltas(), + ToUnsigned(proto.packet_number()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(packet_number_values.size(), number_of_deltas); + + std::vector<absl::optional<uint64_t>> overhead_length_values = + DecodeDeltas(proto.overhead_length_deltas(), proto.overhead_length(), + number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(overhead_length_values.size(), number_of_deltas); + + std::vector<absl::optional<uint64_t>> payload_length_values = DecodeDeltas( + proto.payload_length_deltas(), proto.payload_length(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(payload_length_values.size(), number_of_deltas); + + std::vector<absl::optional<uint64_t>> padding_length_values = DecodeDeltas( + proto.padding_length_deltas(), proto.padding_length(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(padding_length_values.size(), number_of_deltas); + + for (size_t i = 0; i < number_of_deltas; i++) { + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + int64_t packet_number; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(packet_number_values[i].value(), &packet_number)); + RTC_PARSE_CHECK_OR_RETURN(overhead_length_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(payload_length_values[i].has_value()); + RTC_PARSE_CHECK_OR_RETURN(padding_length_values[i].has_value()); + generic_packets_sent_.push_back( + {Timestamp::Millis(timestamp_ms), packet_number, + static_cast<size_t>(overhead_length_values[i].value()), + static_cast<size_t>(payload_length_values[i].value()), + static_cast<size_t>(padding_length_values[i].value())}); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus +ParsedRtcEventLog::StoreGenericPacketReceivedEvent( + const rtclog2::GenericPacketReceived& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + + // Base event + RTC_PARSE_CHECK_OR_RETURN(proto.has_packet_number()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_packet_length()); + + generic_packets_received_.push_back({Timestamp::Millis(proto.timestamp_ms()), + proto.packet_number(), + proto.packet_length()}); + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // packet_number + std::vector<absl::optional<uint64_t>> packet_number_values = + DecodeDeltas(proto.packet_number_deltas(), + ToUnsigned(proto.packet_number()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(packet_number_values.size(), number_of_deltas); + + std::vector<absl::optional<uint64_t>> packet_length_values = DecodeDeltas( + proto.packet_length_deltas(), proto.packet_length(), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(packet_length_values.size(), number_of_deltas); + + for (size_t i = 0; i < number_of_deltas; i++) { + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + int64_t packet_number; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(packet_number_values[i].value(), &packet_number)); + RTC_PARSE_CHECK_OR_RETURN_LE(packet_length_values[i].value(), + std::numeric_limits<int32_t>::max()); + int32_t packet_length = + static_cast<int32_t>(packet_length_values[i].value()); + generic_packets_received_.push_back( + {Timestamp::Millis(timestamp_ms), packet_number, packet_length}); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus +ParsedRtcEventLog::StoreAudioNetworkAdaptationEvent( + const rtclog2::AudioNetworkAdaptations& proto) { + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + + // Base event + { + AudioEncoderRuntimeConfig runtime_config; + if (proto.has_bitrate_bps()) { + runtime_config.bitrate_bps = proto.bitrate_bps(); + } + if (proto.has_frame_length_ms()) { + runtime_config.frame_length_ms = proto.frame_length_ms(); + } + if (proto.has_uplink_packet_loss_fraction()) { + float uplink_packet_loss_fraction; + RTC_PARSE_CHECK_OR_RETURN(ParsePacketLossFractionFromProtoFormat( + proto.uplink_packet_loss_fraction(), &uplink_packet_loss_fraction)); + runtime_config.uplink_packet_loss_fraction = uplink_packet_loss_fraction; + } + if (proto.has_enable_fec()) { + runtime_config.enable_fec = proto.enable_fec(); + } + if (proto.has_enable_dtx()) { + runtime_config.enable_dtx = proto.enable_dtx(); + } + if (proto.has_num_channels()) { + // Note: Encoding N as N-1 only done for `num_channels_deltas`. + runtime_config.num_channels = proto.num_channels(); + } + audio_network_adaptation_events_.emplace_back( + Timestamp::Millis(proto.timestamp_ms()), runtime_config); + } + + const size_t number_of_deltas = + proto.has_number_of_deltas() ? proto.number_of_deltas() : 0u; + if (number_of_deltas == 0) { + return ParseStatus::Success(); + } + + // timestamp_ms + std::vector<absl::optional<uint64_t>> timestamp_ms_values = + DecodeDeltas(proto.timestamp_ms_deltas(), + ToUnsigned(proto.timestamp_ms()), number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(timestamp_ms_values.size(), number_of_deltas); + + // bitrate_bps + const absl::optional<uint64_t> unsigned_base_bitrate_bps = + proto.has_bitrate_bps() + ? absl::optional<uint64_t>(ToUnsigned(proto.bitrate_bps())) + : absl::optional<uint64_t>(); + std::vector<absl::optional<uint64_t>> bitrate_bps_values = DecodeDeltas( + proto.bitrate_bps_deltas(), unsigned_base_bitrate_bps, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(bitrate_bps_values.size(), number_of_deltas); + + // frame_length_ms + const absl::optional<uint64_t> unsigned_base_frame_length_ms = + proto.has_frame_length_ms() + ? absl::optional<uint64_t>(ToUnsigned(proto.frame_length_ms())) + : absl::optional<uint64_t>(); + std::vector<absl::optional<uint64_t>> frame_length_ms_values = + DecodeDeltas(proto.frame_length_ms_deltas(), + unsigned_base_frame_length_ms, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(frame_length_ms_values.size(), number_of_deltas); + + // uplink_packet_loss_fraction + const absl::optional<uint64_t> uplink_packet_loss_fraction = + proto.has_uplink_packet_loss_fraction() + ? absl::optional<uint64_t>(proto.uplink_packet_loss_fraction()) + : absl::optional<uint64_t>(); + std::vector<absl::optional<uint64_t>> uplink_packet_loss_fraction_values = + DecodeDeltas(proto.uplink_packet_loss_fraction_deltas(), + uplink_packet_loss_fraction, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(uplink_packet_loss_fraction_values.size(), + number_of_deltas); + + // enable_fec + const absl::optional<uint64_t> enable_fec = + proto.has_enable_fec() ? absl::optional<uint64_t>(proto.enable_fec()) + : absl::optional<uint64_t>(); + std::vector<absl::optional<uint64_t>> enable_fec_values = + DecodeDeltas(proto.enable_fec_deltas(), enable_fec, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(enable_fec_values.size(), number_of_deltas); + + // enable_dtx + const absl::optional<uint64_t> enable_dtx = + proto.has_enable_dtx() ? absl::optional<uint64_t>(proto.enable_dtx()) + : absl::optional<uint64_t>(); + std::vector<absl::optional<uint64_t>> enable_dtx_values = + DecodeDeltas(proto.enable_dtx_deltas(), enable_dtx, number_of_deltas); + RTC_PARSE_CHECK_OR_RETURN_EQ(enable_dtx_values.size(), number_of_deltas); + + // num_channels + // Note: For delta encoding, all num_channel values, including the base, + // were shifted down by one, but in the base event, they were not. + // We likewise shift the base event down by one, to get the same base as + // encoding had, but then shift all of the values (except the base) back up + // to their original value. + absl::optional<uint64_t> shifted_base_num_channels; + if (proto.has_num_channels()) { + shifted_base_num_channels = + absl::optional<uint64_t>(proto.num_channels() - 1); + } + std::vector<absl::optional<uint64_t>> num_channels_values = DecodeDeltas( + proto.num_channels_deltas(), shifted_base_num_channels, number_of_deltas); + for (size_t i = 0; i < num_channels_values.size(); ++i) { + if (num_channels_values[i].has_value()) { + num_channels_values[i] = num_channels_values[i].value() + 1; + } + } + RTC_PARSE_CHECK_OR_RETURN_EQ(num_channels_values.size(), number_of_deltas); + + // Populate events from decoded deltas + for (size_t i = 0; i < number_of_deltas; ++i) { + RTC_PARSE_CHECK_OR_RETURN(timestamp_ms_values[i].has_value()); + int64_t timestamp_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(timestamp_ms_values[i].value(), ×tamp_ms)); + + AudioEncoderRuntimeConfig runtime_config; + if (bitrate_bps_values[i].has_value()) { + int signed_bitrate_bps; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(bitrate_bps_values[i].value(), &signed_bitrate_bps)); + runtime_config.bitrate_bps = signed_bitrate_bps; + } + if (frame_length_ms_values[i].has_value()) { + int signed_frame_length_ms; + RTC_PARSE_CHECK_OR_RETURN( + ToSigned(frame_length_ms_values[i].value(), &signed_frame_length_ms)); + runtime_config.frame_length_ms = signed_frame_length_ms; + } + if (uplink_packet_loss_fraction_values[i].has_value()) { + float uplink_packet_loss_fraction2; + RTC_PARSE_CHECK_OR_RETURN(ParsePacketLossFractionFromProtoFormat( + rtc::checked_cast<uint32_t>( + uplink_packet_loss_fraction_values[i].value()), + &uplink_packet_loss_fraction2)); + runtime_config.uplink_packet_loss_fraction = uplink_packet_loss_fraction2; + } + if (enable_fec_values[i].has_value()) { + runtime_config.enable_fec = + rtc::checked_cast<bool>(enable_fec_values[i].value()); + } + if (enable_dtx_values[i].has_value()) { + runtime_config.enable_dtx = + rtc::checked_cast<bool>(enable_dtx_values[i].value()); + } + if (num_channels_values[i].has_value()) { + runtime_config.num_channels = + rtc::checked_cast<size_t>(num_channels_values[i].value()); + } + audio_network_adaptation_events_.emplace_back( + Timestamp::Millis(timestamp_ms), runtime_config); + } + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreDtlsTransportState( + const rtclog2::DtlsTransportStateEvent& proto) { + LoggedDtlsTransportState dtls_state; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + dtls_state.timestamp = Timestamp::Millis(proto.timestamp_ms()); + + RTC_PARSE_CHECK_OR_RETURN(proto.has_dtls_transport_state()); + dtls_state.dtls_transport_state = + GetRuntimeDtlsTransportState(proto.dtls_transport_state()); + + dtls_transport_states_.push_back(dtls_state); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreDtlsWritableState( + const rtclog2::DtlsWritableState& proto) { + LoggedDtlsWritableState dtls_writable_state; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + dtls_writable_state.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_writable()); + dtls_writable_state.writable = proto.writable(); + + dtls_writable_states_.push_back(dtls_writable_state); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreIceCandidatePairConfig( + const rtclog2::IceCandidatePairConfig& proto) { + LoggedIceCandidatePairConfig ice_config; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + ice_config.timestamp = Timestamp::Millis(proto.timestamp_ms()); + + RTC_PARSE_CHECK_OR_RETURN(proto.has_config_type()); + ice_config.type = GetRuntimeIceCandidatePairConfigType(proto.config_type()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_candidate_pair_id()); + ice_config.candidate_pair_id = proto.candidate_pair_id(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_candidate_type()); + ice_config.local_candidate_type = + GetRuntimeIceCandidateType(proto.local_candidate_type()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_relay_protocol()); + ice_config.local_relay_protocol = + GetRuntimeIceCandidatePairProtocol(proto.local_relay_protocol()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_network_type()); + ice_config.local_network_type = + GetRuntimeIceCandidateNetworkType(proto.local_network_type()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_address_family()); + ice_config.local_address_family = + GetRuntimeIceCandidatePairAddressFamily(proto.local_address_family()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_remote_candidate_type()); + ice_config.remote_candidate_type = + GetRuntimeIceCandidateType(proto.remote_candidate_type()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_remote_address_family()); + ice_config.remote_address_family = + GetRuntimeIceCandidatePairAddressFamily(proto.remote_address_family()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_candidate_pair_protocol()); + ice_config.candidate_pair_protocol = + GetRuntimeIceCandidatePairProtocol(proto.candidate_pair_protocol()); + + ice_candidate_pair_configs_.push_back(ice_config); + + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreIceCandidateEvent( + const rtclog2::IceCandidatePairEvent& proto) { + LoggedIceCandidatePairEvent ice_event; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + ice_event.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_event_type()); + ice_event.type = GetRuntimeIceCandidatePairEventType(proto.event_type()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_candidate_pair_id()); + ice_event.candidate_pair_id = proto.candidate_pair_id(); + // TODO(zstein): Make the transaction_id field required once all old versions + // of the log (which don't have the field) are obsolete. + ice_event.transaction_id = + proto.has_transaction_id() ? proto.transaction_id() : 0; + + ice_candidate_pair_events_.push_back(ice_event); + + // TODO(terelius): Should we delta encode this event type? + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreVideoRecvConfig( + const rtclog2::VideoRecvStreamConfig& proto) { + LoggedVideoRecvConfig stream; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + stream.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_remote_ssrc()); + stream.config.remote_ssrc = proto.remote_ssrc(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_ssrc()); + stream.config.local_ssrc = proto.local_ssrc(); + if (proto.has_rtx_ssrc()) { + stream.config.rtx_ssrc = proto.rtx_ssrc(); + } + if (proto.has_header_extensions()) { + stream.config.rtp_extensions = + GetRuntimeRtpHeaderExtensionConfig(proto.header_extensions()); + } + video_recv_configs_.push_back(stream); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreVideoSendConfig( + const rtclog2::VideoSendStreamConfig& proto) { + LoggedVideoSendConfig stream; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + stream.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_ssrc()); + stream.config.local_ssrc = proto.ssrc(); + if (proto.has_rtx_ssrc()) { + stream.config.rtx_ssrc = proto.rtx_ssrc(); + } + if (proto.has_header_extensions()) { + stream.config.rtp_extensions = + GetRuntimeRtpHeaderExtensionConfig(proto.header_extensions()); + } + video_send_configs_.push_back(stream); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreAudioRecvConfig( + const rtclog2::AudioRecvStreamConfig& proto) { + LoggedAudioRecvConfig stream; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + stream.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_remote_ssrc()); + stream.config.remote_ssrc = proto.remote_ssrc(); + RTC_PARSE_CHECK_OR_RETURN(proto.has_local_ssrc()); + stream.config.local_ssrc = proto.local_ssrc(); + if (proto.has_header_extensions()) { + stream.config.rtp_extensions = + GetRuntimeRtpHeaderExtensionConfig(proto.header_extensions()); + } + audio_recv_configs_.push_back(stream); + return ParseStatus::Success(); +} + +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreAudioSendConfig( + const rtclog2::AudioSendStreamConfig& proto) { + LoggedAudioSendConfig stream; + RTC_PARSE_CHECK_OR_RETURN(proto.has_timestamp_ms()); + stream.timestamp = Timestamp::Millis(proto.timestamp_ms()); + RTC_PARSE_CHECK_OR_RETURN(proto.has_ssrc()); + stream.config.local_ssrc = proto.ssrc(); + if (proto.has_header_extensions()) { + stream.config.rtp_extensions = + GetRuntimeRtpHeaderExtensionConfig(proto.header_extensions()); + } + audio_send_configs_.push_back(stream); + return ParseStatus::Success(); +} + +} // namespace webrtc |