/* * 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 #include #include #include #include #include #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 unwrap_capture_ticks; }; template void AddRecvStreamInfos(std::map* 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 void AddSendStreamInfos(std::map* 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 GetOverheadChangingEvents( const std::vector& route_changes, PacketDirection direction) { std::vector 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& 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* header_extensions, const RepeatedPtrField& 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 ParsedRtcEventLog::ParseStatus StoreRtpPackets( const ProtoType& proto, std::map>* 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> 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(proto.marker()); header.payloadType = rtc::checked_cast(proto.payload_type()); header.sequenceNumber = rtc::checked_cast(proto.sequence_number()); header.timestamp = rtc::checked_cast(proto.rtp_timestamp()); header.ssrc = rtc::checked_cast(proto.ssrc()); header.numCSRCs = 0; // TODO(terelius): Implement CSRC. header.paddingLength = rtc::checked_cast(proto.padding_size()); header.headerLength = rtc::checked_cast(proto.header_size()); // TODO(terelius): Should we implement payload_type_frequency? if (proto.has_transport_sequence_number()) { header.extension.hasTransportSequenceNumber = true; header.extension.transportSequenceNumber = rtc::checked_cast(proto.transport_sequence_number()); } if (proto.has_transmission_time_offset()) { header.extension.hasTransmissionTimeOffset = true; header.extension.transmissionTimeOffset = rtc::checked_cast(proto.transmission_time_offset()); } if (proto.has_absolute_send_time()) { header.extension.hasAbsoluteSendTime = true; header.extension.absoluteSendTime = rtc::checked_cast(proto.absolute_send_time()); } if (proto.has_video_rotation()) { header.extension.hasVideoRotation = true; header.extension.videoRotation = ConvertCVOByteToVideoRotation( rtc::checked_cast(proto.video_rotation())); } if (proto.has_audio_level()) { RTC_PARSE_CHECK_OR_RETURN(proto.has_voice_activity()); header.extension.hasAudioLevel = true; header.extension.voiceActivity = rtc::checked_cast(proto.voice_activity()); const uint8_t audio_level = rtc::checked_cast(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> 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> 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> 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> 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> 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> 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> 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> 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> 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> transport_sequence_number_values; { const absl::optional base_transport_sequence_number = proto.has_transport_sequence_number() ? proto.transport_sequence_number() : absl::optional(); transport_sequence_number_values = DecodeDeltas(proto.transport_sequence_number_deltas(), base_transport_sequence_number, number_of_deltas); RTC_PARSE_CHECK_OR_RETURN_EQ(transport_sequence_number_values.size(), number_of_deltas); } // transmission_time_offset (RTP extension) std::vector> transmission_time_offset_values; { const absl::optional unsigned_base_transmission_time_offset = proto.has_transmission_time_offset() ? ToUnsigned(proto.transmission_time_offset()) : absl::optional(); transmission_time_offset_values = DecodeDeltas(proto.transmission_time_offset_deltas(), unsigned_base_transmission_time_offset, number_of_deltas); RTC_PARSE_CHECK_OR_RETURN_EQ(transmission_time_offset_values.size(), number_of_deltas); } // absolute_send_time (RTP extension) std::vector> absolute_send_time_values; { const absl::optional base_absolute_send_time = proto.has_absolute_send_time() ? proto.absolute_send_time() : absl::optional(); absolute_send_time_values = DecodeDeltas(proto.absolute_send_time_deltas(), base_absolute_send_time, number_of_deltas); RTC_PARSE_CHECK_OR_RETURN_EQ(absolute_send_time_values.size(), number_of_deltas); } // video_rotation (RTP extension) std::vector> video_rotation_values; { const absl::optional base_video_rotation = proto.has_video_rotation() ? proto.video_rotation() : absl::optional(); video_rotation_values = DecodeDeltas(proto.video_rotation_deltas(), base_video_rotation, number_of_deltas); RTC_PARSE_CHECK_OR_RETURN_EQ(video_rotation_values.size(), number_of_deltas); } // audio_level (RTP extension) std::vector> audio_level_values; { const absl::optional base_audio_level = proto.has_audio_level() ? proto.audio_level() : absl::optional(); audio_level_values = DecodeDeltas(proto.audio_level_deltas(), base_audio_level, number_of_deltas); RTC_PARSE_CHECK_OR_RETURN_EQ(audio_level_values.size(), number_of_deltas); } // voice_activity (RTP extension) std::vector> voice_activity_values; { const absl::optional base_voice_activity = proto.has_voice_activity() ? proto.voice_activity() : absl::optional(); 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(*marker_values[i]); header.payloadType = rtc::checked_cast(*payload_type_values[i]); header.sequenceNumber = rtc::checked_cast(*sequence_number_values[i]); header.timestamp = rtc::checked_cast(*rtp_timestamp_values[i]); header.ssrc = rtc::checked_cast(*ssrc_values[i]); header.numCSRCs = 0; // TODO(terelius): Implement CSRC. header.paddingLength = rtc::checked_cast(*padding_size_values[i]); header.headerLength = rtc::checked_cast(*header_size_values[i]); // TODO(terelius): Should we implement payload_type_frequency? if (transport_sequence_number_values.size() > i && transport_sequence_number_values[i].has_value()) { header.extension.hasTransportSequenceNumber = true; header.extension.transportSequenceNumber = rtc::checked_cast( transport_sequence_number_values[i].value()); } if (transmission_time_offset_values.size() > i && transmission_time_offset_values[i].has_value()) { header.extension.hasTransmissionTimeOffset = true; int32_t transmission_time_offset; RTC_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(absolute_send_time_values[i].value()); } if (video_rotation_values.size() > i && video_rotation_values[i].has_value()) { header.extension.hasVideoRotation = true; header.extension.videoRotation = ConvertCVOByteToVideoRotation( rtc::checked_cast(video_rotation_values[i].value())); } if (audio_level_values.size() > i && audio_level_values[i].has_value()) { 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(voice_activity_values[i].value()); const uint8_t audio_level = rtc::checked_cast(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 ParsedRtcEventLog::ParseStatus StoreRtcpPackets( const ProtoType& proto, std::vector* 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> 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 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* sr_list, std::vector* rr_list, std::vector* xr_list, std::vector* remb_list, std::vector* nack_list, std::vector* fir_list, std::vector* pli_list, std::vector* bye_list, std::vector* transport_feedback_list, std::vector* 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 GetRuntimeRtpHeaderExtensionConfig( const rtclog2::RtpHeaderExtensionConfig& proto_header_extensions) { std::vector 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(rtp.total_length)), payload_size(static_cast(rtp.total_length - rtp.header.paddingLength - rtp.header.headerLength)), padding_size(static_cast(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( 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& 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& 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(kAudioLevelDefaultId); default_map.Register(kTimestampOffsetDefaultId); default_map.Register(kAbsSendTimeDefaultId); default_map.Register(kVideoRotationDefaultId); default_map.Register( kTransportSequenceNumberDefaultId); default_map.Register(kPlayoutDelayDefaultId); default_map.Register(kVideoContentTypeDefaultId); default_map.Register(kVideoTimingDefaultId); default_map.Register( 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::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(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::max()); int64_t next_start_us = std::numeric_limits::max(); int64_t stop_us = std::numeric_limits::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::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(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(RtcEvent::Type::BeginV3Log)); expect_begin_log_event = false; } switch (event_type) { case static_cast(RtcEvent::Type::BeginV3Log): RtcEventBeginLog::Parse(event_fields, batched, start_log_events_); break; case static_cast(RtcEvent::Type::EndV3Log): RtcEventEndLog::Parse(event_fields, batched, stop_log_events_); expect_begin_log_event = true; break; case static_cast(RtcEvent::Type::AlrStateEvent): RtcEventAlrState::Parse(event_fields, batched, alr_state_events_); break; case static_cast(RtcEvent::Type::AudioPlayout): RtcEventAudioPlayout::Parse(event_fields, batched, audio_playout_events_); break; case static_cast(RtcEvent::Type::BweUpdateDelayBased): RtcEventBweUpdateDelayBased::Parse(event_fields, batched, bwe_delay_updates_); break; case static_cast(RtcEvent::Type::AudioNetworkAdaptation): RtcEventAudioNetworkAdaptation::Parse(event_fields, batched, audio_network_adaptation_events_); break; case static_cast(RtcEvent::Type::AudioReceiveStreamConfig): RtcEventAudioReceiveStreamConfig::Parse(event_fields, batched, audio_recv_configs_); break; case static_cast(RtcEvent::Type::AudioSendStreamConfig): RtcEventAudioSendStreamConfig::Parse(event_fields, batched, audio_send_configs_); break; case static_cast(RtcEvent::Type::BweUpdateLossBased): RtcEventBweUpdateLossBased::Parse(event_fields, batched, bwe_loss_updates_); break; case static_cast(RtcEvent::Type::DtlsTransportState): RtcEventDtlsTransportState::Parse(event_fields, batched, dtls_transport_states_); break; case static_cast(RtcEvent::Type::DtlsWritableState): RtcEventDtlsWritableState::Parse(event_fields, batched, dtls_writable_states_); break; case static_cast(RtcEvent::Type::FrameDecoded): RtcEventFrameDecoded::Parse(event_fields, batched, decoded_frames_); break; case static_cast(RtcEvent::Type::GenericAckReceived): RtcEventGenericAckReceived::Parse(event_fields, batched, generic_acks_received_); break; case static_cast(RtcEvent::Type::GenericPacketReceived): RtcEventGenericPacketReceived::Parse(event_fields, batched, generic_packets_received_); break; case static_cast(RtcEvent::Type::GenericPacketSent): RtcEventGenericPacketSent::Parse(event_fields, batched, generic_packets_sent_); break; case static_cast(RtcEvent::Type::IceCandidatePairConfig): RtcEventIceCandidatePairConfig::Parse(event_fields, batched, ice_candidate_pair_configs_); break; case static_cast(RtcEvent::Type::IceCandidatePairEvent): RtcEventIceCandidatePair::Parse(event_fields, batched, ice_candidate_pair_events_); break; case static_cast(RtcEvent::Type::ProbeClusterCreated): RtcEventProbeClusterCreated::Parse(event_fields, batched, bwe_probe_cluster_created_events_); break; case static_cast(RtcEvent::Type::ProbeResultFailure): RtcEventProbeResultFailure::Parse(event_fields, batched, bwe_probe_failure_events_); break; case static_cast(RtcEvent::Type::ProbeResultSuccess): RtcEventProbeResultSuccess::Parse(event_fields, batched, bwe_probe_success_events_); break; case static_cast(RtcEvent::Type::RemoteEstimateEvent): RtcEventRemoteEstimate::Parse(event_fields, batched, remote_estimate_events_); break; case static_cast(RtcEvent::Type::RouteChangeEvent): RtcEventRouteChange::Parse(event_fields, batched, route_change_events_); break; case static_cast(RtcEvent::Type::RtcpPacketIncoming): RtcEventRtcpPacketIncoming::Parse(event_fields, batched, incoming_rtcp_packets_); break; case static_cast(RtcEvent::Type::RtcpPacketOutgoing): RtcEventRtcpPacketOutgoing::Parse(event_fields, batched, outgoing_rtcp_packets_); break; case static_cast(RtcEvent::Type::RtpPacketIncoming): RtcEventRtpPacketIncoming::Parse(event_fields, batched, incoming_rtp_packets_map_); break; case static_cast(RtcEvent::Type::RtpPacketOutgoing): RtcEventRtpPacketOutgoing::Parse(event_fields, batched, outgoing_rtp_packets_map_); break; case static_cast(RtcEvent::Type::VideoReceiveStreamConfig): RtcEventVideoReceiveStreamConfig::Parse(event_fields, batched, video_recv_configs_); break; case static_cast(RtcEvent::Type::VideoSendStreamConfig): RtcEventVideoSendStreamConfig::Parse(event_fields, batched, video_send_configs_); break; } } return ParseStatus::Success(); } template void ParsedRtcEventLog::StoreFirstAndLastTimestamp(const std::vector& 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 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* 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 ParsedRtcEventLog::GetRouteChanges() const { std::vector 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 ParsedRtcEventLog::GetPacketInfos( PacketDirection direction) const { std::map 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 overheads = GetOverheadChangingEvents(GetRouteChanges(), direction); auto overhead_iter = overheads.begin(); std::vector packets; std::map 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 packet_feedbacks; packet_feedbacks.reserve(feedback.GetPacketStatusCount()); std::vector 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 ParsedRtcEventLog::GetIceCandidates() const { std::vector candidates; std::set 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 ParsedRtcEventLog::GetIceEvents() const { using CheckType = IceCandidatePairEventType; using ConfigType = IceCandidatePairConfigType; using Combined = LoggedIceEventType; std::map check_map( {{CheckType::kCheckSent, Combined::kCheckSent}, {CheckType::kCheckReceived, Combined::kCheckReceived}, {CheckType::kCheckResponseSent, Combined::kCheckResponseSent}, {CheckType::kCheckResponseReceived, Combined::kCheckResponseReceived}}); std::map config_map( {{ConfigType::kAdded, Combined::kAdded}, {ConfigType::kUpdated, Combined::kUpdated}, {ConfigType::kDestroyed, Combined::kDestroyed}, {ConfigType::kSelected, Combined::kSelected}}); std::vector 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 GetNetworkTrace( const ParsedRtcEventLog& parsed_log) { std::vector 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 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 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> 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> 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::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(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(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> 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> 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> 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::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(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> 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> 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> 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> 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::max()); const uint32_t bitrate_bps = static_cast(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::max()); const uint32_t fraction_loss = static_cast(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::max()); const uint32_t total_packets = static_cast(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> 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> 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> detector_state_values = DecodeDeltas( proto.detector_state_deltas(), static_cast(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::max()); const uint32_t bitrate_bps = static_cast(bitrate_bps_values[i].value()); RTC_PARSE_CHECK_OR_RETURN(detector_state_values[i].has_value()); const auto detector_state = static_cast( 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(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> 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> 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> 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> 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> 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> codec_values = DecodeDeltas(proto.codec_deltas(), static_cast(proto.codec()), number_of_deltas); RTC_PARSE_CHECK_OR_RETURN_EQ(codec_values.size(), number_of_deltas); // qp std::vector> 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::max()); frame.ssrc = static_cast(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( 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::max()); frame.qp = static_cast(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 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> 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> 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> 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 unsigned_receive_acked_packet_time_ms_base = proto.has_receive_acked_packet_time_ms() ? absl::optional( ToUnsigned(proto.receive_acked_packet_time_ms())) : absl::optional(); std::vector> 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 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(proto.overhead_length()), static_cast(proto.payload_length()), static_cast(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> 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> 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> 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> 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> 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(overhead_length_values[i].value()), static_cast(payload_length_values[i].value()), static_cast(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> 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> 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> 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::max()); int32_t packet_length = static_cast(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> 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 unsigned_base_bitrate_bps = proto.has_bitrate_bps() ? absl::optional(ToUnsigned(proto.bitrate_bps())) : absl::optional(); std::vector> 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 unsigned_base_frame_length_ms = proto.has_frame_length_ms() ? absl::optional(ToUnsigned(proto.frame_length_ms())) : absl::optional(); std::vector> frame_length_ms_values = DecodeDeltas(proto.frame_length_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 uplink_packet_loss_fraction = proto.has_uplink_packet_loss_fraction() ? absl::optional(proto.uplink_packet_loss_fraction()) : absl::optional(); std::vector> uplink_packet_loss_fraction_values = DecodeDeltas(proto.uplink_packet_loss_fraction_deltas(), uplink_packet_loss_fraction, number_of_deltas); RTC_PARSE_CHECK_OR_RETURN_EQ(uplink_packet_loss_fraction_values.size(), number_of_deltas); // enable_fec const absl::optional enable_fec = proto.has_enable_fec() ? absl::optional(proto.enable_fec()) : absl::optional(); std::vector> enable_fec_values = DecodeDeltas(proto.enable_fec_deltas(), enable_fec, number_of_deltas); RTC_PARSE_CHECK_OR_RETURN_EQ(enable_fec_values.size(), number_of_deltas); // enable_dtx const absl::optional enable_dtx = proto.has_enable_dtx() ? absl::optional(proto.enable_dtx()) : absl::optional(); std::vector> enable_dtx_values = DecodeDeltas(proto.enable_dtx_deltas(), enable_dtx, number_of_deltas); RTC_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 shifted_base_num_channels; if (proto.has_num_channels()) { shifted_base_num_channels = absl::optional(proto.num_channels() - 1); } std::vector> 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( 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(enable_fec_values[i].value()); } if (enable_dtx_values[i].has_value()) { runtime_config.enable_dtx = rtc::checked_cast(enable_dtx_values[i].value()); } if (num_channels_values[i].has_value()) { runtime_config.num_channels = rtc::checked_cast(num_channels_values[i].value()); } audio_network_adaptation_events_.emplace_back( 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