diff --git a/logging/BUILD.gn b/logging/BUILD.gn index a47c81d370..8f81e7a7c9 100644 --- a/logging/BUILD.gn +++ b/logging/BUILD.gn @@ -253,8 +253,8 @@ if (rtc_enable_protobuf) { rtc_static_library("rtc_event_log_parser") { sources = [ - "rtc_event_log/rtc_event_log_parser.cc", - "rtc_event_log/rtc_event_log_parser.h", + "rtc_event_log/rtc_event_log_parser2.cc", + "rtc_event_log/rtc_event_log_parser2.h", ] deps = [ @@ -265,6 +265,7 @@ if (rtc_enable_protobuf) { ":rtc_event_log_proto", ":rtc_stream_config", "..:webrtc_common", + "../api:libjingle_peerconnection_api", "../call:video_stream_api", "../modules/audio_coding:audio_network_adaptor", "../modules/remote_bitrate_estimator:remote_bitrate_estimator", @@ -365,6 +366,7 @@ if (rtc_enable_protobuf) { "../rtc_base:checks", "../rtc_base:protobuf_utils", "../rtc_base:rtc_base_approved", + "../rtc_base:stringutils", # TODO(kwiberg): Remove this dependency. "../api/audio_codecs:audio_codecs_api", diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc index 6d882987d4..0e6e183e0f 100644 --- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc @@ -32,7 +32,7 @@ #include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h" #include "logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h" #include "logging/rtc_event_log/events/rtc_event_video_send_stream_config.h" -#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "logging/rtc_event_log/rtc_event_log_parser2.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" #include "modules/remote_bitrate_estimator/include/bwe_defines.h" #include "modules/rtp_rtcp/source/rtcp_packet/bye.h" // Arbitrary RTCP message. @@ -142,11 +142,11 @@ void RtcEventLogEncoderTest::TestRtcEventAudioNetworkAdaptation( ASSERT_EQ(parsed_log_.GetEventType(0), ParsedRtcEventLog::AUDIO_NETWORK_ADAPTATION_EVENT); - AudioEncoderRuntimeConfig parsed_runtime_config; - parsed_log_.GetAudioNetworkAdaptation(0, &parsed_runtime_config); + LoggedAudioNetworkAdaptationEvent parsed_event = + parsed_log_.GetAudioNetworkAdaptation(0); - EXPECT_EQ(parsed_log_.GetTimestamp(0), timestamp_us); - EXPECT_EQ(parsed_runtime_config, original_runtime_config); + EXPECT_EQ(parsed_event.timestamp_us, timestamp_us); + EXPECT_EQ(parsed_event.config, original_runtime_config); } TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationBitrate) { @@ -234,11 +234,10 @@ TEST_P(RtcEventLogEncoderTest, RtcEventAudioPlayout) { ASSERT_EQ(parsed_log_.GetEventType(0), ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT); - uint32_t parsed_ssrc; - parsed_log_.GetAudioPlayout(0, &parsed_ssrc); + LoggedAudioPlayoutEvent playout_event = parsed_log_.GetAudioPlayout(0); - EXPECT_EQ(parsed_log_.GetTimestamp(0), timestamp_us); - EXPECT_EQ(parsed_ssrc, ssrc); + EXPECT_EQ(playout_event.timestamp_us, timestamp_us); + EXPECT_EQ(playout_event.ssrc, ssrc); } TEST_P(RtcEventLogEncoderTest, RtcEventAudioReceiveStreamConfig) { @@ -333,16 +332,12 @@ TEST_P(RtcEventLogEncoderTest, RtcEventBweUpdateLossBased) { ASSERT_EQ(parsed_log_.GetEventType(0), ParsedRtcEventLog::LOSS_BASED_BWE_UPDATE); - int32_t parsed_bitrate_bps; - uint8_t parsed_fraction_loss; - int32_t parsed_total_packets; - parsed_log_.GetLossBasedBweUpdate( - 0, &parsed_bitrate_bps, &parsed_fraction_loss, &parsed_total_packets); + LoggedBweLossBasedUpdate bwe_update = parsed_log_.GetLossBasedBweUpdate(0); - EXPECT_EQ(parsed_log_.GetTimestamp(0), timestamp_us); - EXPECT_EQ(parsed_bitrate_bps, bitrate_bps); - EXPECT_EQ(parsed_fraction_loss, fraction_loss); - EXPECT_EQ(parsed_total_packets, total_packets); + EXPECT_EQ(bwe_update.timestamp_us, timestamp_us); + EXPECT_EQ(bwe_update.bitrate_bps, bitrate_bps); + EXPECT_EQ(bwe_update.fraction_lost, fraction_loss); + EXPECT_EQ(bwe_update.expected_packets, total_packets); } TEST_P(RtcEventLogEncoderTest, RtcEventLoggingStarted) { diff --git a/logging/rtc_event_log/rtc_event_log.h b/logging/rtc_event_log/rtc_event_log.h index a0714e0bdb..0c71406902 100644 --- a/logging/rtc_event_log/rtc_event_log.h +++ b/logging/rtc_event_log/rtc_event_log.h @@ -21,6 +21,7 @@ namespace webrtc { +// TODO(terelius): Move this to the parser. enum PacketDirection { kIncomingPacket = 0, kOutgoingPacket }; class RtcEventLog { diff --git a/logging/rtc_event_log/rtc_event_log2rtp_dump.cc b/logging/rtc_event_log/rtc_event_log2rtp_dump.cc index c6fa12997b..c4d6948227 100644 --- a/logging/rtc_event_log/rtc_event_log2rtp_dump.cc +++ b/logging/rtc_event_log/rtc_event_log2rtp_dump.cc @@ -16,7 +16,7 @@ #include #include "logging/rtc_event_log/rtc_event_log.h" -#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "logging/rtc_event_log/rtc_event_log_parser2.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/rtp_utility.h" #include "rtc_base/checks.h" diff --git a/logging/rtc_event_log/rtc_event_log2text.cc b/logging/rtc_event_log/rtc_event_log2text.cc index c71a2b8023..6ceeaaf879 100644 --- a/logging/rtc_event_log/rtc_event_log2text.cc +++ b/logging/rtc_event_log/rtc_event_log2text.cc @@ -13,13 +13,12 @@ #include // setfill, setw #include #include -#include #include #include // pair #include "call/video_config.h" #include "common_types.h" // NOLINT(build/include) -#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "logging/rtc_event_log/rtc_event_log_parser2.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" #include "modules/rtp_rtcp/source/rtcp_packet/bye.h" #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" @@ -40,6 +39,7 @@ #include "rtc_base/checks.h" #include "rtc_base/flags.h" #include "rtc_base/logging.h" +#include "rtc_base/strings/string_builder.h" namespace { @@ -443,7 +443,7 @@ int main(int argc, char* argv[]) { size_t total_length; uint8_t header[IP_PACKET_SIZE]; webrtc::PacketDirection direction; - webrtc::RtpHeaderExtensionMap* extension_map = + const webrtc::RtpHeaderExtensionMap* extension_map = parsed_stream.GetRtpHeader(i, &direction, header, &header_length, &total_length, nullptr); @@ -583,10 +583,9 @@ int main(int argc, char* argv[]) { case webrtc::ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT: { if (FLAG_playout) { - uint32_t ssrc; - parsed_stream.GetAudioPlayout(i, &ssrc); - std::cout << parsed_stream.GetTimestamp(i) << "\tAUDIO_PLAYOUT" - << "\tssrc=" << ssrc << std::endl; + auto audio_playout = parsed_stream.GetAudioPlayout(i); + std::cout << audio_playout.log_time_us() << "\tAUDIO_PLAYOUT" + << "\tssrc=" << audio_playout.ssrc << std::endl; } event_recognized = true; break; @@ -594,15 +593,13 @@ int main(int argc, char* argv[]) { case webrtc::ParsedRtcEventLog::LOSS_BASED_BWE_UPDATE: { if (FLAG_bwe) { - int32_t bitrate_bps; - uint8_t fraction_loss; - int32_t total_packets; - parsed_stream.GetLossBasedBweUpdate(i, &bitrate_bps, &fraction_loss, - &total_packets); - std::cout << parsed_stream.GetTimestamp(i) << "\tBWE(LOSS_BASED)" - << "\tbitrate_bps=" << bitrate_bps << "\tfraction_loss=" - << static_cast(fraction_loss) - << "\ttotal_packets=" << total_packets << std::endl; + auto bwe_update = parsed_stream.GetLossBasedBweUpdate(i); + std::cout << bwe_update.log_time_us() << "\tBWE(LOSS_BASED)" + << "\tbitrate_bps=" << bwe_update.bitrate_bps + << "\tfraction_lost=" + << static_cast(bwe_update.fraction_lost) + << "\texpected_packets=" << bwe_update.expected_packets + << std::endl; } event_recognized = true; break; @@ -611,7 +608,7 @@ int main(int argc, char* argv[]) { case webrtc::ParsedRtcEventLog::DELAY_BASED_BWE_UPDATE: { if (FLAG_bwe) { auto bwe_update = parsed_stream.GetDelayBasedBweUpdate(i); - std::cout << parsed_stream.GetTimestamp(i) << "\tBWE(DELAY_BASED)" + std::cout << bwe_update.log_time_us() << "\tBWE(DELAY_BASED)" << "\tbitrate_bps=" << bwe_update.bitrate_bps << "\tdetector_state=" << static_cast(bwe_update.detector_state) << std::endl; @@ -723,30 +720,31 @@ int main(int argc, char* argv[]) { case webrtc::ParsedRtcEventLog::AUDIO_NETWORK_ADAPTATION_EVENT: { if (FLAG_ana) { - webrtc::AudioEncoderRuntimeConfig ana_config; - parsed_stream.GetAudioNetworkAdaptation(i, &ana_config); - std::stringstream ss; - ss << parsed_stream.GetTimestamp(i) << "\tANA_UPDATE"; - if (ana_config.bitrate_bps) { - ss << "\tbitrate_bps=" << *ana_config.bitrate_bps; + auto ana_event = parsed_stream.GetAudioNetworkAdaptation(i); + char buffer[300]; + rtc::SimpleStringBuilder builder(buffer); + builder << parsed_stream.GetTimestamp(i) << "\tANA_UPDATE"; + if (ana_event.config.bitrate_bps) { + builder << "\tbitrate_bps=" << *ana_event.config.bitrate_bps; } - if (ana_config.frame_length_ms) { - ss << "\tframe_length_ms=" << *ana_config.frame_length_ms; + if (ana_event.config.frame_length_ms) { + builder << "\tframe_length_ms=" + << *ana_event.config.frame_length_ms; } - if (ana_config.uplink_packet_loss_fraction) { - ss << "\tuplink_packet_loss_fraction=" - << *ana_config.uplink_packet_loss_fraction; + if (ana_event.config.uplink_packet_loss_fraction) { + builder << "\tuplink_packet_loss_fraction=" + << *ana_event.config.uplink_packet_loss_fraction; } - if (ana_config.enable_fec) { - ss << "\tenable_fec=" << *ana_config.enable_fec; + if (ana_event.config.enable_fec) { + builder << "\tenable_fec=" << *ana_event.config.enable_fec; } - if (ana_config.enable_dtx) { - ss << "\tenable_dtx=" << *ana_config.enable_dtx; + if (ana_event.config.enable_dtx) { + builder << "\tenable_dtx=" << *ana_event.config.enable_dtx; } - if (ana_config.num_channels) { - ss << "\tnum_channels=" << *ana_config.num_channels; + if (ana_event.config.num_channels) { + builder << "\tnum_channels=" << *ana_event.config.num_channels; } - std::cout << ss.str() << std::endl; + std::cout << builder.str() << std::endl; } event_recognized = true; break; @@ -754,8 +752,7 @@ int main(int argc, char* argv[]) { case webrtc::ParsedRtcEventLog::BWE_PROBE_CLUSTER_CREATED_EVENT: { if (FLAG_probe) { - webrtc::ParsedRtcEventLog::BweProbeClusterCreatedEvent probe_event = - parsed_stream.GetBweProbeClusterCreated(i); + auto probe_event = parsed_stream.GetBweProbeClusterCreated(i); std::cout << parsed_stream.GetTimestamp(i) << "\tPROBE_CREATED(" << probe_event.id << ")" << "\tbitrate_bps=" << probe_event.bitrate_bps @@ -768,7 +765,7 @@ int main(int argc, char* argv[]) { case webrtc::ParsedRtcEventLog::BWE_PROBE_RESULT_EVENT: { if (FLAG_probe) { - webrtc::ParsedRtcEventLog::BweProbeResultEvent probe_result = + webrtc::LoggedBweProbeResultEvent probe_result = parsed_stream.GetBweProbeResult(i); if (probe_result.failure_reason) { std::cout << parsed_stream.GetTimestamp(i) << "\tPROBE_SUCCESS(" @@ -789,8 +786,7 @@ int main(int argc, char* argv[]) { case webrtc::ParsedRtcEventLog::ALR_STATE_EVENT: { if (FLAG_bwe) { - webrtc::ParsedRtcEventLog::AlrStateEvent alr_state = - parsed_stream.GetAlrState(i); + webrtc::LoggedAlrStateEvent alr_state = parsed_stream.GetAlrState(i); std::cout << parsed_stream.GetTimestamp(i) << "\tALR_STATE" << "\tin_alr=" << alr_state.in_alr << std::endl; } @@ -800,7 +796,7 @@ int main(int argc, char* argv[]) { case webrtc::ParsedRtcEventLog::ICE_CANDIDATE_PAIR_CONFIG: { if (FLAG_ice) { - webrtc::ParsedRtcEventLog::IceCandidatePairConfig ice_cp_config = + webrtc::LoggedIceCandidatePairConfig ice_cp_config = parsed_stream.GetIceCandidatePairConfig(i); // TODO(qingsi): convert the numeric representation of states to text std::cout << parsed_stream.GetTimestamp(i) @@ -814,7 +810,7 @@ int main(int argc, char* argv[]) { case webrtc::ParsedRtcEventLog::ICE_CANDIDATE_PAIR_EVENT: { if (FLAG_ice) { - webrtc::ParsedRtcEventLog::IceCandidatePairEvent ice_cp_event = + webrtc::LoggedIceCandidatePairEvent ice_cp_event = parsed_stream.GetIceCandidatePairEvent(i); // TODO(qingsi): convert the numeric representation of states to text std::cout << parsed_stream.GetTimestamp(i) diff --git a/logging/rtc_event_log/rtc_event_log_parser2.cc b/logging/rtc_event_log/rtc_event_log_parser2.cc new file mode 100644 index 0000000000..9bcad0dd32 --- /dev/null +++ b/logging/rtc_event_log/rtc_event_log_parser2.cc @@ -0,0 +1,1248 @@ +/* + * 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_parser2.h" + +#include +#include + +#include +#include +#include // no-presubmit-check TODO(webrtc:8982) +#include +#include +#include + +#include "api/rtp_headers.h" +#include "api/rtpparameters.h" +#include "logging/rtc_event_log/rtc_event_log.h" +#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" +#include "modules/remote_bitrate_estimator/include/bwe_defines.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/byte_io.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_utility.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/protobuf_utils.h" +#include "rtc_base/ptr_util.h" + +namespace webrtc { + +namespace { +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_NOTREACHED(); + return RtcpMode::kOff; +} + +ParsedRtcEventLog::EventType GetRuntimeEventType( + rtclog::Event::EventType event_type) { + switch (event_type) { + case rtclog::Event::UNKNOWN_EVENT: + return ParsedRtcEventLog::EventType::UNKNOWN_EVENT; + case rtclog::Event::LOG_START: + return ParsedRtcEventLog::EventType::LOG_START; + case rtclog::Event::LOG_END: + return ParsedRtcEventLog::EventType::LOG_END; + case rtclog::Event::RTP_EVENT: + return ParsedRtcEventLog::EventType::RTP_EVENT; + case rtclog::Event::RTCP_EVENT: + return ParsedRtcEventLog::EventType::RTCP_EVENT; + case rtclog::Event::AUDIO_PLAYOUT_EVENT: + return ParsedRtcEventLog::EventType::AUDIO_PLAYOUT_EVENT; + case rtclog::Event::LOSS_BASED_BWE_UPDATE: + return ParsedRtcEventLog::EventType::LOSS_BASED_BWE_UPDATE; + case rtclog::Event::DELAY_BASED_BWE_UPDATE: + return ParsedRtcEventLog::EventType::DELAY_BASED_BWE_UPDATE; + case rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT: + return ParsedRtcEventLog::EventType::VIDEO_RECEIVER_CONFIG_EVENT; + case rtclog::Event::VIDEO_SENDER_CONFIG_EVENT: + return ParsedRtcEventLog::EventType::VIDEO_SENDER_CONFIG_EVENT; + case rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT: + return ParsedRtcEventLog::EventType::AUDIO_RECEIVER_CONFIG_EVENT; + case rtclog::Event::AUDIO_SENDER_CONFIG_EVENT: + return ParsedRtcEventLog::EventType::AUDIO_SENDER_CONFIG_EVENT; + case rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT: + return ParsedRtcEventLog::EventType::AUDIO_NETWORK_ADAPTATION_EVENT; + case rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT: + return ParsedRtcEventLog::EventType::BWE_PROBE_CLUSTER_CREATED_EVENT; + case rtclog::Event::BWE_PROBE_RESULT_EVENT: + return ParsedRtcEventLog::EventType::BWE_PROBE_RESULT_EVENT; + case rtclog::Event::ALR_STATE_EVENT: + return ParsedRtcEventLog::EventType::ALR_STATE_EVENT; + case rtclog::Event::ICE_CANDIDATE_PAIR_CONFIG: + return ParsedRtcEventLog::EventType::ICE_CANDIDATE_PAIR_CONFIG; + case rtclog::Event::ICE_CANDIDATE_PAIR_EVENT: + return ParsedRtcEventLog::EventType::ICE_CANDIDATE_PAIR_EVENT; + } + return ParsedRtcEventLog::EventType::UNKNOWN_EVENT; +} + +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_NOTREACHED(); + return BandwidthUsage::kBwNormal; +} + +IceCandidatePairEventType GetRuntimeIceCandidatePairConfigType( + rtclog::IceCandidatePairConfig::IceCandidatePairConfigType type) { + switch (type) { + case rtclog::IceCandidatePairConfig::ADDED: + return IceCandidatePairEventType::kAdded; + case rtclog::IceCandidatePairConfig::UPDATED: + return IceCandidatePairEventType::kUpdated; + case rtclog::IceCandidatePairConfig::DESTROYED: + return IceCandidatePairEventType::kDestroyed; + case rtclog::IceCandidatePairConfig::SELECTED: + return IceCandidatePairEventType::kSelected; + } + RTC_NOTREACHED(); + return IceCandidatePairEventType::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_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_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_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_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_NOTREACHED(); + return IceCandidatePairEventType::kCheckSent; +} + +// 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 GetDefaultHeaderExtensionMap() { + webrtc::RtpHeaderExtensionMap default_map; + default_map.Register(webrtc::RtpExtension::kAudioLevelDefaultId); + default_map.Register( + webrtc::RtpExtension::kTimestampOffsetDefaultId); + default_map.Register( + webrtc::RtpExtension::kAbsSendTimeDefaultId); + default_map.Register( + webrtc::RtpExtension::kVideoRotationDefaultId); + default_map.Register( + webrtc::RtpExtension::kVideoContentTypeDefaultId); + default_map.Register( + webrtc::RtpExtension::kVideoTimingDefaultId); + default_map.Register( + webrtc::RtpExtension::kTransportSequenceNumberDefaultId); + default_map.Register( + webrtc::RtpExtension::kPlayoutDelayDefaultId); + return default_map; +} + +std::pair ParseVarInt( + std::istream& stream) { // no-presubmit-check TODO(webrtc:8982) + uint64_t varint = 0; + for (size_t bytes_read = 0; bytes_read < 10; ++bytes_read) { + // The most significant bit of each byte is 0 if it is the last byte in + // the varint and 1 otherwise. Thus, we take the 7 least significant bits + // of each byte and shift them 7 bits for each byte read previously to get + // the (unsigned) integer. + int byte = stream.get(); + if (stream.eof()) { + return std::make_pair(varint, false); + } + RTC_DCHECK_GE(byte, 0); + RTC_DCHECK_LE(byte, 255); + varint |= static_cast(byte & 0x7F) << (7 * bytes_read); + if ((byte & 0x80) == 0) { + return std::make_pair(varint, true); + } + } + return std::make_pair(varint, false); +} + +void GetHeaderExtensions(std::vector* header_extensions, + const RepeatedPtrField& + proto_header_extensions) { + header_extensions->clear(); + for (auto& p : proto_header_extensions) { + RTC_CHECK(p.has_name()); + RTC_CHECK(p.has_id()); + const std::string& name = p.name(); + int id = p.id(); + header_extensions->push_back(RtpExtension(name, id)); + } +} + +} // namespace + +ParsedRtcEventLog::ParsedRtcEventLog( + UnconfiguredHeaderExtensions parse_unconfigured_header_extensions) + : parse_unconfigured_header_extensions_( + parse_unconfigured_header_extensions) { + Clear(); +} + +void ParsedRtcEventLog::Clear() { + events_.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(); + + start_log_events_.clear(); + stop_log_events_.clear(); + audio_playout_events_.clear(); + audio_network_adaptation_events_.clear(); + bwe_probe_cluster_created_events_.clear(); + bwe_probe_result_events_.clear(); + bwe_delay_updates_.clear(); + bwe_loss_updates_.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(); + + memset(last_incoming_rtcp_packet_, 0, IP_PACKET_SIZE); + last_incoming_rtcp_packet_length_ = 0; + + first_timestamp_ = std::numeric_limits::max(); + last_timestamp_ = std::numeric_limits::min(); + + incoming_rtp_extensions_maps_.clear(); + outgoing_rtp_extensions_maps_.clear(); +} + +bool ParsedRtcEventLog::ParseFile(const std::string& filename) { + std::ifstream file( // no-presubmit-check TODO(webrtc:8982) + filename, std::ios_base::in | std::ios_base::binary); + if (!file.good() || !file.is_open()) { + RTC_LOG(LS_WARNING) << "Could not open file for reading."; + return false; + } + + return ParseStream(file); +} + +bool ParsedRtcEventLog::ParseString(const std::string& s) { + std::istringstream stream( // no-presubmit-check TODO(webrtc:8982) + s, std::ios_base::in | std::ios_base::binary); + return ParseStream(stream); +} + +bool ParsedRtcEventLog::ParseStream( + std::istream& stream) { // no-presubmit-check TODO(webrtc:8982) + Clear(); + const size_t kMaxEventSize = (1u << 16) - 1; + std::vector tmp_buffer(kMaxEventSize); + uint64_t tag; + uint64_t message_length; + bool success; + + RTC_DCHECK(stream.good()); + + while (1) { + // Check whether we have reached end of file. + stream.peek(); + if (stream.eof()) { + break; + } + + // Read the next message tag. The tag number is defined as + // (fieldnumber << 3) | wire_type. In our case, the field number is + // supposed to be 1 and the wire type for an + // length-delimited field is 2. + const uint64_t kExpectedTag = (1 << 3) | 2; + std::tie(tag, success) = ParseVarInt(stream); + if (!success) { + RTC_LOG(LS_WARNING) + << "Missing field tag from beginning of protobuf event."; + return false; + } else if (tag != kExpectedTag) { + RTC_LOG(LS_WARNING) + << "Unexpected field tag at beginning of protobuf event."; + return false; + } + + // Read the length field. + std::tie(message_length, success) = ParseVarInt(stream); + if (!success) { + RTC_LOG(LS_WARNING) << "Missing message length after protobuf field tag."; + return false; + } else if (message_length > kMaxEventSize) { + RTC_LOG(LS_WARNING) << "Protobuf message length is too large."; + return false; + } + + // Read the next protobuf event to a temporary char buffer. + stream.read(tmp_buffer.data(), message_length); + if (stream.gcount() != static_cast(message_length)) { + RTC_LOG(LS_WARNING) << "Failed to read protobuf message from file."; + return false; + } + + // Parse the protobuf event from the buffer. + rtclog::Event event; + if (!event.ParseFromArray(tmp_buffer.data(), message_length)) { + RTC_LOG(LS_WARNING) << "Failed to parse protobuf message."; + return false; + } + + StoreParsedEvent(event); + + events_.push_back(event); + } + + // Move packets_streams from map to vector. + incoming_rtp_packets_by_ssrc_.reserve(incoming_rtp_packets_map_.size()); + for (const 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); + } + outgoing_rtp_packets_by_ssrc_.reserve(outgoing_rtp_packets_map_.size()); + for (const 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); + } + + // 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.data(), + stream.incoming_packets.size())); + } + for (const auto& stream : outgoing_rtp_packets_by_ssrc_) { + outgoing_rtp_packet_views_by_ssrc_.emplace_back( + LoggedRtpStreamView(stream.ssrc, stream.outgoing_packets.data(), + stream.outgoing_packets.size())); + } + + return true; +} + +void ParsedRtcEventLog::StoreParsedEvent(const rtclog::Event& event) { + if (event.type() != rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT && + event.type() != rtclog::Event::VIDEO_SENDER_CONFIG_EVENT && + event.type() != rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT && + event.type() != rtclog::Event::AUDIO_SENDER_CONFIG_EVENT && + event.type() != rtclog::Event::LOG_START && + event.type() != rtclog::Event::LOG_END) { + RTC_CHECK(event.has_timestamp_us()); + int64_t timestamp = event.timestamp_us(); + first_timestamp_ = std::min(first_timestamp_, timestamp); + last_timestamp_ = std::max(last_timestamp_, timestamp); + } + + switch (event.type()) { + case rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT: { + rtclog::StreamConfig config = GetVideoReceiveConfig(event); + video_recv_configs_.emplace_back(GetTimestamp(event), config); + incoming_rtp_extensions_maps_[config.remote_ssrc] = + RtpHeaderExtensionMap(config.rtp_extensions); + // TODO(terelius): I don't understand the reason for configuring header + // extensions for the local SSRC. I think it should be removed, but for + // now I want to preserve the previous functionality. + incoming_rtp_extensions_maps_[config.local_ssrc] = + RtpHeaderExtensionMap(config.rtp_extensions); + incoming_video_ssrcs_.insert(config.remote_ssrc); + incoming_video_ssrcs_.insert(config.rtx_ssrc); + incoming_rtx_ssrcs_.insert(config.rtx_ssrc); + break; + } + case rtclog::Event::VIDEO_SENDER_CONFIG_EVENT: { + std::vector configs = GetVideoSendConfig(event); + video_send_configs_.emplace_back(GetTimestamp(event), configs); + for (const auto& config : configs) { + outgoing_rtp_extensions_maps_[config.local_ssrc] = + RtpHeaderExtensionMap(config.rtp_extensions); + outgoing_rtp_extensions_maps_[config.rtx_ssrc] = + RtpHeaderExtensionMap(config.rtp_extensions); + outgoing_video_ssrcs_.insert(config.local_ssrc); + outgoing_video_ssrcs_.insert(config.rtx_ssrc); + outgoing_rtx_ssrcs_.insert(config.rtx_ssrc); + } + break; + } + case rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT: { + rtclog::StreamConfig config = GetAudioReceiveConfig(event); + audio_recv_configs_.emplace_back(GetTimestamp(event), config); + incoming_rtp_extensions_maps_[config.remote_ssrc] = + RtpHeaderExtensionMap(config.rtp_extensions); + incoming_rtp_extensions_maps_[config.local_ssrc] = + RtpHeaderExtensionMap(config.rtp_extensions); + incoming_audio_ssrcs_.insert(config.remote_ssrc); + break; + } + case rtclog::Event::AUDIO_SENDER_CONFIG_EVENT: { + rtclog::StreamConfig config = GetAudioSendConfig(event); + audio_send_configs_.emplace_back(GetTimestamp(event), config); + outgoing_rtp_extensions_maps_[config.local_ssrc] = + RtpHeaderExtensionMap(config.rtp_extensions); + outgoing_audio_ssrcs_.insert(config.local_ssrc); + break; + } + case rtclog::Event::RTP_EVENT: { + PacketDirection direction; + uint8_t header[IP_PACKET_SIZE]; + size_t header_length; + size_t total_length; + const RtpHeaderExtensionMap* extension_map = GetRtpHeader( + event, &direction, header, &header_length, &total_length, nullptr); + RtpUtility::RtpHeaderParser rtp_parser(header, header_length); + RTPHeader parsed_header; + if (extension_map != nullptr) { + rtp_parser.Parse(&parsed_header, extension_map); + } else { + // Use the default extension map. + // TODO(ivoc): Once configuration of audio streams is stored in the + // event log, this can be removed. + // Tracking bug: webrtc:6399 + rtp_parser.Parse(&parsed_header, &default_extension_map_); + } + RTC_CHECK(event.has_timestamp_us()); + uint64_t timestamp_us = event.timestamp_us(); + if (direction == kIncomingPacket) { + incoming_rtp_packets_map_[parsed_header.ssrc].push_back( + LoggedRtpPacketIncoming(timestamp_us, parsed_header, header_length, + total_length)); + } else { + outgoing_rtp_packets_map_[parsed_header.ssrc].push_back( + LoggedRtpPacketOutgoing(timestamp_us, parsed_header, header_length, + total_length)); + } + break; + } + case rtclog::Event::RTCP_EVENT: { + PacketDirection direction; + uint8_t packet[IP_PACKET_SIZE]; + size_t total_length; + GetRtcpPacket(event, &direction, packet, &total_length); + uint64_t timestamp_us = GetTimestamp(event); + RTC_CHECK_LE(total_length, IP_PACKET_SIZE); + 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 (total_length == last_incoming_rtcp_packet_length_ && + memcmp(last_incoming_rtcp_packet_, packet, total_length) == 0) + break; + incoming_rtcp_packets_.push_back( + LoggedRtcpPacketIncoming(timestamp_us, packet, total_length)); + last_incoming_rtcp_packet_length_ = total_length; + memcpy(last_incoming_rtcp_packet_, packet, total_length); + } else { + outgoing_rtcp_packets_.push_back( + LoggedRtcpPacketOutgoing(timestamp_us, packet, total_length)); + } + rtcp::CommonHeader header; + const uint8_t* packet_end = packet + total_length; + for (const uint8_t* block = packet; block < packet_end; + block = header.NextPacket()) { + RTC_CHECK(header.Parse(block, packet_end - block)); + if (header.type() == rtcp::TransportFeedback::kPacketType && + header.fmt() == rtcp::TransportFeedback::kFeedbackMessageType) { + if (direction == kIncomingPacket) { + incoming_transport_feedback_.emplace_back(); + LoggedRtcpPacketTransportFeedback& parsed_block = + incoming_transport_feedback_.back(); + parsed_block.timestamp_us = GetTimestamp(event); + if (!parsed_block.transport_feedback.Parse(header)) + incoming_transport_feedback_.pop_back(); + } else { + outgoing_transport_feedback_.emplace_back(); + LoggedRtcpPacketTransportFeedback& parsed_block = + outgoing_transport_feedback_.back(); + parsed_block.timestamp_us = GetTimestamp(event); + if (!parsed_block.transport_feedback.Parse(header)) + outgoing_transport_feedback_.pop_back(); + } + } else if (header.type() == rtcp::SenderReport::kPacketType) { + LoggedRtcpPacketSenderReport parsed_block; + parsed_block.timestamp_us = GetTimestamp(event); + if (parsed_block.sr.Parse(header)) { + if (direction == kIncomingPacket) + incoming_sr_.push_back(std::move(parsed_block)); + else + outgoing_sr_.push_back(std::move(parsed_block)); + } + } else if (header.type() == rtcp::ReceiverReport::kPacketType) { + LoggedRtcpPacketReceiverReport parsed_block; + parsed_block.timestamp_us = GetTimestamp(event); + if (parsed_block.rr.Parse(header)) { + if (direction == kIncomingPacket) + incoming_rr_.push_back(std::move(parsed_block)); + else + outgoing_rr_.push_back(std::move(parsed_block)); + } + } else if (header.type() == rtcp::Remb::kPacketType && + header.fmt() == rtcp::Remb::kFeedbackMessageType) { + LoggedRtcpPacketRemb parsed_block; + parsed_block.timestamp_us = GetTimestamp(event); + if (parsed_block.remb.Parse(header)) { + if (direction == kIncomingPacket) + incoming_remb_.push_back(std::move(parsed_block)); + else + outgoing_remb_.push_back(std::move(parsed_block)); + } + } else if (header.type() == rtcp::Nack::kPacketType && + header.fmt() == rtcp::Nack::kFeedbackMessageType) { + LoggedRtcpPacketNack parsed_block; + parsed_block.timestamp_us = GetTimestamp(event); + if (parsed_block.nack.Parse(header)) { + if (direction == kIncomingPacket) + incoming_nack_.push_back(std::move(parsed_block)); + else + outgoing_nack_.push_back(std::move(parsed_block)); + } + } + } + break; + } + case ParsedRtcEventLog::LOG_START: { + start_log_events_.push_back(LoggedStartEvent(GetTimestamp(event))); + break; + } + case ParsedRtcEventLog::LOG_END: { + stop_log_events_.push_back(LoggedStopEvent(GetTimestamp(event))); + break; + } + case ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT: { + LoggedAudioPlayoutEvent playout_event = GetAudioPlayout(event); + audio_playout_events_[playout_event.ssrc].push_back( + playout_event.timestamp_us); + break; + } + case ParsedRtcEventLog::LOSS_BASED_BWE_UPDATE: { + bwe_loss_updates_.push_back(GetLossBasedBweUpdate(event)); + break; + } + case ParsedRtcEventLog::DELAY_BASED_BWE_UPDATE: { + bwe_delay_updates_.push_back(GetDelayBasedBweUpdate(event)); + break; + } + case ParsedRtcEventLog::AUDIO_NETWORK_ADAPTATION_EVENT: { + LoggedAudioNetworkAdaptationEvent ana_event = + GetAudioNetworkAdaptation(event); + audio_network_adaptation_events_.push_back(ana_event); + break; + } + case ParsedRtcEventLog::BWE_PROBE_CLUSTER_CREATED_EVENT: { + bwe_probe_cluster_created_events_.push_back( + GetBweProbeClusterCreated(event)); + break; + } + case ParsedRtcEventLog::BWE_PROBE_RESULT_EVENT: { + bwe_probe_result_events_.push_back(GetBweProbeResult(event)); + break; + } + case ParsedRtcEventLog::ALR_STATE_EVENT: { + alr_state_events_.push_back(GetAlrState(event)); + break; + } + case ParsedRtcEventLog::ICE_CANDIDATE_PAIR_CONFIG: { + ice_candidate_pair_configs_.push_back(GetIceCandidatePairConfig(event)); + break; + } + case ParsedRtcEventLog::ICE_CANDIDATE_PAIR_EVENT: { + ice_candidate_pair_events_.push_back(GetIceCandidatePairEvent(event)); + break; + } + case ParsedRtcEventLog::UNKNOWN_EVENT: { + break; + } + } +} + +size_t ParsedRtcEventLog::GetNumberOfEvents() const { + return events_.size(); +} + +int64_t ParsedRtcEventLog::GetTimestamp(size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& event = events_[index]; + return GetTimestamp(event); +} + +int64_t ParsedRtcEventLog::GetTimestamp(const rtclog::Event& event) const { + RTC_CHECK(event.has_timestamp_us()); + return event.timestamp_us(); +} + +ParsedRtcEventLog::EventType ParsedRtcEventLog::GetEventType( + size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& event = events_[index]; + RTC_CHECK(event.has_type()); + return GetRuntimeEventType(event.type()); +} + +// The header must have space for at least IP_PACKET_SIZE bytes. +const webrtc::RtpHeaderExtensionMap* ParsedRtcEventLog::GetRtpHeader( + size_t index, + PacketDirection* incoming, + uint8_t* header, + size_t* header_length, + size_t* total_length, + int* probe_cluster_id) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& event = events_[index]; + return GetRtpHeader(event, incoming, header, header_length, total_length, + probe_cluster_id); +} + +const webrtc::RtpHeaderExtensionMap* ParsedRtcEventLog::GetRtpHeader( + const rtclog::Event& event, + PacketDirection* incoming, + uint8_t* header, + size_t* header_length, + size_t* total_length, + int* probe_cluster_id) const { + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::RTP_EVENT); + RTC_CHECK(event.has_rtp_packet()); + const rtclog::RtpPacket& rtp_packet = event.rtp_packet(); + // Get direction of packet. + RTC_CHECK(rtp_packet.has_incoming()); + if (incoming != nullptr) { + *incoming = rtp_packet.incoming() ? kIncomingPacket : kOutgoingPacket; + } + // Get packet length. + RTC_CHECK(rtp_packet.has_packet_length()); + if (total_length != nullptr) { + *total_length = rtp_packet.packet_length(); + } + // Get header length. + RTC_CHECK(rtp_packet.has_header()); + if (header_length != nullptr) { + *header_length = rtp_packet.header().size(); + } + if (probe_cluster_id != nullptr) { + if (rtp_packet.has_probe_cluster_id()) { + *probe_cluster_id = rtp_packet.probe_cluster_id(); + RTC_CHECK_NE(*probe_cluster_id, PacedPacketInfo::kNotAProbe); + } else { + *probe_cluster_id = PacedPacketInfo::kNotAProbe; + } + } + // Get header contents. + if (header != nullptr) { + const size_t kMinRtpHeaderSize = 12; + RTC_CHECK_GE(rtp_packet.header().size(), kMinRtpHeaderSize); + RTC_CHECK_LE(rtp_packet.header().size(), + static_cast(IP_PACKET_SIZE)); + memcpy(header, rtp_packet.header().data(), rtp_packet.header().size()); + uint32_t ssrc = ByteReader::ReadBigEndian(header + 8); + auto& extensions_maps = rtp_packet.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_LOG(LS_WARNING) << "Using default header extension map for SSRC " + << ssrc; + extensions_maps.insert(std::make_pair(ssrc, default_extension_map_)); + return &default_extension_map_; + } + } + return nullptr; +} + +// The packet must have space for at least IP_PACKET_SIZE bytes. +void ParsedRtcEventLog::GetRtcpPacket(size_t index, + PacketDirection* incoming, + uint8_t* packet, + size_t* length) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& event = events_[index]; + GetRtcpPacket(event, incoming, packet, length); +} + +void ParsedRtcEventLog::GetRtcpPacket(const rtclog::Event& event, + PacketDirection* incoming, + uint8_t* packet, + size_t* length) const { + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::RTCP_EVENT); + RTC_CHECK(event.has_rtcp_packet()); + const rtclog::RtcpPacket& rtcp_packet = event.rtcp_packet(); + // Get direction of packet. + RTC_CHECK(rtcp_packet.has_incoming()); + if (incoming != nullptr) { + *incoming = rtcp_packet.incoming() ? kIncomingPacket : kOutgoingPacket; + } + // Get packet length. + RTC_CHECK(rtcp_packet.has_packet_data()); + if (length != nullptr) { + *length = rtcp_packet.packet_data().size(); + } + // Get packet contents. + if (packet != nullptr) { + RTC_CHECK_LE(rtcp_packet.packet_data().size(), + static_cast(IP_PACKET_SIZE)); + memcpy(packet, rtcp_packet.packet_data().data(), + rtcp_packet.packet_data().size()); + } +} + +rtclog::StreamConfig ParsedRtcEventLog::GetVideoReceiveConfig( + size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + return GetVideoReceiveConfig(events_[index]); +} + +rtclog::StreamConfig ParsedRtcEventLog::GetVideoReceiveConfig( + const rtclog::Event& event) const { + rtclog::StreamConfig config; + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT); + RTC_CHECK(event.has_video_receiver_config()); + const rtclog::VideoReceiveConfig& receiver_config = + event.video_receiver_config(); + // Get SSRCs. + RTC_CHECK(receiver_config.has_remote_ssrc()); + config.remote_ssrc = receiver_config.remote_ssrc(); + RTC_CHECK(receiver_config.has_local_ssrc()); + config.local_ssrc = receiver_config.local_ssrc(); + config.rtx_ssrc = 0; + // Get RTCP settings. + RTC_CHECK(receiver_config.has_rtcp_mode()); + config.rtcp_mode = GetRuntimeRtcpMode(receiver_config.rtcp_mode()); + RTC_CHECK(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_CHECK(map.has_payload_type()); + RTC_CHECK(map.has_config()); + RTC_CHECK(map.config().has_rtx_ssrc()); + RTC_CHECK(map.config().has_rtx_payload_type()); + rtx_map.insert(std::make_pair(map.payload_type(), map.config())); + } + + // Get header extensions. + GetHeaderExtensions(&config.rtp_extensions, + receiver_config.header_extensions()); + // Get decoders. + config.codecs.clear(); + for (int i = 0; i < receiver_config.decoders_size(); i++) { + RTC_CHECK(receiver_config.decoders(i).has_name()); + RTC_CHECK(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; +} + +std::vector ParsedRtcEventLog::GetVideoSendConfig( + size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + return GetVideoSendConfig(events_[index]); +} + +std::vector ParsedRtcEventLog::GetVideoSendConfig( + const rtclog::Event& event) const { + std::vector configs; + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::VIDEO_SENDER_CONFIG_EVENT); + RTC_CHECK(event.has_video_sender_config()); + const rtclog::VideoSendConfig& sender_config = event.video_sender_config(); + if (sender_config.rtx_ssrcs_size() > 0 && + sender_config.ssrcs_size() != sender_config.rtx_ssrcs_size()) { + RTC_LOG(WARNING) + << "VideoSendConfig is configured for RTX but the number of " + "SSRCs doesn't match the number of RTX SSRCs."; + } + configs.resize(sender_config.ssrcs_size()); + for (int i = 0; i < sender_config.ssrcs_size(); i++) { + // Get SSRCs. + configs[i].local_ssrc = sender_config.ssrcs(i); + if (sender_config.rtx_ssrcs_size() > 0 && + i < sender_config.rtx_ssrcs_size()) { + RTC_CHECK(sender_config.has_rtx_payload_type()); + configs[i].rtx_ssrc = sender_config.rtx_ssrcs(i); + } + // Get header extensions. + GetHeaderExtensions(&configs[i].rtp_extensions, + sender_config.header_extensions()); + + // Get the codec. + RTC_CHECK(sender_config.has_encoder()); + RTC_CHECK(sender_config.encoder().has_name()); + RTC_CHECK(sender_config.encoder().has_payload_type()); + configs[i].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 configs; +} + +rtclog::StreamConfig ParsedRtcEventLog::GetAudioReceiveConfig( + size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + return GetAudioReceiveConfig(events_[index]); +} + +rtclog::StreamConfig ParsedRtcEventLog::GetAudioReceiveConfig( + const rtclog::Event& event) const { + rtclog::StreamConfig config; + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT); + RTC_CHECK(event.has_audio_receiver_config()); + const rtclog::AudioReceiveConfig& receiver_config = + event.audio_receiver_config(); + // Get SSRCs. + RTC_CHECK(receiver_config.has_remote_ssrc()); + config.remote_ssrc = receiver_config.remote_ssrc(); + RTC_CHECK(receiver_config.has_local_ssrc()); + config.local_ssrc = receiver_config.local_ssrc(); + // Get header extensions. + GetHeaderExtensions(&config.rtp_extensions, + receiver_config.header_extensions()); + return config; +} + +rtclog::StreamConfig ParsedRtcEventLog::GetAudioSendConfig(size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + return GetAudioSendConfig(events_[index]); +} + +rtclog::StreamConfig ParsedRtcEventLog::GetAudioSendConfig( + const rtclog::Event& event) const { + rtclog::StreamConfig config; + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::AUDIO_SENDER_CONFIG_EVENT); + RTC_CHECK(event.has_audio_sender_config()); + const rtclog::AudioSendConfig& sender_config = event.audio_sender_config(); + // Get SSRCs. + RTC_CHECK(sender_config.has_ssrc()); + config.local_ssrc = sender_config.ssrc(); + // Get header extensions. + GetHeaderExtensions(&config.rtp_extensions, + sender_config.header_extensions()); + return config; +} + +LoggedAudioPlayoutEvent ParsedRtcEventLog::GetAudioPlayout(size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& event = events_[index]; + return GetAudioPlayout(event); +} + +LoggedAudioPlayoutEvent ParsedRtcEventLog::GetAudioPlayout( + const rtclog::Event& event) const { + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::AUDIO_PLAYOUT_EVENT); + RTC_CHECK(event.has_audio_playout_event()); + const rtclog::AudioPlayoutEvent& playout_event = event.audio_playout_event(); + LoggedAudioPlayoutEvent res; + res.timestamp_us = GetTimestamp(event); + RTC_CHECK(playout_event.has_local_ssrc()); + res.ssrc = playout_event.local_ssrc(); + return res; +} + +LoggedBweLossBasedUpdate ParsedRtcEventLog::GetLossBasedBweUpdate( + size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& event = events_[index]; + return GetLossBasedBweUpdate(event); +} + +LoggedBweLossBasedUpdate ParsedRtcEventLog::GetLossBasedBweUpdate( + const rtclog::Event& event) const { + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::LOSS_BASED_BWE_UPDATE); + RTC_CHECK(event.has_loss_based_bwe_update()); + const rtclog::LossBasedBweUpdate& loss_event = event.loss_based_bwe_update(); + + LoggedBweLossBasedUpdate bwe_update; + bwe_update.timestamp_us = GetTimestamp(event); + RTC_CHECK(loss_event.has_bitrate_bps()); + bwe_update.bitrate_bps = loss_event.bitrate_bps(); + RTC_CHECK(loss_event.has_fraction_loss()); + bwe_update.fraction_lost = loss_event.fraction_loss(); + RTC_CHECK(loss_event.has_total_packets()); + bwe_update.expected_packets = loss_event.total_packets(); + return bwe_update; +} + +LoggedBweDelayBasedUpdate ParsedRtcEventLog::GetDelayBasedBweUpdate( + size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& event = events_[index]; + return GetDelayBasedBweUpdate(event); +} + +LoggedBweDelayBasedUpdate ParsedRtcEventLog::GetDelayBasedBweUpdate( + const rtclog::Event& event) const { + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::DELAY_BASED_BWE_UPDATE); + RTC_CHECK(event.has_delay_based_bwe_update()); + const rtclog::DelayBasedBweUpdate& delay_event = + event.delay_based_bwe_update(); + + LoggedBweDelayBasedUpdate res; + res.timestamp_us = GetTimestamp(event); + RTC_CHECK(delay_event.has_bitrate_bps()); + res.bitrate_bps = delay_event.bitrate_bps(); + RTC_CHECK(delay_event.has_detector_state()); + res.detector_state = GetRuntimeDetectorState(delay_event.detector_state()); + return res; +} + +LoggedAudioNetworkAdaptationEvent ParsedRtcEventLog::GetAudioNetworkAdaptation( + size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& event = events_[index]; + return GetAudioNetworkAdaptation(event); +} + +LoggedAudioNetworkAdaptationEvent ParsedRtcEventLog::GetAudioNetworkAdaptation( + const rtclog::Event& event) const { + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT); + RTC_CHECK(event.has_audio_network_adaptation()); + const rtclog::AudioNetworkAdaptation& ana_event = + event.audio_network_adaptation(); + + LoggedAudioNetworkAdaptationEvent res; + res.timestamp_us = GetTimestamp(event); + 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; +} + +LoggedBweProbeClusterCreatedEvent ParsedRtcEventLog::GetBweProbeClusterCreated( + size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& event = events_[index]; + return GetBweProbeClusterCreated(event); +} + +LoggedBweProbeClusterCreatedEvent ParsedRtcEventLog::GetBweProbeClusterCreated( + const rtclog::Event& event) const { + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT); + RTC_CHECK(event.has_probe_cluster()); + const rtclog::BweProbeCluster& pcc_event = event.probe_cluster(); + LoggedBweProbeClusterCreatedEvent res; + res.timestamp_us = GetTimestamp(event); + RTC_CHECK(pcc_event.has_id()); + res.id = pcc_event.id(); + RTC_CHECK(pcc_event.has_bitrate_bps()); + res.bitrate_bps = pcc_event.bitrate_bps(); + RTC_CHECK(pcc_event.has_min_packets()); + res.min_packets = pcc_event.min_packets(); + RTC_CHECK(pcc_event.has_min_bytes()); + res.min_bytes = pcc_event.min_bytes(); + return res; +} + +LoggedBweProbeResultEvent ParsedRtcEventLog::GetBweProbeResult( + size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& event = events_[index]; + return GetBweProbeResult(event); +} + +LoggedBweProbeResultEvent ParsedRtcEventLog::GetBweProbeResult( + const rtclog::Event& event) const { + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::BWE_PROBE_RESULT_EVENT); + RTC_CHECK(event.has_probe_result()); + const rtclog::BweProbeResult& pr_event = event.probe_result(); + LoggedBweProbeResultEvent res; + res.timestamp_us = GetTimestamp(event); + RTC_CHECK(pr_event.has_id()); + res.id = pr_event.id(); + + RTC_CHECK(pr_event.has_result()); + if (pr_event.result() == rtclog::BweProbeResult::SUCCESS) { + RTC_CHECK(pr_event.has_bitrate_bps()); + res.bitrate_bps = pr_event.bitrate_bps(); + } else 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_NOTREACHED(); + } + + return res; +} + +LoggedAlrStateEvent ParsedRtcEventLog::GetAlrState(size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& event = events_[index]; + return GetAlrState(event); +} + +LoggedAlrStateEvent ParsedRtcEventLog::GetAlrState( + const rtclog::Event& event) const { + RTC_CHECK(event.has_type()); + RTC_CHECK_EQ(event.type(), rtclog::Event::ALR_STATE_EVENT); + RTC_CHECK(event.has_alr_state()); + const rtclog::AlrState& alr_event = event.alr_state(); + LoggedAlrStateEvent res; + res.timestamp_us = GetTimestamp(event); + RTC_CHECK(alr_event.has_in_alr()); + res.in_alr = alr_event.in_alr(); + + return res; +} + +LoggedIceCandidatePairConfig ParsedRtcEventLog::GetIceCandidatePairConfig( + size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& rtc_event = events_[index]; + return GetIceCandidatePairConfig(rtc_event); +} + +LoggedIceCandidatePairConfig ParsedRtcEventLog::GetIceCandidatePairConfig( + const rtclog::Event& rtc_event) const { + RTC_CHECK(rtc_event.has_type()); + RTC_CHECK_EQ(rtc_event.type(), rtclog::Event::ICE_CANDIDATE_PAIR_CONFIG); + LoggedIceCandidatePairConfig res; + const rtclog::IceCandidatePairConfig& config = + rtc_event.ice_candidate_pair_config(); + res.timestamp_us = GetTimestamp(rtc_event); + RTC_CHECK(config.has_config_type()); + res.type = GetRuntimeIceCandidatePairConfigType(config.config_type()); + RTC_CHECK(config.has_candidate_pair_id()); + res.candidate_pair_id = config.candidate_pair_id(); + RTC_CHECK(config.has_local_candidate_type()); + res.local_candidate_type = + GetRuntimeIceCandidateType(config.local_candidate_type()); + RTC_CHECK(config.has_local_relay_protocol()); + res.local_relay_protocol = + GetRuntimeIceCandidatePairProtocol(config.local_relay_protocol()); + RTC_CHECK(config.has_local_network_type()); + res.local_network_type = + GetRuntimeIceCandidateNetworkType(config.local_network_type()); + RTC_CHECK(config.has_local_address_family()); + res.local_address_family = + GetRuntimeIceCandidatePairAddressFamily(config.local_address_family()); + RTC_CHECK(config.has_remote_candidate_type()); + res.remote_candidate_type = + GetRuntimeIceCandidateType(config.remote_candidate_type()); + RTC_CHECK(config.has_remote_address_family()); + res.remote_address_family = + GetRuntimeIceCandidatePairAddressFamily(config.remote_address_family()); + RTC_CHECK(config.has_candidate_pair_protocol()); + res.candidate_pair_protocol = + GetRuntimeIceCandidatePairProtocol(config.candidate_pair_protocol()); + return res; +} + +LoggedIceCandidatePairEvent ParsedRtcEventLog::GetIceCandidatePairEvent( + size_t index) const { + RTC_CHECK_LT(index, GetNumberOfEvents()); + const rtclog::Event& rtc_event = events_[index]; + return GetIceCandidatePairEvent(rtc_event); +} + +LoggedIceCandidatePairEvent ParsedRtcEventLog::GetIceCandidatePairEvent( + const rtclog::Event& rtc_event) const { + RTC_CHECK(rtc_event.has_type()); + RTC_CHECK_EQ(rtc_event.type(), rtclog::Event::ICE_CANDIDATE_PAIR_EVENT); + LoggedIceCandidatePairEvent res; + const rtclog::IceCandidatePairEvent& event = + rtc_event.ice_candidate_pair_event(); + res.timestamp_us = GetTimestamp(rtc_event); + RTC_CHECK(event.has_event_type()); + res.type = GetRuntimeIceCandidatePairEventType(event.event_type()); + RTC_CHECK(event.has_candidate_pair_id()); + res.candidate_pair_id = event.candidate_pair_id(); + 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; +} + +} // namespace webrtc diff --git a/logging/rtc_event_log/rtc_event_log_parser2.h b/logging/rtc_event_log/rtc_event_log_parser2.h new file mode 100644 index 0000000000..dfc607d618 --- /dev/null +++ b/logging/rtc_event_log/rtc_event_log_parser2.h @@ -0,0 +1,920 @@ +/* + * 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. + */ +#ifndef LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_PARSER2_H_ +#define LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_PARSER2_H_ + +#include +#include +#include +#include +#include // pair +#include + +#include "call/video_receive_stream.h" +#include "call/video_send_stream.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" +#include "logging/rtc_event_log/events/rtc_event_probe_result_failure.h" +#include "logging/rtc_event_log/rtc_event_log.h" +#include "logging/rtc_event_log/rtc_stream_config.h" +#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" +#include "modules/rtp_rtcp/source/rtcp_packet/nack.h" +#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/remb.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" +#include "rtc_base/ignore_wundef.h" + +// Files generated at build-time by the protobuf compiler. +RTC_PUSH_IGNORING_WUNDEF() +#ifdef WEBRTC_ANDROID_PLATFORM_BUILD +#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log.pb.h" +#else +#include "logging/rtc_event_log/rtc_event_log.pb.h" +#endif +RTC_POP_IGNORING_WUNDEF() + +namespace webrtc { + +enum class BandwidthUsage; +struct AudioEncoderRuntimeConfig; + +struct LoggedAlrStateEvent { + int64_t timestamp_us; + bool in_alr; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedAudioPlayoutEvent { + int64_t timestamp_us; + uint32_t ssrc; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedAudioNetworkAdaptationEvent { + int64_t timestamp_us; + AudioEncoderRuntimeConfig config; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedBweDelayBasedUpdate { + int64_t timestamp_us; + int32_t bitrate_bps; + BandwidthUsage detector_state; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedBweLossBasedUpdate { + int64_t timestamp_us; + int32_t bitrate_bps; + uint8_t fraction_lost; + int32_t expected_packets; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedBweProbeClusterCreatedEvent { + int64_t timestamp_us; + uint32_t id; + uint64_t bitrate_bps; + uint32_t min_packets; + uint32_t min_bytes; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedBweProbeResultEvent { + int64_t timestamp_us; + uint32_t id; + rtc::Optional bitrate_bps; + rtc::Optional failure_reason; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedIceCandidatePairConfig { + int64_t timestamp_us; + IceCandidatePairEventType type; + uint32_t candidate_pair_id; + IceCandidateType local_candidate_type; + IceCandidatePairProtocol local_relay_protocol; + IceCandidateNetworkType local_network_type; + IceCandidatePairAddressFamily local_address_family; + IceCandidateType remote_candidate_type; + IceCandidatePairAddressFamily remote_address_family; + IceCandidatePairProtocol candidate_pair_protocol; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedIceCandidatePairEvent { + int64_t timestamp_us; + IceCandidatePairEventType type; + uint32_t candidate_pair_id; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedRtpPacket { + LoggedRtpPacket(uint64_t timestamp_us, + RTPHeader header, + size_t header_length, + size_t total_length) + : timestamp_us(timestamp_us), + header(header), + header_length(header_length), + total_length(total_length) {} + int64_t timestamp_us; + // TODO(terelius): This allocates space for 15 CSRCs even if none are used. + RTPHeader header; + size_t header_length; + size_t total_length; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedRtpPacketIncoming { + LoggedRtpPacketIncoming(uint64_t timestamp_us, + RTPHeader header, + size_t header_length, + size_t total_length) + : rtp(timestamp_us, header, header_length, total_length) {} + LoggedRtpPacket rtp; + int64_t log_time_us() const { return rtp.timestamp_us; } + int64_t log_time_ms() const { return rtp.timestamp_us / 1000; } +}; + +struct LoggedRtpPacketOutgoing { + LoggedRtpPacketOutgoing(uint64_t timestamp_us, + RTPHeader header, + size_t header_length, + size_t total_length) + : rtp(timestamp_us, header, header_length, total_length) {} + LoggedRtpPacket rtp; + int64_t log_time_us() const { return rtp.timestamp_us; } + int64_t log_time_ms() const { return rtp.timestamp_us / 1000; } +}; + +struct LoggedRtcpPacket { + LoggedRtcpPacket(uint64_t timestamp_us, + const uint8_t* packet, + size_t total_length) + : timestamp_us(timestamp_us), raw_data(packet, packet + total_length) {} + int64_t timestamp_us; + std::vector raw_data; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedRtcpPacketIncoming { + LoggedRtcpPacketIncoming(uint64_t timestamp_us, + const uint8_t* packet, + size_t total_length) + : rtcp(timestamp_us, packet, total_length) {} + LoggedRtcpPacket rtcp; + int64_t log_time_us() const { return rtcp.timestamp_us; } + int64_t log_time_ms() const { return rtcp.timestamp_us / 1000; } +}; + +struct LoggedRtcpPacketOutgoing { + LoggedRtcpPacketOutgoing(uint64_t timestamp_us, + const uint8_t* packet, + size_t total_length) + : rtcp(timestamp_us, packet, total_length) {} + LoggedRtcpPacket rtcp; + int64_t log_time_us() const { return rtcp.timestamp_us; } + int64_t log_time_ms() const { return rtcp.timestamp_us / 1000; } +}; + +struct LoggedRtcpPacketReceiverReport { + int64_t timestamp_us; + rtcp::ReceiverReport rr; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedRtcpPacketSenderReport { + int64_t timestamp_us; + rtcp::SenderReport sr; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedRtcpPacketRemb { + int64_t timestamp_us; + rtcp::Remb remb; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedRtcpPacketNack { + int64_t timestamp_us; + rtcp::Nack nack; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedRtcpPacketTransportFeedback { + int64_t timestamp_us; + rtcp::TransportFeedback transport_feedback; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedStartEvent { + explicit LoggedStartEvent(uint64_t timestamp_us) + : timestamp_us(timestamp_us) {} + int64_t timestamp_us; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedStopEvent { + explicit LoggedStopEvent(uint64_t timestamp_us) + : timestamp_us(timestamp_us) {} + int64_t timestamp_us; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedAudioRecvConfig { + LoggedAudioRecvConfig(int64_t timestamp_us, const rtclog::StreamConfig config) + : timestamp_us(timestamp_us), config(config) {} + int64_t timestamp_us; + rtclog::StreamConfig config; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedAudioSendConfig { + LoggedAudioSendConfig(int64_t timestamp_us, const rtclog::StreamConfig config) + : timestamp_us(timestamp_us), config(config) {} + int64_t timestamp_us; + rtclog::StreamConfig config; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedVideoRecvConfig { + LoggedVideoRecvConfig(int64_t timestamp_us, const rtclog::StreamConfig config) + : timestamp_us(timestamp_us), config(config) {} + int64_t timestamp_us; + rtclog::StreamConfig config; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +struct LoggedVideoSendConfig { + LoggedVideoSendConfig(int64_t timestamp_us, + const std::vector configs) + : timestamp_us(timestamp_us), configs(configs) {} + int64_t timestamp_us; + std::vector configs; + int64_t log_time_us() const { return timestamp_us; } + int64_t log_time_ms() const { return timestamp_us / 1000; } +}; + +template +class PacketView; + +template +class PacketIterator { + friend class PacketView; + + public: + // Standard iterator traits. + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T*; + using reference = T&; + using iterator_category = std::bidirectional_iterator_tag; + + // The default-contructed iterator is meaningless, but is required by the + // ForwardIterator concept. + PacketIterator() : ptr_(nullptr), element_size_(0) {} + PacketIterator(const PacketIterator& other) + : ptr_(other.ptr_), element_size_(other.element_size_) {} + PacketIterator(const PacketIterator&& other) + : ptr_(other.ptr_), element_size_(other.element_size_) {} + ~PacketIterator() = default; + + PacketIterator& operator=(const PacketIterator& other) { + ptr_ = other.ptr_; + element_size_ = other.element_size_; + return *this; + } + PacketIterator& operator=(const PacketIterator&& other) { + ptr_ = other.ptr_; + element_size_ = other.element_size_; + return *this; + } + + bool operator==(const PacketIterator& other) const { + RTC_DCHECK_EQ(element_size_, other.element_size_); + return ptr_ == other.ptr_; + } + bool operator!=(const PacketIterator& other) const { + RTC_DCHECK_EQ(element_size_, other.element_size_); + return ptr_ != other.ptr_; + } + + PacketIterator& operator++() { + ptr_ += element_size_; + return *this; + } + PacketIterator& operator--() { + ptr_ -= element_size_; + return *this; + } + PacketIterator operator++(int) { + PacketIterator iter_copy(ptr_, element_size_); + ptr_ += element_size_; + return iter_copy; + } + PacketIterator operator--(int) { + PacketIterator iter_copy(ptr_, element_size_); + ptr_ -= element_size_; + return iter_copy; + } + + T& operator*() { return *reinterpret_cast(ptr_); } + const T& operator*() const { return *reinterpret_cast(ptr_); } + + private: + PacketIterator(typename std::conditional::value, + const void*, + void*>::type p, + size_t s) + : ptr_(reinterpret_cast(p)), element_size_(s) {} + + typename std::conditional::value, const char*, char*>::type + ptr_; + size_t element_size_; +}; + +// Suppose that we have a struct S where we are only interested in a specific +// member M. Given an array of S, PacketView can be used to treat the array +// as an array of M, without exposing the type S to surrounding code and without +// accessing the member through a virtual function. In this case, we want to +// have a common view for incoming and outgoing RtpPackets, hence the PacketView +// name. +// Note that constructing a PacketView bypasses the typesystem, so the caller +// has to take extra care when constructing these objects. The implementation +// also requires that the containing struct is standard-layout (e.g. POD). +// +// Usage example: +// struct A {...}; +// struct B { A a; ...}; +// struct C { A a; ...}; +// size_t len = 10; +// B* array1 = new B[len]; +// C* array2 = new C[len]; +// +// PacketView view1 = PacketView::Create(array1, len, offsetof(B, a)); +// PacketView view2 = PacketView::Create(array2, len, offsetof(C, a)); +// +// The following code works with either view1 or view2. +// void f(PacketView view) +// for (A& a : view) { +// DoSomething(a); +// } +template +class PacketView { + public: + template + static PacketView Create(U* ptr, size_t num_elements, size_t offset) { + static_assert(std::is_standard_layout::value, + "PacketView can only be created for standard layout types."); + static_assert(std::is_standard_layout::value, + "PacketView can only be created for standard layout types."); + return PacketView(ptr, num_elements, offset, sizeof(U)); + } + + using iterator = PacketIterator; + using const_iterator = PacketIterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + iterator begin() { return iterator(data_, element_size_); } + iterator end() { + auto end_ptr = data_ + num_elements_ * element_size_; + return iterator(end_ptr, element_size_); + } + + const_iterator begin() const { return const_iterator(data_, element_size_); } + const_iterator end() const { + auto end_ptr = data_ + num_elements_ * element_size_; + return const_iterator(end_ptr, element_size_); + } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + size_t size() const { return num_elements_; } + + T& operator[](size_t i) { + auto elem_ptr = data_ + i * element_size_; + return *reinterpret_cast(elem_ptr); + } + + const T& operator[](size_t i) const { + auto elem_ptr = data_ + i * element_size_; + return *reinterpret_cast(elem_ptr); + } + + private: + PacketView(typename std::conditional::value, + const void*, + void*>::type data, + size_t num_elements, + size_t offset, + size_t element_size) + : data_(reinterpret_cast(data) + offset), + num_elements_(num_elements), + element_size_(element_size) {} + + typename std::conditional::value, const char*, char*>::type + data_; + size_t num_elements_; + size_t element_size_; +}; + +class ParsedRtcEventLog { + friend class RtcEventLogTestHelper; + + public: + enum EventType { + UNKNOWN_EVENT = 0, + LOG_START = 1, + LOG_END = 2, + RTP_EVENT = 3, + RTCP_EVENT = 4, + AUDIO_PLAYOUT_EVENT = 5, + LOSS_BASED_BWE_UPDATE = 6, + DELAY_BASED_BWE_UPDATE = 7, + VIDEO_RECEIVER_CONFIG_EVENT = 8, + VIDEO_SENDER_CONFIG_EVENT = 9, + AUDIO_RECEIVER_CONFIG_EVENT = 10, + AUDIO_SENDER_CONFIG_EVENT = 11, + AUDIO_NETWORK_ADAPTATION_EVENT = 16, + BWE_PROBE_CLUSTER_CREATED_EVENT = 17, + BWE_PROBE_RESULT_EVENT = 18, + ALR_STATE_EVENT = 19, + ICE_CANDIDATE_PAIR_CONFIG = 20, + ICE_CANDIDATE_PAIR_EVENT = 21, + }; + + enum class MediaType { ANY, AUDIO, VIDEO, DATA }; + enum class UnconfiguredHeaderExtensions { + kDontParse, + kAttemptWebrtcDefaultConfig + }; + + explicit ParsedRtcEventLog( + UnconfiguredHeaderExtensions parse_unconfigured_header_extensions = + UnconfiguredHeaderExtensions::kDontParse); + + // Clears previously parsed events and resets the ParsedRtcEventLog to an + // empty state. + void Clear(); + + // Reads an RtcEventLog file and returns true if parsing was successful. + bool ParseFile(const std::string& file_name); + + // Reads an RtcEventLog from a string and returns true if successful. + bool ParseString(const std::string& s); + + // Reads an RtcEventLog from an istream and returns true if successful. + bool ParseStream( + std::istream& stream); // no-presubmit-check TODO(webrtc:8982) + + // Returns the number of events in an EventStream. + size_t GetNumberOfEvents() const; + + // Reads the arrival timestamp (in microseconds) from a rtclog::Event. + int64_t GetTimestamp(size_t index) const; + int64_t GetTimestamp(const rtclog::Event& event) const; + + // Reads the event type of the rtclog::Event at |index|. + EventType GetEventType(size_t index) const; + + // Reads the header, direction, header length and packet length from the RTP + // event at |index|, and stores the values in the corresponding output + // parameters. Each output parameter can be set to nullptr if that value + // isn't needed. + // NB: The header must have space for at least IP_PACKET_SIZE bytes. + // Returns: a pointer to a header extensions map acquired from parsing + // corresponding Audio/Video Sender/Receiver config events. + // Warning: if the same SSRC is reused by both video and audio streams during + // call, extensions maps may be incorrect (the last one would be returned). + const webrtc::RtpHeaderExtensionMap* GetRtpHeader( + size_t index, + PacketDirection* incoming, + uint8_t* header, + size_t* header_length, + size_t* total_length, + int* probe_cluster_id) const; + const webrtc::RtpHeaderExtensionMap* GetRtpHeader( + const rtclog::Event& event, + PacketDirection* incoming, + uint8_t* header, + size_t* header_length, + size_t* total_length, + int* probe_cluster_id) const; + + // Reads packet, direction and packet length from the RTCP event at |index|, + // and stores the values in the corresponding output parameters. + // Each output parameter can be set to nullptr if that value isn't needed. + // NB: The packet must have space for at least IP_PACKET_SIZE bytes. + void GetRtcpPacket(size_t index, + PacketDirection* incoming, + uint8_t* packet, + size_t* length) const; + void GetRtcpPacket(const rtclog::Event& event, + PacketDirection* incoming, + uint8_t* packet, + size_t* length) const; + + // Reads a video receive config event to a StreamConfig struct. + // Only the fields that are stored in the protobuf will be written. + rtclog::StreamConfig GetVideoReceiveConfig(size_t index) const; + + // Reads a video send config event to a StreamConfig struct. If the proto + // contains multiple SSRCs and RTX SSRCs (this used to be the case for + // simulcast streams) then we return one StreamConfig per SSRC,RTX_SSRC pair. + // Only the fields that are stored in the protobuf will be written. + std::vector GetVideoSendConfig(size_t index) const; + + // Reads a audio receive config event to a StreamConfig struct. + // Only the fields that are stored in the protobuf will be written. + rtclog::StreamConfig GetAudioReceiveConfig(size_t index) const; + + // Reads a config event to a StreamConfig struct. + // Only the fields that are stored in the protobuf will be written. + rtclog::StreamConfig GetAudioSendConfig(size_t index) const; + + // Reads the SSRC from the audio playout event at |index|. The SSRC is stored + // in the output parameter ssrc. The output parameter can be set to nullptr + // and in that case the function only asserts that the event is well formed. + LoggedAudioPlayoutEvent GetAudioPlayout(size_t index) const; + + // Reads bitrate, fraction loss (as defined in RFC 1889) and total number of + // expected packets from the loss based BWE event at |index| and stores the + // values in + // the corresponding output parameters. Each output parameter can be set to + // nullptr if that + // value isn't needed. + LoggedBweLossBasedUpdate GetLossBasedBweUpdate(size_t index) const; + + // Reads bitrate and detector_state from the delay based BWE event at |index| + // and stores the values in the corresponding output parameters. Each output + // parameter can be set to nullptr if that + // value isn't needed. + LoggedBweDelayBasedUpdate GetDelayBasedBweUpdate(size_t index) const; + + // Reads a audio network adaptation event to a (non-NULL) + // AudioEncoderRuntimeConfig struct. Only the fields that are + // stored in the protobuf will be written. + LoggedAudioNetworkAdaptationEvent GetAudioNetworkAdaptation( + size_t index) const; + + LoggedBweProbeClusterCreatedEvent GetBweProbeClusterCreated( + size_t index) const; + + LoggedBweProbeResultEvent GetBweProbeResult(size_t index) const; + + MediaType GetMediaType(uint32_t ssrc, PacketDirection direction) const; + + LoggedAlrStateEvent GetAlrState(size_t index) const; + + LoggedIceCandidatePairConfig GetIceCandidatePairConfig(size_t index) const; + + LoggedIceCandidatePairEvent GetIceCandidatePairEvent(size_t index) const; + + const std::set& incoming_rtx_ssrcs() const { + return incoming_rtx_ssrcs_; + } + const std::set& incoming_video_ssrcs() const { + return incoming_video_ssrcs_; + } + const std::set& incoming_audio_ssrcs() const { + return incoming_audio_ssrcs_; + } + const std::set& outgoing_rtx_ssrcs() const { + return outgoing_rtx_ssrcs_; + } + const std::set& outgoing_video_ssrcs() const { + return outgoing_video_ssrcs_; + } + const std::set& outgoing_audio_ssrcs() const { + return outgoing_audio_ssrcs_; + } + + const std::vector& start_log_events() const { + return start_log_events_; + } + const std::vector& stop_log_events() const { + return stop_log_events_; + } + const std::map>& audio_playout_events() const { + return audio_playout_events_; + } + const std::vector& + audio_network_adaptation_events() const { + return audio_network_adaptation_events_; + } + const std::vector& + bwe_probe_cluster_created_events() const { + return bwe_probe_cluster_created_events_; + } + const std::vector& bwe_probe_result_events() + const { + return bwe_probe_result_events_; + } + const std::vector& bwe_delay_updates() const { + return bwe_delay_updates_; + } + const std::vector& bwe_loss_updates() const { + return bwe_loss_updates_; + } + const std::vector& alr_state_events() const { + return alr_state_events_; + } + const std::vector& ice_candidate_pair_configs() + const { + return ice_candidate_pair_configs_; + } + const std::vector& ice_candidate_pair_events() + const { + return ice_candidate_pair_events_; + } + + struct LoggedRtpStreamIncoming { + uint32_t ssrc; + std::vector incoming_packets; + }; + + struct LoggedRtpStreamOutgoing { + uint32_t ssrc; + std::vector outgoing_packets; + }; + + struct LoggedRtpStreamView { + LoggedRtpStreamView(uint32_t ssrc, + const LoggedRtpPacketIncoming* ptr, + size_t num_elements) + : ssrc(ssrc), + packet_view(PacketView::Create( + ptr, + num_elements, + offsetof(LoggedRtpPacketIncoming, rtp))) {} + LoggedRtpStreamView(uint32_t ssrc, + const LoggedRtpPacketOutgoing* ptr, + size_t num_elements) + : ssrc(ssrc), + packet_view(PacketView::Create( + ptr, + num_elements, + offsetof(LoggedRtpPacketOutgoing, rtp))) {} + uint32_t ssrc; + PacketView packet_view; + }; + + const std::vector& incoming_rtp_packets_by_ssrc() + const { + return incoming_rtp_packets_by_ssrc_; + } + + const std::vector& outgoing_rtp_packets_by_ssrc() + const { + return outgoing_rtp_packets_by_ssrc_; + } + + const std::vector& incoming_rtcp_packets() const { + return incoming_rtcp_packets_; + } + + const std::vector& outgoing_rtcp_packets() const { + return outgoing_rtcp_packets_; + } + + const std::vector& rtp_packets_by_ssrc( + PacketDirection direction) const { + if (direction == kIncomingPacket) + return incoming_rtp_packet_views_by_ssrc_; + else + return outgoing_rtp_packet_views_by_ssrc_; + } + + const std::vector& receiver_reports( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_rr_; + } else { + return outgoing_rr_; + } + } + + const std::vector& sender_reports( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_sr_; + } else { + return outgoing_sr_; + } + } + + const std::vector& nacks( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_nack_; + } else { + return outgoing_nack_; + } + } + + const std::vector& rembs( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_remb_; + } else { + return outgoing_remb_; + } + } + + const std::vector& transport_feedbacks( + PacketDirection direction) const { + if (direction == kIncomingPacket) { + return incoming_transport_feedback_; + } else { + return outgoing_transport_feedback_; + } + } + + int64_t first_timestamp() const { return first_timestamp_; } + int64_t last_timestamp() const { return last_timestamp_; } + + private: + void StoreParsedEvent(const rtclog::Event& event); + + rtclog::StreamConfig GetVideoReceiveConfig(const rtclog::Event& event) const; + std::vector GetVideoSendConfig( + const rtclog::Event& event) const; + rtclog::StreamConfig GetAudioReceiveConfig(const rtclog::Event& event) const; + rtclog::StreamConfig GetAudioSendConfig(const rtclog::Event& event) const; + + LoggedAudioPlayoutEvent GetAudioPlayout(const rtclog::Event& event) const; + + LoggedBweLossBasedUpdate GetLossBasedBweUpdate( + const rtclog::Event& event) const; + LoggedBweDelayBasedUpdate GetDelayBasedBweUpdate( + const rtclog::Event& event) const; + + LoggedAudioNetworkAdaptationEvent GetAudioNetworkAdaptation( + const rtclog::Event& event) const; + + LoggedBweProbeClusterCreatedEvent GetBweProbeClusterCreated( + const rtclog::Event& event) const; + LoggedBweProbeResultEvent GetBweProbeResult(const rtclog::Event& event) const; + + LoggedAlrStateEvent GetAlrState(const rtclog::Event& event) const; + + LoggedIceCandidatePairConfig GetIceCandidatePairConfig( + const rtclog::Event& event) const; + LoggedIceCandidatePairEvent GetIceCandidatePairEvent( + const rtclog::Event& event) const; + + std::vector events_; + + struct Stream { + Stream(uint32_t ssrc, + MediaType media_type, + PacketDirection direction, + webrtc::RtpHeaderExtensionMap map) + : ssrc(ssrc), + media_type(media_type), + direction(direction), + rtp_extensions_map(map) {} + uint32_t ssrc; + MediaType media_type; + PacketDirection direction; + webrtc::RtpHeaderExtensionMap rtp_extensions_map; + }; + + const UnconfiguredHeaderExtensions parse_unconfigured_header_extensions_; + + // Make a default extension map for streams without configuration information. + // TODO(ivoc): Once configuration of audio streams is stored in the event log, + // this can be removed. Tracking bug: webrtc:6399 + RtpHeaderExtensionMap default_extension_map_; + + // Tracks what each stream is configured for. Note that a single SSRC can be + // in several sets. For example, the SSRC used for sending video over RTX + // will appear in both video_ssrcs_ and rtx_ssrcs_. In the unlikely case that + // an SSRC is reconfigured to a different media type mid-call, it will also + // appear in multiple sets. + std::set incoming_rtx_ssrcs_; + std::set incoming_video_ssrcs_; + std::set incoming_audio_ssrcs_; + std::set outgoing_rtx_ssrcs_; + std::set outgoing_video_ssrcs_; + std::set outgoing_audio_ssrcs_; + + // Maps an SSRC to the parsed RTP headers in that stream. Header extensions + // are parsed if the stream has been configured. This is only used for + // grouping the events by SSRC during parsing; the events are moved to + // incoming_rtp_packets_by_ssrc_ once the parsing is done. + std::map> + incoming_rtp_packets_map_; + std::map> + outgoing_rtp_packets_map_; + + // RTP headers. + std::vector incoming_rtp_packets_by_ssrc_; + std::vector outgoing_rtp_packets_by_ssrc_; + std::vector incoming_rtp_packet_views_by_ssrc_; + std::vector outgoing_rtp_packet_views_by_ssrc_; + + // Raw RTCP packets. + std::vector incoming_rtcp_packets_; + std::vector outgoing_rtcp_packets_; + + // Parsed RTCP messages. Currently not separated based on SSRC. + std::vector incoming_rr_; + std::vector outgoing_rr_; + std::vector incoming_sr_; + std::vector outgoing_sr_; + std::vector incoming_nack_; + std::vector outgoing_nack_; + std::vector incoming_remb_; + std::vector outgoing_remb_; + std::vector incoming_transport_feedback_; + std::vector outgoing_transport_feedback_; + + std::vector start_log_events_; + std::vector stop_log_events_; + + // Maps an SSRC to the timestamps of parsed audio playout events. + std::map> audio_playout_events_; + + std::vector + audio_network_adaptation_events_; + + std::vector + bwe_probe_cluster_created_events_; + + std::vector bwe_probe_result_events_; + + std::vector bwe_delay_updates_; + + // A list of all updates from the send-side loss-based bandwidth estimator. + std::vector bwe_loss_updates_; + + std::vector alr_state_events_; + + std::vector ice_candidate_pair_configs_; + + std::vector ice_candidate_pair_events_; + + std::vector audio_recv_configs_; + std::vector audio_send_configs_; + std::vector video_recv_configs_; + std::vector video_send_configs_; + + uint8_t last_incoming_rtcp_packet_[IP_PACKET_SIZE]; + uint8_t last_incoming_rtcp_packet_length_; + + int64_t first_timestamp_; + int64_t last_timestamp_; + + // The extension maps are mutable to allow us to insert the default + // configuration when parsing an RTP header for an unconfigured stream. + mutable std::map + incoming_rtp_extensions_maps_; + mutable std::map + outgoing_rtp_extensions_maps_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_PARSER2_H_ diff --git a/logging/rtc_event_log/rtc_event_log_unittest.cc b/logging/rtc_event_log/rtc_event_log_unittest.cc index 1cea3138ab..529228bc4f 100644 --- a/logging/rtc_event_log/rtc_event_log_unittest.cc +++ b/logging/rtc_event_log/rtc_event_log_unittest.cc @@ -34,7 +34,7 @@ #include "logging/rtc_event_log/events/rtc_event_video_send_stream_config.h" #include "logging/rtc_event_log/output/rtc_event_log_output_file.h" #include "logging/rtc_event_log/rtc_event_log.h" -#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "logging/rtc_event_log/rtc_event_log_parser2.h" #include "logging/rtc_event_log/rtc_event_log_unittest_helper.h" #include "logging/rtc_event_log/rtc_stream_config.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" @@ -750,17 +750,16 @@ TEST(RtcEventLogTest, CircularBufferKeepsMostRecentEvents) { for (size_t i = 1; i < parsed_log.GetNumberOfEvents() - 1; i++) { EXPECT_EQ(parsed_log.GetEventType(i), ParsedRtcEventLog::EventType::AUDIO_PLAYOUT_EVENT); - uint32_t ssrc; - parsed_log.GetAudioPlayout(i, &ssrc); - int64_t timestamp = parsed_log.GetTimestamp(i); - EXPECT_LT(ssrc, kNumEvents); - EXPECT_EQ(static_cast(kStartTime + 10000 * ssrc), timestamp); + LoggedAudioPlayoutEvent playout_event = parsed_log.GetAudioPlayout(i); + EXPECT_LT(playout_event.ssrc, kNumEvents); + EXPECT_EQ(static_cast(kStartTime + 10000 * playout_event.ssrc), + playout_event.timestamp_us); if (last_ssrc) - EXPECT_EQ(ssrc, *last_ssrc + 1); + EXPECT_EQ(playout_event.ssrc, *last_ssrc + 1); if (last_timestamp) - EXPECT_EQ(timestamp, *last_timestamp + 10000); - last_ssrc = ssrc; - last_timestamp = timestamp; + EXPECT_EQ(playout_event.timestamp_us, *last_timestamp + 10000); + last_ssrc = playout_event.ssrc; + last_timestamp = playout_event.timestamp_us; } RtcEventLogTestHelper::VerifyLogEndEvent(parsed_log, parsed_log.GetNumberOfEvents() - 1); diff --git a/logging/rtc_event_log/rtc_event_log_unittest_helper.cc b/logging/rtc_event_log/rtc_event_log_unittest_helper.cc index 23e15b5033..955c3b98e8 100644 --- a/logging/rtc_event_log/rtc_event_log_unittest_helper.cc +++ b/logging/rtc_event_log/rtc_event_log_unittest_helper.cc @@ -460,9 +460,8 @@ void RtcEventLogTestHelper::VerifyPlayoutEvent( EXPECT_EQ(ssrc, playout_event.local_ssrc()); // Check consistency of the parser. - uint32_t parsed_ssrc; - parsed_log.GetAudioPlayout(index, &parsed_ssrc); - EXPECT_EQ(ssrc, parsed_ssrc); + LoggedAudioPlayoutEvent parsed_event = parsed_log.GetAudioPlayout(index); + EXPECT_EQ(ssrc, parsed_event.ssrc); } void RtcEventLogTestHelper::VerifyBweLossEvent( @@ -484,14 +483,10 @@ void RtcEventLogTestHelper::VerifyBweLossEvent( EXPECT_EQ(total_packets, bwe_event.total_packets()); // Check consistency of the parser. - int32_t parsed_bitrate; - uint8_t parsed_fraction_loss; - int32_t parsed_total_packets; - parsed_log.GetLossBasedBweUpdate( - index, &parsed_bitrate, &parsed_fraction_loss, &parsed_total_packets); - EXPECT_EQ(bitrate, parsed_bitrate); - EXPECT_EQ(fraction_loss, parsed_fraction_loss); - EXPECT_EQ(total_packets, parsed_total_packets); + LoggedBweLossBasedUpdate bwe_update = parsed_log.GetLossBasedBweUpdate(index); + EXPECT_EQ(bitrate, bwe_update.bitrate_bps); + EXPECT_EQ(fraction_loss, bwe_update.fraction_lost); + EXPECT_EQ(total_packets, bwe_update.expected_packets); } void RtcEventLogTestHelper::VerifyBweDelayEvent( @@ -511,8 +506,7 @@ void RtcEventLogTestHelper::VerifyBweDelayEvent( GetRuntimeDetectorState(bwe_event.detector_state())); // Check consistency of the parser. - ParsedRtcEventLog::BweDelayBasedUpdate res = - parsed_log.GetDelayBasedBweUpdate(index); + LoggedBweDelayBasedUpdate res = parsed_log.GetDelayBasedBweUpdate(index); EXPECT_EQ(res.bitrate_bps, bitrate); EXPECT_EQ(res.detector_state, detector_state); } @@ -521,15 +515,20 @@ void RtcEventLogTestHelper::VerifyAudioNetworkAdaptation( const ParsedRtcEventLog& parsed_log, size_t index, const AudioEncoderRuntimeConfig& config) { - AudioEncoderRuntimeConfig parsed_config; - parsed_log.GetAudioNetworkAdaptation(index, &parsed_config); - EXPECT_EQ(config.bitrate_bps, parsed_config.bitrate_bps); - EXPECT_EQ(config.enable_dtx, parsed_config.enable_dtx); - EXPECT_EQ(config.enable_fec, parsed_config.enable_fec); - EXPECT_EQ(config.frame_length_ms, parsed_config.frame_length_ms); - EXPECT_EQ(config.num_channels, parsed_config.num_channels); + ASSERT_LT(index, parsed_log.events_.size()); + const rtclog::Event& event = parsed_log.events_[index]; + ASSERT_TRUE(IsValidBasicEvent(event)); + ASSERT_EQ(rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT, event.type()); + + LoggedAudioNetworkAdaptationEvent parsed_event = + parsed_log.GetAudioNetworkAdaptation(index); + EXPECT_EQ(config.bitrate_bps, parsed_event.config.bitrate_bps); + EXPECT_EQ(config.enable_dtx, parsed_event.config.enable_dtx); + EXPECT_EQ(config.enable_fec, parsed_event.config.enable_fec); + EXPECT_EQ(config.frame_length_ms, parsed_event.config.frame_length_ms); + EXPECT_EQ(config.num_channels, parsed_event.config.num_channels); EXPECT_EQ(config.uplink_packet_loss_fraction, - parsed_config.uplink_packet_loss_fraction); + parsed_event.config.uplink_packet_loss_fraction); } void RtcEventLogTestHelper::VerifyLogStartEvent( diff --git a/logging/rtc_event_log/rtc_event_log_unittest_helper.h b/logging/rtc_event_log/rtc_event_log_unittest_helper.h index 630f160a18..949a3db8a2 100644 --- a/logging/rtc_event_log/rtc_event_log_unittest_helper.h +++ b/logging/rtc_event_log/rtc_event_log_unittest_helper.h @@ -12,7 +12,7 @@ #define LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG_UNITTEST_HELPER_H_ #include "call/call.h" -#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "logging/rtc_event_log/rtc_event_log_parser2.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" diff --git a/modules/audio_coding/neteq/tools/rtc_event_log_source.cc b/modules/audio_coding/neteq/tools/rtc_event_log_source.cc index d6224ffa0e..b370e8898a 100644 --- a/modules/audio_coding/neteq/tools/rtc_event_log_source.cc +++ b/modules/audio_coding/neteq/tools/rtc_event_log_source.cc @@ -66,7 +66,7 @@ std::unique_ptr RtcEventLogSource::NextPacket() { } if (parsed_stream_.GetMediaType(packet->header().ssrc, direction) != - webrtc::ParsedRtcEventLog::MediaType::AUDIO) { + ParsedRtcEventLog::MediaType::AUDIO) { continue; } @@ -85,12 +85,10 @@ int64_t RtcEventLogSource::NextAudioOutputEventMs() { while (audio_output_index_ < parsed_stream_.GetNumberOfEvents()) { if (parsed_stream_.GetEventType(audio_output_index_) == ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) { - uint64_t timestamp_us = parsed_stream_.GetTimestamp(audio_output_index_); - // We call GetAudioPlayout only to check that the protobuf event is - // well-formed. - parsed_stream_.GetAudioPlayout(audio_output_index_, nullptr); + LoggedAudioPlayoutEvent playout_event = + parsed_stream_.GetAudioPlayout(audio_output_index_); audio_output_index_++; - return timestamp_us / 1000; + return playout_event.timestamp_us / 1000; } audio_output_index_++; } diff --git a/modules/audio_coding/neteq/tools/rtc_event_log_source.h b/modules/audio_coding/neteq/tools/rtc_event_log_source.h index df01e06535..1b25e1239c 100644 --- a/modules/audio_coding/neteq/tools/rtc_event_log_source.h +++ b/modules/audio_coding/neteq/tools/rtc_event_log_source.h @@ -14,7 +14,7 @@ #include #include -#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "logging/rtc_event_log/rtc_event_log_parser2.h" #include "modules/audio_coding/neteq/tools/packet_source.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "rtc_base/constructormagic.h" diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn index d1d2b55dd4..73e2494d1e 100644 --- a/rtc_tools/BUILD.gn +++ b/rtc_tools/BUILD.gn @@ -237,6 +237,7 @@ if (!build_with_chromium) { "../rtc_base:checks", "../rtc_base:rtc_base_approved", "../rtc_base:rtc_numerics", + "../rtc_base:stringutils", # TODO(kwiberg): Remove this dependency. "../api/audio_codecs:audio_codecs_api", diff --git a/rtc_tools/event_log_visualizer/analyzer.cc b/rtc_tools/event_log_visualizer/analyzer.cc index 1a7309c4e1..c7806324f4 100644 --- a/rtc_tools/event_log_visualizer/analyzer.cc +++ b/rtc_tools/event_log_visualizer/analyzer.cc @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -25,6 +24,7 @@ #include "call/video_send_stream.h" #include "common_types.h" // NOLINT(build/include) #include "logging/rtc_event_log/rtc_stream_config.h" +#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" #include "modules/audio_coding/neteq/tools/audio_sink.h" #include "modules/audio_coding/neteq/tools/fake_decode_from_file.h" #include "modules/audio_coding/neteq/tools/neteq_delay_analyzer.h" @@ -40,6 +40,7 @@ #include "modules/pacing/packet_router.h" #include "modules/rtp_rtcp/include/rtp_rtcp.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/rtcp_packet.h" #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" #include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" #include "modules/rtp_rtcp/source/rtcp_packet/remb.h" @@ -49,6 +50,7 @@ #include "modules/rtp_rtcp/source/rtp_utility.h" #include "rtc_base/checks.h" #include "rtc_base/format_macros.h" +#include "rtc_base/function_view.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/sequence_number_util.h" #include "rtc_base/ptr_util.h" @@ -59,7 +61,6 @@ #endif // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE namespace webrtc { -namespace plotting { namespace { @@ -127,29 +128,45 @@ int64_t WrappingDifference(uint32_t later, uint32_t earlier, int64_t modulus) { return difference; } -// 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 GetDefaultHeaderExtensionMap() { - webrtc::RtpHeaderExtensionMap default_map; - default_map.Register(webrtc::RtpExtension::kAudioLevelDefaultId); - default_map.Register( - webrtc::RtpExtension::kTimestampOffsetDefaultId); - default_map.Register( - webrtc::RtpExtension::kAbsSendTimeDefaultId); - default_map.Register( - webrtc::RtpExtension::kVideoRotationDefaultId); - default_map.Register( - webrtc::RtpExtension::kVideoContentTypeDefaultId); - default_map.Register( - webrtc::RtpExtension::kVideoTimingDefaultId); - default_map.Register( - webrtc::RtpExtension::kTransportSequenceNumberDefaultId); - default_map.Register( - webrtc::RtpExtension::kPlayoutDelayDefaultId); - return default_map; +// This is much more reliable for outgoing streams than for incoming streams. +template +rtc::Optional EstimateRtpClockFrequency( + const RtpPacketContainer& packets, + int64_t end_time_us) { + RTC_CHECK(packets.size() >= 2); + SeqNumUnwrapper unwrapper; + uint64_t first_rtp_timestamp = + unwrapper.Unwrap(packets[0].rtp.header.timestamp); + int64_t first_log_timestamp = packets[0].log_time_us(); + uint64_t last_rtp_timestamp = first_rtp_timestamp; + int64_t last_log_timestamp = first_log_timestamp; + for (size_t i = 1; i < packets.size(); i++) { + if (packets[i].log_time_us() > end_time_us) + break; + last_rtp_timestamp = unwrapper.Unwrap(packets[i].rtp.header.timestamp); + last_log_timestamp = packets[i].log_time_us(); + } + if (last_log_timestamp - first_log_timestamp < kNumMicrosecsPerSec) { + RTC_LOG(LS_WARNING) + << "Failed to estimate RTP clock frequency: Stream too short. (" + << packets.size() << " packets, " + << last_log_timestamp - first_log_timestamp << " us)"; + return rtc::nullopt; + } + double duration = + static_cast(last_log_timestamp - first_log_timestamp) / + kNumMicrosecsPerSec; + double estimated_frequency = + (last_rtp_timestamp - first_rtp_timestamp) / duration; + for (uint32_t f : {8000, 16000, 32000, 48000, 90000}) { + if (std::fabs(estimated_frequency - f) < 0.05 * f) { + return f; + } + } + RTC_LOG(LS_WARNING) << "Failed to estimate RTP clock frequency: Estimate " + << estimated_frequency + << "not close to any stardard RTP frequency."; + return rtc::nullopt; } constexpr float kLeftMargin = 0.01f; @@ -165,7 +182,8 @@ rtc::Optional NetworkDelayDiff_AbsSendTime( int64_t send_time_diff = WrappingDifference( new_packet.header.extension.absoluteSendTime, old_packet.header.extension.absoluteSendTime, 1ul << 24); - int64_t recv_time_diff = new_packet.timestamp - old_packet.timestamp; + int64_t recv_time_diff = + new_packet.log_time_us() - old_packet.log_time_us(); double delay_change_us = recv_time_diff - AbsSendTimeToMicroseconds(send_time_diff); return delay_change_us / 1000; @@ -179,7 +197,7 @@ rtc::Optional NetworkDelayDiff_CaptureTime( const LoggedRtpPacket& new_packet) { int64_t send_time_diff = WrappingDifference( new_packet.header.timestamp, old_packet.header.timestamp, 1ull << 32); - int64_t recv_time_diff = new_packet.timestamp - old_packet.timestamp; + int64_t recv_time_diff = new_packet.log_time_us() - old_packet.log_time_us(); const double kVideoSampleRate = 90000; // TODO(terelius): We treat all streams as video for now, even though @@ -193,9 +211,9 @@ rtc::Optional NetworkDelayDiff_CaptureTime( if (delay_change < -10000 || 10000 < delay_change) { RTC_LOG(LS_WARNING) << "Very large delay change. Timestamps correct?"; RTC_LOG(LS_WARNING) << "Old capture time " << old_packet.header.timestamp - << ", received time " << old_packet.timestamp; + << ", received time " << old_packet.log_time_us(); RTC_LOG(LS_WARNING) << "New capture time " << new_packet.header.timestamp - << ", received time " << new_packet.timestamp; + << ", received time " << new_packet.log_time_us(); RTC_LOG(LS_WARNING) << "Receive time difference " << recv_time_diff << " = " << static_cast(recv_time_diff) / kNumMicrosecsPerSec @@ -208,55 +226,55 @@ rtc::Optional NetworkDelayDiff_CaptureTime( return delay_change; } -// For each element in data, use |get_y()| to extract a y-coordinate and +// For each element in data_view, use |f()| to extract a y-coordinate and // store the result in a TimeSeries. -template -void ProcessPoints( - rtc::FunctionView(const DataType&)> get_y, - const std::vector& data, - uint64_t begin_time, - TimeSeries* result) { - for (size_t i = 0; i < data.size(); i++) { - float x = static_cast(data[i].timestamp - begin_time) / +template +void ProcessPoints(rtc::FunctionView(const DataType&)> f, + const IterableType& data_view, + int64_t begin_time, + TimeSeries* result) { + for (size_t i = 0; i < data_view.size(); i++) { + const DataType& elem = data_view[i]; + float x = static_cast(elem.log_time_us() - begin_time) / kNumMicrosecsPerSec; - rtc::Optional y = get_y(data[i]); + rtc::Optional y = f(elem); if (y) result->points.emplace_back(x, *y); } } -// For each pair of adjacent elements in |data|, use |get_y| to extract a +// For each pair of adjacent elements in |data|, use |f()| to extract a // y-coordinate and store the result in a TimeSeries. Note that the x-coordinate // will be the time of the second element in the pair. -template +template void ProcessPairs( rtc::FunctionView(const DataType&, - const DataType&)> get_y, - const std::vector& data, - uint64_t begin_time, + const DataType&)> f, + const IterableType& data, + int64_t begin_time, TimeSeries* result) { for (size_t i = 1; i < data.size(); i++) { - float x = static_cast(data[i].timestamp - begin_time) / + float x = static_cast(data[i].log_time_us() - begin_time) / kNumMicrosecsPerSec; - rtc::Optional y = get_y(data[i - 1], data[i]); + rtc::Optional y = f(data[i - 1], data[i]); if (y) result->points.emplace_back(x, static_cast(*y)); } } -// For each element in data, use |extract()| to extract a y-coordinate and +// For each element in data, use |f()| to extract a y-coordinate and // store the result in a TimeSeries. -template +template void AccumulatePoints( - rtc::FunctionView(const DataType&)> extract, - const std::vector& data, - uint64_t begin_time, + rtc::FunctionView(const DataType&)> f, + const IterableType& data, + int64_t begin_time, TimeSeries* result) { ResultType sum = 0; for (size_t i = 0; i < data.size(); i++) { - float x = static_cast(data[i].timestamp - begin_time) / + float x = static_cast(data[i].log_time_us() - begin_time) / kNumMicrosecsPerSec; - rtc::Optional y = extract(data[i]); + rtc::Optional y = f(data[i]); if (y) { sum += *y; result->points.emplace_back(x, static_cast(sum)); @@ -264,21 +282,21 @@ void AccumulatePoints( } } -// For each pair of adjacent elements in |data|, use |extract()| to extract a +// For each pair of adjacent elements in |data|, use |f()| to extract a // y-coordinate and store the result in a TimeSeries. Note that the x-coordinate // will be the time of the second element in the pair. -template +template void AccumulatePairs( rtc::FunctionView(const DataType&, - const DataType&)> extract, - const std::vector& data, - uint64_t begin_time, + const DataType&)> f, + const IterableType& data, + int64_t begin_time, TimeSeries* result) { ResultType sum = 0; for (size_t i = 1; i < data.size(); i++) { - float x = static_cast(data[i].timestamp - begin_time) / + float x = static_cast(data[i].log_time_us() - begin_time) / kNumMicrosecsPerSec; - rtc::Optional y = extract(data[i - 1], data[i]); + rtc::Optional y = f(data[i - 1], data[i]); if (y) sum += *y; result->points.emplace_back(x, static_cast(sum)); @@ -289,30 +307,31 @@ void AccumulatePairs( // A data point is generated every |step| microseconds from |begin_time| // to |end_time|. The value of each data point is the average of the data // during the preceeding |window_duration_us| microseconds. -template +template void MovingAverage( - rtc::FunctionView(const DataType&)> extract, - const std::vector& data, - uint64_t begin_time, - uint64_t end_time, - uint64_t window_duration_us, - uint64_t step, - webrtc::plotting::TimeSeries* result) { + rtc::FunctionView(const DataType&)> f, + const IterableType& data_view, + int64_t begin_time, + int64_t end_time, + int64_t window_duration_us, + int64_t step, + TimeSeries* result) { size_t window_index_begin = 0; size_t window_index_end = 0; ResultType sum_in_window = 0; - for (uint64_t t = begin_time; t < end_time + step; t += step) { - while (window_index_end < data.size() && - data[window_index_end].timestamp < t) { - rtc::Optional value = extract(data[window_index_end]); + for (int64_t t = begin_time; t < end_time + step; t += step) { + while (window_index_end < data_view.size() && + data_view[window_index_end].log_time_us() < t) { + rtc::Optional value = f(data_view[window_index_end]); if (value) sum_in_window += *value; ++window_index_end; } - while (window_index_begin < data.size() && - data[window_index_begin].timestamp < t - window_duration_us) { - rtc::Optional value = extract(data[window_index_begin]); + while (window_index_begin < data_view.size() && + data_view[window_index_begin].log_time_us() < + t - window_duration_us) { + rtc::Optional value = f(data_view[window_index_begin]); if (value) sum_in_window -= *value; ++window_index_begin; @@ -406,7 +425,7 @@ std::string GetNetworkTypeAsString(webrtc::IceCandidateNetworkType type) { } std::string GetCandidatePairLogDescriptionAsString( - const ParsedRtcEventLog::IceCandidatePairConfig& config) { + const LoggedIceCandidatePairConfig& config) { // Example: stun:wifi->relay(tcp):cellular@udp:ipv4 // represents a pair of a local server-reflexive candidate on a WiFi network // and a remote relay candidate using TCP as the relay protocol on a cell @@ -429,244 +448,57 @@ std::string GetCandidatePairLogDescriptionAsString( return ss.str(); } +std::string GetDirectionAsString(PacketDirection direction) { + if (direction == kIncomingPacket) { + return "Incoming"; + } else { + return "Outgoing"; + } +} + +std::string GetDirectionAsShortString(PacketDirection direction) { + if (direction == kIncomingPacket) { + return "In"; + } else { + return "Out"; + } +} + } // namespace EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log) : parsed_log_(log), window_duration_(250000), step_(10000) { - uint64_t first_timestamp = std::numeric_limits::max(); - uint64_t last_timestamp = std::numeric_limits::min(); - - PacketDirection direction; - uint8_t header[IP_PACKET_SIZE]; - size_t header_length; - size_t total_length; - - uint8_t last_incoming_rtcp_packet[IP_PACKET_SIZE]; - uint8_t last_incoming_rtcp_packet_length = 0; - - // Make a default extension map for streams without configuration information. - // TODO(ivoc): Once configuration of audio streams is stored in the event log, - // this can be removed. Tracking bug: webrtc:6399 - RtpHeaderExtensionMap default_extension_map = GetDefaultHeaderExtensionMap(); - - rtc::Optional last_log_start; - - for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) { - ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i); - if (event_type != ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT && - event_type != ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT && - event_type != ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT && - event_type != ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT && - event_type != ParsedRtcEventLog::LOG_START && - event_type != ParsedRtcEventLog::LOG_END) { - uint64_t timestamp = parsed_log_.GetTimestamp(i); - first_timestamp = std::min(first_timestamp, timestamp); - last_timestamp = std::max(last_timestamp, timestamp); - } - - switch (parsed_log_.GetEventType(i)) { - case ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT: { - rtclog::StreamConfig config = parsed_log_.GetVideoReceiveConfig(i); - StreamId stream(config.remote_ssrc, kIncomingPacket); - video_ssrcs_.insert(stream); - StreamId rtx_stream(config.rtx_ssrc, kIncomingPacket); - video_ssrcs_.insert(rtx_stream); - rtx_ssrcs_.insert(rtx_stream); - break; - } - case ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT: { - std::vector configs = - parsed_log_.GetVideoSendConfig(i); - for (const auto& config : configs) { - StreamId stream(config.local_ssrc, kOutgoingPacket); - video_ssrcs_.insert(stream); - StreamId rtx_stream(config.rtx_ssrc, kOutgoingPacket); - video_ssrcs_.insert(rtx_stream); - rtx_ssrcs_.insert(rtx_stream); - } - break; - } - case ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT: { - rtclog::StreamConfig config = parsed_log_.GetAudioReceiveConfig(i); - StreamId stream(config.remote_ssrc, kIncomingPacket); - audio_ssrcs_.insert(stream); - break; - } - case ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT: { - rtclog::StreamConfig config = parsed_log_.GetAudioSendConfig(i); - StreamId stream(config.local_ssrc, kOutgoingPacket); - audio_ssrcs_.insert(stream); - break; - } - case ParsedRtcEventLog::RTP_EVENT: { - RtpHeaderExtensionMap* extension_map = parsed_log_.GetRtpHeader( - i, &direction, header, &header_length, &total_length, nullptr); - RtpUtility::RtpHeaderParser rtp_parser(header, header_length); - RTPHeader parsed_header; - if (extension_map != nullptr) { - rtp_parser.Parse(&parsed_header, extension_map); - } else { - // Use the default extension map. - // TODO(ivoc): Once configuration of audio streams is stored in the - // event log, this can be removed. - // Tracking bug: webrtc:6399 - rtp_parser.Parse(&parsed_header, &default_extension_map); - } - uint64_t timestamp = parsed_log_.GetTimestamp(i); - StreamId stream(parsed_header.ssrc, direction); - rtp_packets_[stream].push_back(LoggedRtpPacket( - timestamp, parsed_header, header_length, total_length)); - break; - } - case ParsedRtcEventLog::RTCP_EVENT: { - uint8_t packet[IP_PACKET_SIZE]; - parsed_log_.GetRtcpPacket(i, &direction, packet, &total_length); - // 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 (direction == webrtc::kIncomingPacket) { - RTC_CHECK_LE(total_length, IP_PACKET_SIZE); - if (total_length == last_incoming_rtcp_packet_length && - memcmp(last_incoming_rtcp_packet, packet, total_length) == 0) { - continue; - } else { - memcpy(last_incoming_rtcp_packet, packet, total_length); - last_incoming_rtcp_packet_length = total_length; - } - } - rtcp::CommonHeader header; - const uint8_t* packet_end = packet + total_length; - for (const uint8_t* block = packet; block < packet_end; - block = header.NextPacket()) { - RTC_CHECK(header.Parse(block, packet_end - block)); - if (header.type() == rtcp::TransportFeedback::kPacketType && - header.fmt() == rtcp::TransportFeedback::kFeedbackMessageType) { - std::unique_ptr rtcp_packet( - rtc::MakeUnique()); - if (rtcp_packet->Parse(header)) { - uint32_t ssrc = rtcp_packet->sender_ssrc(); - StreamId stream(ssrc, direction); - uint64_t timestamp = parsed_log_.GetTimestamp(i); - rtcp_packets_[stream].push_back(LoggedRtcpPacket( - timestamp, kRtcpTransportFeedback, std::move(rtcp_packet))); - } - } else if (header.type() == rtcp::SenderReport::kPacketType) { - std::unique_ptr rtcp_packet( - rtc::MakeUnique()); - if (rtcp_packet->Parse(header)) { - uint32_t ssrc = rtcp_packet->sender_ssrc(); - StreamId stream(ssrc, direction); - uint64_t timestamp = parsed_log_.GetTimestamp(i); - rtcp_packets_[stream].push_back( - LoggedRtcpPacket(timestamp, kRtcpSr, std::move(rtcp_packet))); - } - } else if (header.type() == rtcp::ReceiverReport::kPacketType) { - std::unique_ptr rtcp_packet( - rtc::MakeUnique()); - if (rtcp_packet->Parse(header)) { - uint32_t ssrc = rtcp_packet->sender_ssrc(); - StreamId stream(ssrc, direction); - uint64_t timestamp = parsed_log_.GetTimestamp(i); - rtcp_packets_[stream].push_back( - LoggedRtcpPacket(timestamp, kRtcpRr, std::move(rtcp_packet))); - } - } else if (header.type() == rtcp::Remb::kPacketType && - header.fmt() == rtcp::Remb::kFeedbackMessageType) { - std::unique_ptr rtcp_packet( - rtc::MakeUnique()); - if (rtcp_packet->Parse(header)) { - uint32_t ssrc = rtcp_packet->sender_ssrc(); - StreamId stream(ssrc, direction); - uint64_t timestamp = parsed_log_.GetTimestamp(i); - rtcp_packets_[stream].push_back(LoggedRtcpPacket( - timestamp, kRtcpRemb, std::move(rtcp_packet))); - } - } - } - break; - } - case ParsedRtcEventLog::LOG_START: { - if (last_log_start) { - // A LOG_END event was missing. Use last_timestamp. - RTC_DCHECK_GE(last_timestamp, *last_log_start); - log_segments_.push_back( - std::make_pair(*last_log_start, last_timestamp)); - } - last_log_start = parsed_log_.GetTimestamp(i); - break; - } - case ParsedRtcEventLog::LOG_END: { - RTC_DCHECK(last_log_start); - log_segments_.push_back( - std::make_pair(*last_log_start, parsed_log_.GetTimestamp(i))); - last_log_start.reset(); - break; - } - case ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT: { - uint32_t this_ssrc; - parsed_log_.GetAudioPlayout(i, &this_ssrc); - audio_playout_events_[this_ssrc].push_back(parsed_log_.GetTimestamp(i)); - break; - } - case ParsedRtcEventLog::LOSS_BASED_BWE_UPDATE: { - LossBasedBweUpdate bwe_update; - bwe_update.timestamp = parsed_log_.GetTimestamp(i); - parsed_log_.GetLossBasedBweUpdate(i, &bwe_update.new_bitrate, - &bwe_update.fraction_loss, - &bwe_update.expected_packets); - bwe_loss_updates_.push_back(bwe_update); - break; - } - case ParsedRtcEventLog::DELAY_BASED_BWE_UPDATE: { - bwe_delay_updates_.push_back(parsed_log_.GetDelayBasedBweUpdate(i)); - break; - } - case ParsedRtcEventLog::AUDIO_NETWORK_ADAPTATION_EVENT: { - AudioNetworkAdaptationEvent ana_event; - ana_event.timestamp = parsed_log_.GetTimestamp(i); - parsed_log_.GetAudioNetworkAdaptation(i, &ana_event.config); - audio_network_adaptation_events_.push_back(ana_event); - break; - } - case ParsedRtcEventLog::BWE_PROBE_CLUSTER_CREATED_EVENT: { - bwe_probe_cluster_created_events_.push_back( - parsed_log_.GetBweProbeClusterCreated(i)); - break; - } - case ParsedRtcEventLog::BWE_PROBE_RESULT_EVENT: { - bwe_probe_result_events_.push_back(parsed_log_.GetBweProbeResult(i)); - break; - } - case ParsedRtcEventLog::ALR_STATE_EVENT: { - alr_state_events_.push_back(parsed_log_.GetAlrState(i)); - break; - } - case ParsedRtcEventLog::ICE_CANDIDATE_PAIR_CONFIG: { - ice_candidate_pair_configs_.push_back( - parsed_log_.GetIceCandidatePairConfig(i)); - break; - } - case ParsedRtcEventLog::ICE_CANDIDATE_PAIR_EVENT: { - ice_candidate_pair_events_.push_back( - parsed_log_.GetIceCandidatePairEvent(i)); - break; - } - case ParsedRtcEventLog::UNKNOWN_EVENT: { - break; - } - } + begin_time_ = parsed_log_.first_timestamp(); + end_time_ = parsed_log_.last_timestamp(); + if (end_time_ < begin_time_) { + RTC_LOG(LS_WARNING) << "No useful events in the log."; + begin_time_ = end_time_ = 0; } - - if (last_timestamp < first_timestamp) { - // No useful events in the log. - first_timestamp = last_timestamp = 0; - } - begin_time_ = first_timestamp; - end_time_ = last_timestamp; call_duration_s_ = ToCallTime(end_time_); - if (last_log_start) { - // The log was missing the last LOG_END event. Fake it. - log_segments_.push_back(std::make_pair(*last_log_start, end_time_)); + + const auto& log_start_events = parsed_log_.start_log_events(); + const auto& log_end_events = parsed_log_.stop_log_events(); + auto start_iter = log_start_events.begin(); + auto end_iter = log_end_events.begin(); + while (start_iter != log_start_events.end()) { + int64_t start = start_iter->log_time_us(); + ++start_iter; + rtc::Optional next_start; + if (start_iter != log_start_events.end()) + next_start.emplace(start_iter->log_time_us()); + if (end_iter != log_end_events.end() && + end_iter->log_time_us() <= + next_start.value_or(std::numeric_limits::max())) { + int64_t end = end_iter->log_time_us(); + RTC_DCHECK_LE(start, end); + log_segments_.push_back(std::make_pair(start, end)); + ++end_iter; + } else { + // we're missing an end event. Assume that it occurred just before the + // next start. + log_segments_.push_back( + std::make_pair(start, next_start.value_or(end_time_))); + } } RTC_LOG(LS_INFO) << "Found " << log_segments_.size() << " (LOG_START, LOG_END) segments in log."; @@ -678,7 +510,7 @@ class BitrateObserver : public NetworkChangedObserver, BitrateObserver() : last_bitrate_bps_(0), bitrate_updated_(false) {} void OnNetworkChanged(uint32_t bitrate_bps, - uint8_t fraction_loss, + uint8_t fraction_lost, int64_t rtt_ms, int64_t probing_interval_ms) override { last_bitrate_bps_ = bitrate_bps; @@ -700,187 +532,90 @@ class BitrateObserver : public NetworkChangedObserver, bool bitrate_updated_; }; -bool EventLogAnalyzer::IsRtxSsrc(StreamId stream_id) const { - return rtx_ssrcs_.count(stream_id) == 1; -} - -bool EventLogAnalyzer::IsVideoSsrc(StreamId stream_id) const { - return video_ssrcs_.count(stream_id) == 1; -} - -bool EventLogAnalyzer::IsAudioSsrc(StreamId stream_id) const { - return audio_ssrcs_.count(stream_id) == 1; -} - -std::string EventLogAnalyzer::GetStreamName(StreamId stream_id) const { - std::stringstream name; - if (IsAudioSsrc(stream_id)) { - name << "Audio "; - } else if (IsVideoSsrc(stream_id)) { - name << "Video "; - } else { - name << "Unknown "; - } - if (IsRtxSsrc(stream_id)) - name << "RTX "; - if (stream_id.GetDirection() == kIncomingPacket) { - name << "(In) "; - } else { - name << "(Out) "; - } - name << SsrcToString(stream_id.GetSsrc()); - return name.str(); -} - -// This is much more reliable for outgoing streams than for incoming streams. -rtc::Optional EventLogAnalyzer::EstimateRtpClockFrequency( - const std::vector& packets) const { - RTC_CHECK(packets.size() >= 2); - uint64_t end_time_us = log_segments_.empty() - ? std::numeric_limits::max() - : log_segments_.front().second; - SeqNumUnwrapper unwrapper; - uint64_t first_rtp_timestamp = unwrapper.Unwrap(packets[0].header.timestamp); - uint64_t first_log_timestamp = packets[0].timestamp; - uint64_t last_rtp_timestamp = first_rtp_timestamp; - uint64_t last_log_timestamp = first_log_timestamp; - for (size_t i = 1; i < packets.size(); i++) { - if (packets[i].timestamp > end_time_us) - break; - last_rtp_timestamp = unwrapper.Unwrap(packets[i].header.timestamp); - last_log_timestamp = packets[i].timestamp; - } - if (last_log_timestamp - first_log_timestamp < kNumMicrosecsPerSec) { - RTC_LOG(LS_WARNING) - << "Failed to estimate RTP clock frequency: Stream too short. (" - << packets.size() << " packets, " - << last_log_timestamp - first_log_timestamp << " us)"; - return rtc::nullopt; - } - double duration = - static_cast(last_log_timestamp - first_log_timestamp) / - kNumMicrosecsPerSec; - double estimated_frequency = - (last_rtp_timestamp - first_rtp_timestamp) / duration; - for (uint32_t f : {8000, 16000, 32000, 48000, 90000}) { - if (std::fabs(estimated_frequency - f) < 0.05 * f) { - return f; - } - } - RTC_LOG(LS_WARNING) << "Failed to estimate RTP clock frequency: Estimate " - << estimated_frequency - << "not close to any stardard RTP frequency."; - return rtc::nullopt; -} - float EventLogAnalyzer::ToCallTime(int64_t timestamp) const { return static_cast(timestamp - begin_time_) / kNumMicrosecsPerSec; } -void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction, +void EventLogAnalyzer::CreatePacketGraph(PacketDirection direction, Plot* plot) { - for (auto& kv : rtp_packets_) { - StreamId stream_id = kv.first; - const std::vector& packet_stream = kv.second; - // Filter on direction and SSRC. - if (stream_id.GetDirection() != desired_direction || - !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) { + for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { + // Filter on SSRC. + if (!MatchingSsrc(stream.ssrc, desired_ssrc_)) { continue; } - TimeSeries time_series(GetStreamName(stream_id), LineStyle::kBar); - ProcessPoints( - [](const LoggedRtpPacket& packet) { - return rtc::Optional(packet.total_length); - }, - packet_stream, begin_time_, &time_series); + TimeSeries time_series(GetStreamName(direction, stream.ssrc), + LineStyle::kBar); + auto GetPacketSize = [](const LoggedRtpPacket& packet) { + return rtc::Optional(packet.total_length); + }; + ProcessPoints(GetPacketSize, stream.packet_view, + begin_time_, &time_series); plot->AppendTimeSeries(std::move(time_series)); } plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); plot->SetSuggestedYAxis(0, 1, "Packet size (bytes)", kBottomMargin, kTopMargin); - if (desired_direction == webrtc::PacketDirection::kIncomingPacket) { - plot->SetTitle("Incoming RTP packets"); - } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) { - plot->SetTitle("Outgoing RTP packets"); - } + plot->SetTitle(GetDirectionAsString(direction) + " RTP packets"); } -template +template void EventLogAnalyzer::CreateAccumulatedPacketsTimeSeries( - PacketDirection desired_direction, Plot* plot, - const std::map>& packets, - const std::string& label_prefix) { - for (auto& kv : packets) { - StreamId stream_id = kv.first; - const std::vector& packet_stream = kv.second; - // Filter on direction and SSRC. - if (stream_id.GetDirection() != desired_direction || - !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) { - continue; - } - - std::string label = label_prefix + " " + GetStreamName(stream_id); - TimeSeries time_series(label, LineStyle::kStep); - for (size_t i = 0; i < packet_stream.size(); i++) { - float x = ToCallTime(packet_stream[i].timestamp); - time_series.points.emplace_back(x, i + 1); - } - - plot->AppendTimeSeries(std::move(time_series)); + const IterableType& packets, + const std::string& label) { + TimeSeries time_series(label, LineStyle::kStep); + for (size_t i = 0; i < packets.size(); i++) { + float x = ToCallTime(packets[i].log_time_us()); + time_series.points.emplace_back(x, i + 1); } + plot->AppendTimeSeries(std::move(time_series)); } -void EventLogAnalyzer::CreateAccumulatedPacketsGraph( - PacketDirection desired_direction, - Plot* plot) { - CreateAccumulatedPacketsTimeSeries(desired_direction, plot, rtp_packets_, - "RTP"); - CreateAccumulatedPacketsTimeSeries(desired_direction, plot, rtcp_packets_, - "RTCP"); +void EventLogAnalyzer::CreateAccumulatedPacketsGraph(PacketDirection direction, + Plot* plot) { + for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { + if (!MatchingSsrc(stream.ssrc, desired_ssrc_)) + continue; + std::string label = + std::string("RTP ") + GetStreamName(direction, stream.ssrc); + CreateAccumulatedPacketsTimeSeries(plot, stream.packet_view, label); + } + std::string label = + std::string("RTCP ") + "(" + GetDirectionAsShortString(direction) + ")"; + if (direction == kIncomingPacket) { + CreateAccumulatedPacketsTimeSeries( + plot, parsed_log_.incoming_rtcp_packets(), label); + } else { + CreateAccumulatedPacketsTimeSeries( + plot, parsed_log_.outgoing_rtcp_packets(), label); + } plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); plot->SetSuggestedYAxis(0, 1, "Received Packets", kBottomMargin, kTopMargin); - if (desired_direction == webrtc::PacketDirection::kIncomingPacket) { - plot->SetTitle("Accumulated Incoming RTP/RTCP packets"); - } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) { - plot->SetTitle("Accumulated Outgoing RTP/RTCP packets"); - } + plot->SetTitle(std::string("Accumulated ") + GetDirectionAsString(direction) + + " RTP/RTCP packets"); } // For each SSRC, plot the time between the consecutive playouts. void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) { - std::map time_series; - std::map last_playout; - - uint32_t ssrc; - - for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) { - ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i); - if (event_type == ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) { - parsed_log_.GetAudioPlayout(i, &ssrc); - uint64_t timestamp = parsed_log_.GetTimestamp(i); - if (MatchingSsrc(ssrc, desired_ssrc_)) { - float x = ToCallTime(timestamp); - float y = static_cast(timestamp - last_playout[ssrc]) / 1000; - if (time_series[ssrc].points.size() == 0) { - // There were no previusly logged playout for this SSRC. - // Generate a point, but place it on the x-axis. - y = 0; - } - time_series[ssrc].points.push_back(TimeSeriesPoint(x, y)); - last_playout[ssrc] = timestamp; - } + for (const auto& playout_stream : parsed_log_.audio_playout_events()) { + uint32_t ssrc = playout_stream.first; + if (!MatchingSsrc(ssrc, desired_ssrc_)) + continue; + rtc::Optional last_playout; + TimeSeries time_series(SsrcToString(ssrc), LineStyle::kBar); + for (const auto& playout_time : playout_stream.second) { + float x = ToCallTime(playout_time); + // If there were no previous playouts, place the point on the x-axis. + float y = static_cast(playout_time - + last_playout.value_or(playout_time)) / + 1000; + time_series.points.push_back(TimeSeriesPoint(x, y)); + last_playout.emplace(playout_time); } - } - - // Set labels and put in graph. - for (auto& kv : time_series) { - kv.second.label = SsrcToString(kv.first); - kv.second.line_style = LineStyle::kBar; - plot->AppendTimeSeries(std::move(kv.second)); + plot->AppendTimeSeries(std::move(time_series)); } plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); @@ -890,59 +625,51 @@ void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) { } // For audio SSRCs, plot the audio level. -void EventLogAnalyzer::CreateAudioLevelGraph(Plot* plot) { - std::map time_series; - - for (auto& kv : rtp_packets_) { - StreamId stream_id = kv.first; - const std::vector& packet_stream = kv.second; - // TODO(ivoc): When audio send/receive configs are stored in the event - // log, a check should be added here to only process audio - // streams. Tracking bug: webrtc:6399 - for (auto& packet : packet_stream) { +void EventLogAnalyzer::CreateAudioLevelGraph(PacketDirection direction, + Plot* plot) { + for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { + if (!IsAudioSsrc(direction, stream.ssrc)) + continue; + TimeSeries time_series(GetStreamName(direction, stream.ssrc), + LineStyle::kLine); + for (auto& packet : stream.packet_view) { if (packet.header.extension.hasAudioLevel) { - float x = ToCallTime(packet.timestamp); + float x = ToCallTime(packet.log_time_us()); // The audio level is stored in -dBov (so e.g. -10 dBov is stored as 10) // Here we convert it to dBov. float y = static_cast(-packet.header.extension.audioLevel); - time_series[stream_id].points.emplace_back(TimeSeriesPoint(x, y)); + time_series.points.emplace_back(TimeSeriesPoint(x, y)); } } - } - - for (auto& series : time_series) { - series.second.label = GetStreamName(series.first); - series.second.line_style = LineStyle::kLine; - plot->AppendTimeSeries(std::move(series.second)); + plot->AppendTimeSeries(std::move(time_series)); } plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); plot->SetYAxis(-127, 0, "Audio level (dBov)", kBottomMargin, kTopMargin); - plot->SetTitle("Audio level"); + plot->SetTitle(GetDirectionAsString(direction) + " audio level"); } // For each SSRC, plot the time between the consecutive playouts. void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) { - for (auto& kv : rtp_packets_) { - StreamId stream_id = kv.first; - const std::vector& packet_stream = kv.second; - // Filter on direction and SSRC. - if (stream_id.GetDirection() != kIncomingPacket || - !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) { + for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) { + // Filter on SSRC. + if (!MatchingSsrc(stream.ssrc, desired_ssrc_)) { continue; } - TimeSeries time_series(GetStreamName(stream_id), LineStyle::kBar); - ProcessPairs( - [](const LoggedRtpPacket& old_packet, - const LoggedRtpPacket& new_packet) { - int64_t diff = - WrappingDifference(new_packet.header.sequenceNumber, - old_packet.header.sequenceNumber, 1ul << 16); - return diff; - }, - packet_stream, begin_time_, &time_series); + TimeSeries time_series(GetStreamName(kIncomingPacket, stream.ssrc), + LineStyle::kBar); + auto GetSequenceNumberDiff = [](const LoggedRtpPacketIncoming& old_packet, + const LoggedRtpPacketIncoming& new_packet) { + int64_t diff = + WrappingDifference(new_packet.rtp.header.sequenceNumber, + old_packet.rtp.header.sequenceNumber, 1ul << 16); + return diff; + }; + ProcessPairs(GetSequenceNumberDiff, + stream.incoming_packets, + begin_time_, &time_series); plot->AppendTimeSeries(std::move(time_series)); } @@ -953,47 +680,47 @@ void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) { } void EventLogAnalyzer::CreateIncomingPacketLossGraph(Plot* plot) { - for (auto& kv : rtp_packets_) { - StreamId stream_id = kv.first; - const std::vector& packet_stream = kv.second; - // Filter on direction and SSRC. - if (stream_id.GetDirection() != kIncomingPacket || - !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_) || - packet_stream.size() == 0) { + for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) { + const std::vector& packets = + stream.incoming_packets; + // Filter on SSRC. + if (!MatchingSsrc(stream.ssrc, desired_ssrc_) || packets.size() == 0) { continue; } - TimeSeries time_series(GetStreamName(stream_id), LineStyle::kLine, - PointStyle::kHighlight); - const uint64_t kWindowUs = 1000000; - const uint64_t kStep = 1000000; + TimeSeries time_series(GetStreamName(kIncomingPacket, stream.ssrc), + LineStyle::kLine, PointStyle::kHighlight); + // TODO(terelius): Should the window and step size be read from the class + // instead? + const int64_t kWindowUs = 1000000; + const int64_t kStep = 1000000; SeqNumUnwrapper unwrapper_; SeqNumUnwrapper prior_unwrapper_; size_t window_index_begin = 0; size_t window_index_end = 0; - int64_t highest_seq_number = - unwrapper_.Unwrap(packet_stream[0].header.sequenceNumber) - 1; - int64_t highest_prior_seq_number = - prior_unwrapper_.Unwrap(packet_stream[0].header.sequenceNumber) - 1; + uint64_t highest_seq_number = + unwrapper_.Unwrap(packets[0].rtp.header.sequenceNumber) - 1; + uint64_t highest_prior_seq_number = + prior_unwrapper_.Unwrap(packets[0].rtp.header.sequenceNumber) - 1; - for (uint64_t t = begin_time_; t < end_time_ + kStep; t += kStep) { - while (window_index_end < packet_stream.size() && - packet_stream[window_index_end].timestamp < t) { - int64_t sequence_number = unwrapper_.Unwrap( - packet_stream[window_index_end].header.sequenceNumber); + for (int64_t t = begin_time_; t < end_time_ + kStep; t += kStep) { + while (window_index_end < packets.size() && + packets[window_index_end].rtp.log_time_us() < t) { + uint64_t sequence_number = unwrapper_.Unwrap( + packets[window_index_end].rtp.header.sequenceNumber); highest_seq_number = std::max(highest_seq_number, sequence_number); ++window_index_end; } - while (window_index_begin < packet_stream.size() && - packet_stream[window_index_begin].timestamp < t - kWindowUs) { - int64_t sequence_number = prior_unwrapper_.Unwrap( - packet_stream[window_index_begin].header.sequenceNumber); + while (window_index_begin < packets.size() && + packets[window_index_begin].rtp.log_time_us() < t - kWindowUs) { + uint64_t sequence_number = prior_unwrapper_.Unwrap( + packets[window_index_begin].rtp.header.sequenceNumber); highest_prior_seq_number = std::max(highest_prior_seq_number, sequence_number); ++window_index_begin; } float x = ToCallTime(t); - int64_t expected_packets = highest_seq_number - highest_prior_seq_number; + uint64_t expected_packets = highest_seq_number - highest_prior_seq_number; if (expected_packets > 0) { int64_t received_packets = window_index_end - window_index_begin; int64_t lost_packets = expected_packets - received_packets; @@ -1011,28 +738,28 @@ void EventLogAnalyzer::CreateIncomingPacketLossGraph(Plot* plot) { } void EventLogAnalyzer::CreateIncomingDelayDeltaGraph(Plot* plot) { - for (auto& kv : rtp_packets_) { - StreamId stream_id = kv.first; - const std::vector& packet_stream = kv.second; - // Filter on direction and SSRC. - if (stream_id.GetDirection() != kIncomingPacket || - !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_) || - IsAudioSsrc(stream_id) || !IsVideoSsrc(stream_id) || - IsRtxSsrc(stream_id)) { + for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(kIncomingPacket)) { + // Filter on SSRC. + if (!MatchingSsrc(stream.ssrc, desired_ssrc_) || + IsAudioSsrc(kIncomingPacket, stream.ssrc) || + !IsVideoSsrc(kIncomingPacket, stream.ssrc) || + IsRtxSsrc(kIncomingPacket, stream.ssrc)) { continue; } - TimeSeries capture_time_data(GetStreamName(stream_id) + " capture-time", - LineStyle::kBar); + TimeSeries capture_time_data( + GetStreamName(kIncomingPacket, stream.ssrc) + " capture-time", + LineStyle::kBar); ProcessPairs(NetworkDelayDiff_CaptureTime, - packet_stream, begin_time_, + stream.packet_view, begin_time_, &capture_time_data); plot->AppendTimeSeries(std::move(capture_time_data)); - TimeSeries send_time_data(GetStreamName(stream_id) + " abs-send-time", - LineStyle::kBar); + TimeSeries send_time_data( + GetStreamName(kIncomingPacket, stream.ssrc) + " abs-send-time", + LineStyle::kBar); ProcessPairs(NetworkDelayDiff_AbsSendTime, - packet_stream, begin_time_, + stream.packet_view, begin_time_, &send_time_data); plot->AppendTimeSeries(std::move(send_time_data)); } @@ -1044,28 +771,28 @@ void EventLogAnalyzer::CreateIncomingDelayDeltaGraph(Plot* plot) { } void EventLogAnalyzer::CreateIncomingDelayGraph(Plot* plot) { - for (auto& kv : rtp_packets_) { - StreamId stream_id = kv.first; - const std::vector& packet_stream = kv.second; - // Filter on direction and SSRC. - if (stream_id.GetDirection() != kIncomingPacket || - !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_) || - IsAudioSsrc(stream_id) || !IsVideoSsrc(stream_id) || - IsRtxSsrc(stream_id)) { + for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(kIncomingPacket)) { + // Filter on SSRC. + if (!MatchingSsrc(stream.ssrc, desired_ssrc_) || + IsAudioSsrc(kIncomingPacket, stream.ssrc) || + !IsVideoSsrc(kIncomingPacket, stream.ssrc) || + IsRtxSsrc(kIncomingPacket, stream.ssrc)) { continue; } - TimeSeries capture_time_data(GetStreamName(stream_id) + " capture-time", - LineStyle::kLine); + TimeSeries capture_time_data( + GetStreamName(kIncomingPacket, stream.ssrc) + " capture-time", + LineStyle::kLine); AccumulatePairs(NetworkDelayDiff_CaptureTime, - packet_stream, begin_time_, + stream.packet_view, begin_time_, &capture_time_data); plot->AppendTimeSeries(std::move(capture_time_data)); - TimeSeries send_time_data(GetStreamName(stream_id) + " abs-send-time", - LineStyle::kLine); + TimeSeries send_time_data( + GetStreamName(kIncomingPacket, stream.ssrc) + " abs-send-time", + LineStyle::kLine); AccumulatePairs(NetworkDelayDiff_AbsSendTime, - packet_stream, begin_time_, + stream.packet_view, begin_time_, &send_time_data); plot->AppendTimeSeries(std::move(send_time_data)); } @@ -1080,9 +807,9 @@ void EventLogAnalyzer::CreateIncomingDelayGraph(Plot* plot) { void EventLogAnalyzer::CreateFractionLossGraph(Plot* plot) { TimeSeries time_series("Fraction lost", LineStyle::kLine, PointStyle::kHighlight); - for (auto& bwe_update : bwe_loss_updates_) { - float x = ToCallTime(bwe_update.timestamp); - float y = static_cast(bwe_update.fraction_loss) / 255 * 100; + for (auto& bwe_update : parsed_log_.bwe_loss_updates()) { + float x = ToCallTime(bwe_update.log_time_us()); + float y = static_cast(bwe_update.fraction_lost) / 255 * 100; time_series.points.emplace_back(x, y); } @@ -1094,51 +821,82 @@ void EventLogAnalyzer::CreateFractionLossGraph(Plot* plot) { } // Plot the total bandwidth used by all RTP streams. -void EventLogAnalyzer::CreateTotalBitrateGraph( - PacketDirection desired_direction, - Plot* plot, - bool show_detector_state, - bool show_alr_state) { - struct TimestampSize { - TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {} - uint64_t timestamp; - size_t size; - }; - std::vector packets; - - PacketDirection direction; - size_t total_length; - - // Extract timestamps and sizes for the relevant packets. - for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) { - ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i); - if (event_type == ParsedRtcEventLog::RTP_EVENT) { - parsed_log_.GetRtpHeader(i, &direction, nullptr, nullptr, &total_length, - nullptr); - if (direction == desired_direction) { - uint64_t timestamp = parsed_log_.GetTimestamp(i); - packets.push_back(TimestampSize(timestamp, total_length)); - } - } +void EventLogAnalyzer::CreateTotalIncomingBitrateGraph(Plot* plot) { + // TODO(terelius): This could be provided by the parser. + std::multimap packets_in_order; + for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) { + for (const LoggedRtpPacketIncoming& packet : stream.incoming_packets) + packets_in_order.insert( + std::make_pair(packet.rtp.log_time_us(), packet.rtp.total_length)); } - size_t window_index_begin = 0; - size_t window_index_end = 0; + auto window_begin = packets_in_order.begin(); + auto window_end = packets_in_order.begin(); size_t bytes_in_window = 0; // Calculate a moving average of the bitrate and store in a TimeSeries. TimeSeries bitrate_series("Bitrate", LineStyle::kLine); - for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) { - while (window_index_end < packets.size() && - packets[window_index_end].timestamp < time) { - bytes_in_window += packets[window_index_end].size; - ++window_index_end; + for (int64_t time = begin_time_; time < end_time_ + step_; time += step_) { + while (window_end != packets_in_order.end() && window_end->first < time) { + bytes_in_window += window_end->second; + ++window_end; } - while (window_index_begin < packets.size() && - packets[window_index_begin].timestamp < time - window_duration_) { - RTC_DCHECK_LE(packets[window_index_begin].size, bytes_in_window); - bytes_in_window -= packets[window_index_begin].size; - ++window_index_begin; + while (window_begin != packets_in_order.end() && + window_begin->first < time - window_duration_) { + RTC_DCHECK_LE(window_begin->second, bytes_in_window); + bytes_in_window -= window_begin->second; + ++window_begin; + } + float window_duration_in_seconds = + static_cast(window_duration_) / kNumMicrosecsPerSec; + float x = ToCallTime(time); + float y = bytes_in_window * 8 / window_duration_in_seconds / 1000; + bitrate_series.points.emplace_back(x, y); + } + plot->AppendTimeSeries(std::move(bitrate_series)); + + // Overlay the outgoing REMB over incoming bitrate. + TimeSeries remb_series("Remb", LineStyle::kStep); + for (const auto& rtcp : parsed_log_.rembs(kOutgoingPacket)) { + float x = ToCallTime(rtcp.log_time_us()); + float y = static_cast(rtcp.remb.bitrate_bps()) / 1000; + remb_series.points.emplace_back(x, y); + } + plot->AppendTimeSeriesIfNotEmpty(std::move(remb_series)); + + plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); + plot->SetTitle("Incoming RTP bitrate"); +} + +// Plot the total bandwidth used by all RTP streams. +void EventLogAnalyzer::CreateTotalOutgoingBitrateGraph(Plot* plot, + bool show_detector_state, + bool show_alr_state) { + // TODO(terelius): This could be provided by the parser. + std::multimap packets_in_order; + for (const auto& stream : parsed_log_.outgoing_rtp_packets_by_ssrc()) { + for (const LoggedRtpPacketOutgoing& packet : stream.outgoing_packets) + packets_in_order.insert( + std::make_pair(packet.rtp.log_time_us(), packet.rtp.total_length)); + } + + auto window_begin = packets_in_order.begin(); + auto window_end = packets_in_order.begin(); + size_t bytes_in_window = 0; + + // Calculate a moving average of the bitrate and store in a TimeSeries. + TimeSeries bitrate_series("Bitrate", LineStyle::kLine); + for (int64_t time = begin_time_; time < end_time_ + step_; time += step_) { + while (window_end != packets_in_order.end() && window_end->first < time) { + bytes_in_window += window_end->second; + ++window_end; + } + while (window_begin != packets_in_order.end() && + window_begin->first < time - window_duration_) { + RTC_DCHECK_LE(window_begin->second, bytes_in_window); + bytes_in_window -= window_begin->second; + ++window_begin; } float window_duration_in_seconds = static_cast(window_duration_) / kNumMicrosecsPerSec; @@ -1149,195 +907,161 @@ void EventLogAnalyzer::CreateTotalBitrateGraph( plot->AppendTimeSeries(std::move(bitrate_series)); // Overlay the send-side bandwidth estimate over the outgoing bitrate. - if (desired_direction == kOutgoingPacket) { - TimeSeries loss_series("Loss-based estimate", LineStyle::kStep); - for (auto& loss_update : bwe_loss_updates_) { - float x = ToCallTime(loss_update.timestamp); - float y = static_cast(loss_update.new_bitrate) / 1000; - loss_series.points.emplace_back(x, y); - } + TimeSeries loss_series("Loss-based estimate", LineStyle::kStep); + for (auto& loss_update : parsed_log_.bwe_loss_updates()) { + float x = ToCallTime(loss_update.log_time_us()); + float y = static_cast(loss_update.bitrate_bps) / 1000; + loss_series.points.emplace_back(x, y); + } - TimeSeries delay_series("Delay-based estimate", LineStyle::kStep); - IntervalSeries overusing_series("Overusing", "#ff8e82", - IntervalSeries::kHorizontal); - IntervalSeries underusing_series("Underusing", "#5092fc", - IntervalSeries::kHorizontal); - IntervalSeries normal_series("Normal", "#c4ffc4", - IntervalSeries::kHorizontal); - IntervalSeries* last_series = &normal_series; - double last_detector_switch = 0.0; + TimeSeries delay_series("Delay-based estimate", LineStyle::kStep); + IntervalSeries overusing_series("Overusing", "#ff8e82", + IntervalSeries::kHorizontal); + IntervalSeries underusing_series("Underusing", "#5092fc", + IntervalSeries::kHorizontal); + IntervalSeries normal_series("Normal", "#c4ffc4", + IntervalSeries::kHorizontal); + IntervalSeries* last_series = &normal_series; + double last_detector_switch = 0.0; - BandwidthUsage last_detector_state = BandwidthUsage::kBwNormal; + BandwidthUsage last_detector_state = BandwidthUsage::kBwNormal; - for (auto& delay_update : bwe_delay_updates_) { - float x = ToCallTime(delay_update.timestamp); - float y = static_cast(delay_update.bitrate_bps) / 1000; + for (auto& delay_update : parsed_log_.bwe_delay_updates()) { + float x = ToCallTime(delay_update.log_time_us()); + float y = static_cast(delay_update.bitrate_bps) / 1000; - if (last_detector_state != delay_update.detector_state) { - last_series->intervals.emplace_back(last_detector_switch, x); - last_detector_state = delay_update.detector_state; - last_detector_switch = x; + if (last_detector_state != delay_update.detector_state) { + last_series->intervals.emplace_back(last_detector_switch, x); + last_detector_state = delay_update.detector_state; + last_detector_switch = x; - switch (delay_update.detector_state) { - case BandwidthUsage::kBwNormal: - last_series = &normal_series; - break; - case BandwidthUsage::kBwUnderusing: - last_series = &underusing_series; - break; - case BandwidthUsage::kBwOverusing: - last_series = &overusing_series; - break; - case BandwidthUsage::kLast: - RTC_NOTREACHED(); - } - } - - delay_series.points.emplace_back(x, y); - } - - RTC_CHECK(last_series); - last_series->intervals.emplace_back(last_detector_switch, end_time_); - - TimeSeries created_series("Probe cluster created.", LineStyle::kNone, - PointStyle::kHighlight); - for (auto& cluster : bwe_probe_cluster_created_events_) { - float x = ToCallTime(cluster.timestamp); - float y = static_cast(cluster.bitrate_bps) / 1000; - created_series.points.emplace_back(x, y); - } - - TimeSeries result_series("Probing results.", LineStyle::kNone, - PointStyle::kHighlight); - for (auto& result : bwe_probe_result_events_) { - if (result.bitrate_bps) { - float x = ToCallTime(result.timestamp); - float y = static_cast(*result.bitrate_bps) / 1000; - result_series.points.emplace_back(x, y); + switch (delay_update.detector_state) { + case BandwidthUsage::kBwNormal: + last_series = &normal_series; + break; + case BandwidthUsage::kBwUnderusing: + last_series = &underusing_series; + break; + case BandwidthUsage::kBwOverusing: + last_series = &overusing_series; + break; + case BandwidthUsage::kLast: + RTC_NOTREACHED(); } } - IntervalSeries alr_state("ALR", "#555555", IntervalSeries::kHorizontal); - bool previously_in_alr = false; - int64_t alr_start = 0; - for (auto& alr : alr_state_events_) { - float y = ToCallTime(alr.timestamp); - if (!previously_in_alr && alr.in_alr) { - alr_start = alr.timestamp; - previously_in_alr = true; - } else if (previously_in_alr && !alr.in_alr) { - float x = ToCallTime(alr_start); - alr_state.intervals.emplace_back(x, y); - previously_in_alr = false; - } - } + delay_series.points.emplace_back(x, y); + } - if (previously_in_alr) { + RTC_CHECK(last_series); + last_series->intervals.emplace_back(last_detector_switch, end_time_); + + TimeSeries created_series("Probe cluster created.", LineStyle::kNone, + PointStyle::kHighlight); + for (auto& cluster : parsed_log_.bwe_probe_cluster_created_events()) { + float x = ToCallTime(cluster.log_time_us()); + float y = static_cast(cluster.bitrate_bps) / 1000; + created_series.points.emplace_back(x, y); + } + + TimeSeries result_series("Probing results.", LineStyle::kNone, + PointStyle::kHighlight); + for (auto& result : parsed_log_.bwe_probe_result_events()) { + if (result.bitrate_bps) { + float x = ToCallTime(result.log_time_us()); + float y = static_cast(*result.bitrate_bps) / 1000; + result_series.points.emplace_back(x, y); + } + } + + IntervalSeries alr_state("ALR", "#555555", IntervalSeries::kHorizontal); + bool previously_in_alr = false; + int64_t alr_start = 0; + for (auto& alr : parsed_log_.alr_state_events()) { + float y = ToCallTime(alr.log_time_us()); + if (!previously_in_alr && alr.in_alr) { + alr_start = alr.log_time_us(); + previously_in_alr = true; + } else if (previously_in_alr && !alr.in_alr) { float x = ToCallTime(alr_start); - float y = ToCallTime(end_time_); alr_state.intervals.emplace_back(x, y); + previously_in_alr = false; } - - if (show_detector_state) { - plot->AppendIntervalSeries(std::move(overusing_series)); - plot->AppendIntervalSeries(std::move(underusing_series)); - plot->AppendIntervalSeries(std::move(normal_series)); - } - - if (show_alr_state) { - plot->AppendIntervalSeries(std::move(alr_state)); - } - plot->AppendTimeSeries(std::move(loss_series)); - plot->AppendTimeSeries(std::move(delay_series)); - plot->AppendTimeSeries(std::move(created_series)); - plot->AppendTimeSeries(std::move(result_series)); } - // Overlay the incoming REMB over the outgoing bitrate - // and outgoing REMB over incoming bitrate. - PacketDirection remb_direction = - desired_direction == kOutgoingPacket ? kIncomingPacket : kOutgoingPacket; + if (previously_in_alr) { + float x = ToCallTime(alr_start); + float y = ToCallTime(end_time_); + alr_state.intervals.emplace_back(x, y); + } + + if (show_detector_state) { + plot->AppendIntervalSeries(std::move(overusing_series)); + plot->AppendIntervalSeries(std::move(underusing_series)); + plot->AppendIntervalSeries(std::move(normal_series)); + } + + if (show_alr_state) { + plot->AppendIntervalSeries(std::move(alr_state)); + } + plot->AppendTimeSeries(std::move(loss_series)); + plot->AppendTimeSeries(std::move(delay_series)); + plot->AppendTimeSeries(std::move(created_series)); + plot->AppendTimeSeries(std::move(result_series)); + + // Overlay the incoming REMB over the outgoing bitrate. TimeSeries remb_series("Remb", LineStyle::kStep); - std::multimap remb_packets; - for (const auto& kv : rtcp_packets_) { - if (kv.first.GetDirection() == remb_direction) { - for (const LoggedRtcpPacket& rtcp_packet : kv.second) { - if (rtcp_packet.type == kRtcpRemb) { - remb_packets.insert( - std::make_pair(rtcp_packet.timestamp, &rtcp_packet)); - } - } - } - } - - for (const auto& kv : remb_packets) { - const LoggedRtcpPacket* const rtcp = kv.second; - const rtcp::Remb* const remb = static_cast(rtcp->packet.get()); - float x = ToCallTime(rtcp->timestamp); - float y = static_cast(remb->bitrate_bps()) / 1000; + for (const auto& rtcp : parsed_log_.rembs(kIncomingPacket)) { + float x = ToCallTime(rtcp.log_time_us()); + float y = static_cast(rtcp.remb.bitrate_bps()) / 1000; remb_series.points.emplace_back(x, y); } plot->AppendTimeSeriesIfNotEmpty(std::move(remb_series)); plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); - if (desired_direction == webrtc::PacketDirection::kIncomingPacket) { - plot->SetTitle("Incoming RTP bitrate"); - } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) { - plot->SetTitle("Outgoing RTP bitrate"); - } + plot->SetTitle("Outgoing RTP bitrate"); } // For each SSRC, plot the bandwidth used by that stream. -void EventLogAnalyzer::CreateStreamBitrateGraph( - PacketDirection desired_direction, - Plot* plot) { - for (auto& kv : rtp_packets_) { - StreamId stream_id = kv.first; - const std::vector& packet_stream = kv.second; - // Filter on direction and SSRC. - if (stream_id.GetDirection() != desired_direction || - !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) { +void EventLogAnalyzer::CreateStreamBitrateGraph(PacketDirection direction, + Plot* plot) { + for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { + // Filter on SSRC. + if (!MatchingSsrc(stream.ssrc, desired_ssrc_)) { continue; } - TimeSeries time_series(GetStreamName(stream_id), LineStyle::kLine); + TimeSeries time_series(GetStreamName(direction, stream.ssrc), + LineStyle::kLine); + auto GetPacketSizeKilobits = [](const LoggedRtpPacket& packet) { + return packet.total_length * 8.0 / 1000.0; + }; MovingAverage( - [](const LoggedRtpPacket& packet) { - return packet.total_length * 8.0 / 1000.0; - }, - packet_stream, begin_time_, end_time_, window_duration_, step_, - &time_series); + GetPacketSizeKilobits, stream.packet_view, begin_time_, end_time_, + window_duration_, step_, &time_series); plot->AppendTimeSeries(std::move(time_series)); } plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); - if (desired_direction == webrtc::PacketDirection::kIncomingPacket) { - plot->SetTitle("Incoming bitrate per stream"); - } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) { - plot->SetTitle("Outgoing bitrate per stream"); - } + plot->SetTitle(GetDirectionAsString(direction) + " bitrate per stream"); } void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { - std::multimap outgoing_rtp; - std::multimap incoming_rtcp; + using RtpPacketType = LoggedRtpPacketOutgoing; + using TransportFeedbackType = LoggedRtcpPacketTransportFeedback; - for (const auto& kv : rtp_packets_) { - if (kv.first.GetDirection() == PacketDirection::kOutgoingPacket) { - for (const LoggedRtpPacket& rtp_packet : kv.second) - outgoing_rtp.insert(std::make_pair(rtp_packet.timestamp, &rtp_packet)); - } + // TODO(terelius): This could be provided by the parser. + std::multimap outgoing_rtp; + for (const auto& stream : parsed_log_.outgoing_rtp_packets_by_ssrc()) { + for (const RtpPacketType& rtp_packet : stream.outgoing_packets) + outgoing_rtp.insert( + std::make_pair(rtp_packet.rtp.log_time_us(), &rtp_packet)); } - for (const auto& kv : rtcp_packets_) { - if (kv.first.GetDirection() == PacketDirection::kIncomingPacket) { - for (const LoggedRtcpPacket& rtcp_packet : kv.second) - incoming_rtcp.insert( - std::make_pair(rtcp_packet.timestamp, &rtcp_packet)); - } - } + const std::vector& incoming_rtcp = + parsed_log_.transport_feedbacks(kIncomingPacket); SimulatedClock clock(0); BitrateObserver observer; @@ -1367,7 +1091,7 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { auto NextRtcpTime = [&]() { if (rtcp_iterator != incoming_rtcp.end()) - return static_cast(rtcp_iterator->first); + return static_cast(rtcp_iterator->log_time_us()); return std::numeric_limits::max(); }; @@ -1398,41 +1122,38 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { clock.AdvanceTimeMicroseconds(time_us - clock.TimeInMicroseconds()); if (clock.TimeInMicroseconds() >= NextRtcpTime()) { RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtcpTime()); - const LoggedRtcpPacket& rtcp = *rtcp_iterator->second; - if (rtcp.type == kRtcpTransportFeedback) { - cc.OnTransportFeedback( - *static_cast(rtcp.packet.get())); - std::vector feedback = cc.GetTransportFeedbackVector(); - SortPacketFeedbackVector(&feedback); - rtc::Optional bitrate_bps; - if (!feedback.empty()) { + cc.OnTransportFeedback(rtcp_iterator->transport_feedback); + std::vector feedback = cc.GetTransportFeedbackVector(); + SortPacketFeedbackVector(&feedback); + rtc::Optional bitrate_bps; + if (!feedback.empty()) { #if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE) - acknowledged_bitrate_estimator.IncomingPacketFeedbackVector(feedback); -#endif // !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE) - for (const PacketFeedback& packet : feedback) - acked_bitrate.Update(packet.payload_size, packet.arrival_time_ms); - bitrate_bps = acked_bitrate.Rate(feedback.back().arrival_time_ms); - } - float x = ToCallTime(clock.TimeInMicroseconds()); - float y = bitrate_bps.value_or(0) / 1000; - acked_time_series.points.emplace_back(x, y); -#if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE) - y = acknowledged_bitrate_estimator.bitrate_bps().value_or(0) / 1000; - acked_estimate_time_series.points.emplace_back(x, y); + acknowledged_bitrate_estimator.IncomingPacketFeedbackVector(feedback); #endif // !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE) + for (const PacketFeedback& packet : feedback) + acked_bitrate.Update(packet.payload_size, packet.arrival_time_ms); + bitrate_bps = acked_bitrate.Rate(feedback.back().arrival_time_ms); } + float x = ToCallTime(clock.TimeInMicroseconds()); + float y = bitrate_bps.value_or(0) / 1000; + acked_time_series.points.emplace_back(x, y); +#if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE) + y = acknowledged_bitrate_estimator.bitrate_bps().value_or(0) / 1000; + acked_estimate_time_series.points.emplace_back(x, y); +#endif // !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE) ++rtcp_iterator; } if (clock.TimeInMicroseconds() >= NextRtpTime()) { RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtpTime()); - const LoggedRtpPacket& rtp = *rtp_iterator->second; - if (rtp.header.extension.hasTransportSequenceNumber) { - RTC_DCHECK(rtp.header.extension.hasTransportSequenceNumber); - cc.AddPacket(rtp.header.ssrc, - rtp.header.extension.transportSequenceNumber, - rtp.total_length, PacedPacketInfo()); + const RtpPacketType& rtp_packet = *rtp_iterator->second; + if (rtp_packet.rtp.header.extension.hasTransportSequenceNumber) { + RTC_DCHECK(rtp_packet.rtp.header.extension.hasTransportSequenceNumber); + cc.AddPacket(rtp_packet.rtp.header.ssrc, + rtp_packet.rtp.header.extension.transportSequenceNumber, + rtp_packet.rtp.total_length, PacedPacketInfo()); rtc::SentPacket sent_packet( - rtp.header.extension.transportSequenceNumber, rtp.timestamp / 1000); + rtp_packet.rtp.header.extension.transportSequenceNumber, + rtp_packet.rtp.log_time_us() / 1000); cc.OnSentPacket(sent_packet); } ++rtp_iterator; @@ -1461,6 +1182,7 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { } void EventLogAnalyzer::CreateReceiveSideBweSimulationGraph(Plot* plot) { + using RtpPacketType = LoggedRtpPacketIncoming; class RembInterceptingPacketRouter : public PacketRouter { public: void OnReceiveBitrateChanged(const std::vector& ssrcs, @@ -1481,19 +1203,19 @@ void EventLogAnalyzer::CreateReceiveSideBweSimulationGraph(Plot* plot) { bool bitrate_updated_; }; - std::multimap incoming_rtp; + std::multimap incoming_rtp; - for (const auto& kv : rtp_packets_) { - if (kv.first.GetDirection() == PacketDirection::kIncomingPacket && - IsVideoSsrc(kv.first)) { - for (const LoggedRtpPacket& rtp_packet : kv.second) - incoming_rtp.insert(std::make_pair(rtp_packet.timestamp, &rtp_packet)); + for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) { + if (IsVideoSsrc(kIncomingPacket, stream.ssrc)) { + for (const auto& rtp_packet : stream.incoming_packets) + incoming_rtp.insert( + std::make_pair(rtp_packet.rtp.log_time_us(), &rtp_packet)); } } SimulatedClock clock(0); RembInterceptingPacketRouter packet_router; - // TODO(terelius): The PacketRrouter is the used as the RemoteBitrateObserver. + // TODO(terelius): The PacketRouter is used as the RemoteBitrateObserver. // Is this intentional? ReceiveSideCongestionController rscc(&clock, &packet_router); // TODO(holmer): Log the call config and use that here instead. @@ -1507,12 +1229,12 @@ void EventLogAnalyzer::CreateReceiveSideBweSimulationGraph(Plot* plot) { RateStatistics acked_bitrate(250, 8000); int64_t last_update_us = 0; for (const auto& kv : incoming_rtp) { - const LoggedRtpPacket& packet = *kv.second; - int64_t arrival_time_ms = packet.timestamp / 1000; - size_t payload = packet.total_length; /*Should subtract header?*/ - clock.AdvanceTimeMicroseconds(packet.timestamp - + const RtpPacketType& packet = *kv.second; + int64_t arrival_time_ms = packet.rtp.log_time_us() / 1000; + size_t payload = packet.rtp.total_length; /*Should subtract header?*/ + clock.AdvanceTimeMicroseconds(packet.rtp.log_time_us() - clock.TimeInMicroseconds()); - rscc.OnReceivedPacket(arrival_time_ms, payload, packet.header); + rscc.OnReceivedPacket(arrival_time_ms, payload, packet.rtp.header); acked_bitrate.Update(payload, arrival_time_ms); rtc::Optional bitrate_bps = acked_bitrate.Rate(arrival_time_ms); if (bitrate_bps) { @@ -1538,23 +1260,19 @@ void EventLogAnalyzer::CreateReceiveSideBweSimulationGraph(Plot* plot) { } void EventLogAnalyzer::CreateNetworkDelayFeedbackGraph(Plot* plot) { - std::multimap outgoing_rtp; - std::multimap incoming_rtcp; + using RtpPacketType = LoggedRtpPacketOutgoing; + using TransportFeedbackType = LoggedRtcpPacketTransportFeedback; - for (const auto& kv : rtp_packets_) { - if (kv.first.GetDirection() == PacketDirection::kOutgoingPacket) { - for (const LoggedRtpPacket& rtp_packet : kv.second) - outgoing_rtp.insert(std::make_pair(rtp_packet.timestamp, &rtp_packet)); - } + // TODO(terelius): This could be provided by the parser. + std::multimap outgoing_rtp; + for (const auto& stream : parsed_log_.outgoing_rtp_packets_by_ssrc()) { + for (const RtpPacketType& rtp_packet : stream.outgoing_packets) + outgoing_rtp.insert( + std::make_pair(rtp_packet.rtp.log_time_us(), &rtp_packet)); } - for (const auto& kv : rtcp_packets_) { - if (kv.first.GetDirection() == PacketDirection::kIncomingPacket) { - for (const LoggedRtcpPacket& rtcp_packet : kv.second) - incoming_rtcp.insert( - std::make_pair(rtcp_packet.timestamp, &rtcp_packet)); - } - } + const std::vector& incoming_rtcp = + parsed_log_.transport_feedbacks(kIncomingPacket); SimulatedClock clock(0); TransportFeedbackAdapter feedback_adapter(&clock); @@ -1576,7 +1294,7 @@ void EventLogAnalyzer::CreateNetworkDelayFeedbackGraph(Plot* plot) { auto NextRtcpTime = [&]() { if (rtcp_iterator != incoming_rtcp.end()) - return static_cast(rtcp_iterator->first); + return static_cast(rtcp_iterator->log_time_us()); return std::numeric_limits::max(); }; @@ -1586,37 +1304,34 @@ void EventLogAnalyzer::CreateNetworkDelayFeedbackGraph(Plot* plot) { clock.AdvanceTimeMicroseconds(time_us - clock.TimeInMicroseconds()); if (clock.TimeInMicroseconds() >= NextRtcpTime()) { RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtcpTime()); - const LoggedRtcpPacket& rtcp = *rtcp_iterator->second; - if (rtcp.type == kRtcpTransportFeedback) { - feedback_adapter.OnTransportFeedback( - *static_cast(rtcp.packet.get())); - std::vector feedback = - feedback_adapter.GetTransportFeedbackVector(); - SortPacketFeedbackVector(&feedback); - for (const PacketFeedback& packet : feedback) { - float x = ToCallTime(clock.TimeInMicroseconds()); - if (packet.send_time_ms == PacketFeedback::kNoSendTime) { - late_feedback_series.points.emplace_back(x, prev_y); - continue; - } - int64_t y = packet.arrival_time_ms - packet.send_time_ms; - prev_y = y; - estimated_base_delay_ms = std::min(y, estimated_base_delay_ms); - time_series.points.emplace_back(x, y); + feedback_adapter.OnTransportFeedback(rtcp_iterator->transport_feedback); + std::vector feedback = + feedback_adapter.GetTransportFeedbackVector(); + SortPacketFeedbackVector(&feedback); + for (const PacketFeedback& packet : feedback) { + float x = ToCallTime(clock.TimeInMicroseconds()); + if (packet.send_time_ms == PacketFeedback::kNoSendTime) { + late_feedback_series.points.emplace_back(x, prev_y); + continue; } + int64_t y = packet.arrival_time_ms - packet.send_time_ms; + prev_y = y; + estimated_base_delay_ms = std::min(y, estimated_base_delay_ms); + time_series.points.emplace_back(x, y); } ++rtcp_iterator; } if (clock.TimeInMicroseconds() >= NextRtpTime()) { RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtpTime()); - const LoggedRtpPacket& rtp = *rtp_iterator->second; - if (rtp.header.extension.hasTransportSequenceNumber) { - RTC_DCHECK(rtp.header.extension.hasTransportSequenceNumber); - feedback_adapter.AddPacket(rtp.header.ssrc, - rtp.header.extension.transportSequenceNumber, - rtp.total_length, PacedPacketInfo()); + const RtpPacketType& rtp_packet = *rtp_iterator->second; + if (rtp_packet.rtp.header.extension.hasTransportSequenceNumber) { + feedback_adapter.AddPacket( + rtp_packet.rtp.header.ssrc, + rtp_packet.rtp.header.extension.transportSequenceNumber, + rtp_packet.rtp.total_length, PacedPacketInfo()); feedback_adapter.OnSentPacket( - rtp.header.extension.transportSequenceNumber, rtp.timestamp / 1000); + rtp_packet.rtp.header.extension.transportSequenceNumber, + rtp_packet.rtp.log_time_us() / 1000); } ++rtp_iterator; } @@ -1637,40 +1352,10 @@ void EventLogAnalyzer::CreateNetworkDelayFeedbackGraph(Plot* plot) { plot->SetTitle("Network Delay Change."); } -std::vector> EventLogAnalyzer::GetFrameTimestamps() - const { - std::vector> timestamps; - size_t largest_stream_size = 0; - const std::vector* largest_video_stream = nullptr; - // Find the incoming video stream with the most number of packets that is - // not rtx. - for (const auto& kv : rtp_packets_) { - if (kv.first.GetDirection() == kIncomingPacket && - video_ssrcs_.find(kv.first) != video_ssrcs_.end() && - rtx_ssrcs_.find(kv.first) == rtx_ssrcs_.end() && - kv.second.size() > largest_stream_size) { - largest_stream_size = kv.second.size(); - largest_video_stream = &kv.second; - } - } - if (largest_video_stream == nullptr) { - for (auto& packet : *largest_video_stream) { - if (packet.header.markerBit) { - int64_t capture_ms = packet.header.timestamp / 90.0; - int64_t arrival_ms = packet.timestamp / 1000.0; - timestamps.push_back(std::make_pair(capture_ms, arrival_ms)); - } - } - } - return timestamps; -} - void EventLogAnalyzer::CreatePacerDelayGraph(Plot* plot) { - for (const auto& kv : rtp_packets_) { - const std::vector& packets = kv.second; - StreamId stream_id = kv.first; - if (stream_id.GetDirection() == kIncomingPacket) - continue; + for (const auto& stream : parsed_log_.outgoing_rtp_packets_by_ssrc()) { + const std::vector& packets = + stream.outgoing_packets; if (packets.size() < 2) { RTC_LOG(LS_WARNING) @@ -1678,11 +1363,15 @@ void EventLogAnalyzer::CreatePacerDelayGraph(Plot* plot) { "pacer delay with less than 2 packets in the stream"; continue; } + int64_t end_time_us = log_segments_.empty() + ? std::numeric_limits::max() + : log_segments_.front().second; rtc::Optional estimated_frequency = - EstimateRtpClockFrequency(packets); + EstimateRtpClockFrequency(packets, end_time_us); if (!estimated_frequency) continue; - if (IsVideoSsrc(stream_id) && *estimated_frequency != 90000) { + if (IsVideoSsrc(kOutgoingPacket, stream.ssrc) && + *estimated_frequency != 90000) { RTC_LOG(LS_WARNING) << "Video stream should use a 90 kHz clock but appears to use " << *estimated_frequency / 1000 << ". Discarding."; @@ -1690,21 +1379,22 @@ void EventLogAnalyzer::CreatePacerDelayGraph(Plot* plot) { } TimeSeries pacer_delay_series( - GetStreamName(stream_id) + "(" + + GetStreamName(kOutgoingPacket, stream.ssrc) + "(" + std::to_string(*estimated_frequency / 1000) + " kHz)", LineStyle::kLine, PointStyle::kHighlight); SeqNumUnwrapper timestamp_unwrapper; uint64_t first_capture_timestamp = - timestamp_unwrapper.Unwrap(packets.front().header.timestamp); - uint64_t first_send_timestamp = packets.front().timestamp; - for (LoggedRtpPacket packet : packets) { + timestamp_unwrapper.Unwrap(packets.front().rtp.header.timestamp); + uint64_t first_send_timestamp = packets.front().rtp.log_time_us(); + for (const auto& packet : packets) { double capture_time_ms = (static_cast(timestamp_unwrapper.Unwrap( - packet.header.timestamp)) - + packet.rtp.header.timestamp)) - first_capture_timestamp) / *estimated_frequency * 1000; double send_time_ms = - static_cast(packet.timestamp - first_send_timestamp) / 1000; - float x = ToCallTime(packet.timestamp); + static_cast(packet.rtp.log_time_us() - first_send_timestamp) / + 1000; + float x = ToCallTime(packet.rtp.log_time_us()); float y = send_time_ms - capture_time_ms; pacer_delay_series.points.emplace_back(x, y); } @@ -1717,58 +1407,52 @@ void EventLogAnalyzer::CreatePacerDelayGraph(Plot* plot) { "Delay from capture to send time. (First packet normalized to 0.)"); } -void EventLogAnalyzer::CreateTimestampGraph(Plot* plot) { - for (const auto& kv : rtp_packets_) { - const std::vector& rtp_packets = kv.second; - StreamId stream_id = kv.first; - - { - TimeSeries timestamp_data(GetStreamName(stream_id) + " capture-time", - LineStyle::kLine, PointStyle::kHighlight); - for (LoggedRtpPacket packet : rtp_packets) { - float x = ToCallTime(packet.timestamp); - float y = packet.header.timestamp; - timestamp_data.points.emplace_back(x, y); - } - plot->AppendTimeSeries(std::move(timestamp_data)); +void EventLogAnalyzer::CreateTimestampGraph(PacketDirection direction, + Plot* plot) { + for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { + TimeSeries rtp_timestamps( + GetStreamName(direction, stream.ssrc) + " capture-time", + LineStyle::kLine, PointStyle::kHighlight); + for (const auto& packet : stream.packet_view) { + float x = ToCallTime(packet.log_time_us()); + float y = packet.header.timestamp; + rtp_timestamps.points.emplace_back(x, y); } + plot->AppendTimeSeries(std::move(rtp_timestamps)); - { - auto kv = rtcp_packets_.find(stream_id); - if (kv != rtcp_packets_.end()) { - const auto& packets = kv->second; - TimeSeries timestamp_data( - GetStreamName(stream_id) + " rtcp capture-time", LineStyle::kLine, - PointStyle::kHighlight); - for (const LoggedRtcpPacket& rtcp : packets) { - if (rtcp.type != kRtcpSr) - continue; - rtcp::SenderReport* sr; - sr = static_cast(rtcp.packet.get()); - float x = ToCallTime(rtcp.timestamp); - float y = sr->rtp_timestamp(); - timestamp_data.points.emplace_back(x, y); - } - plot->AppendTimeSeries(std::move(timestamp_data)); - } + TimeSeries rtcp_timestamps( + GetStreamName(direction, stream.ssrc) + " rtcp capture-time", + LineStyle::kLine, PointStyle::kHighlight); + // TODO(terelius): Why only sender reports? + const auto& sender_reports = parsed_log_.sender_reports(direction); + for (const auto& rtcp : sender_reports) { + if (rtcp.sr.sender_ssrc() != stream.ssrc) + continue; + float x = ToCallTime(rtcp.log_time_us()); + float y = rtcp.sr.rtp_timestamp(); + rtcp_timestamps.points.emplace_back(x, y); } + plot->AppendTimeSeriesIfNotEmpty(std::move(rtcp_timestamps)); } plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Timestamp (90khz)", kBottomMargin, kTopMargin); - plot->SetTitle("Timestamps"); + plot->SetSuggestedYAxis(0, 1, "RTP timestamp", kBottomMargin, kTopMargin); + plot->SetTitle(GetDirectionAsString(direction) + " timestamps"); } void EventLogAnalyzer::CreateAudioEncoderTargetBitrateGraph(Plot* plot) { TimeSeries time_series("Audio encoder target bitrate", LineStyle::kLine, PointStyle::kHighlight); - ProcessPoints( - [](const AudioNetworkAdaptationEvent& ana_event) -> rtc::Optional { - if (ana_event.config.bitrate_bps) - return static_cast(*ana_event.config.bitrate_bps); - return rtc::nullopt; - }, - audio_network_adaptation_events_, begin_time_, &time_series); + auto GetAnaBitrateBps = [](const LoggedAudioNetworkAdaptationEvent& ana_event) + -> rtc::Optional { + if (ana_event.config.bitrate_bps) + return rtc::Optional( + static_cast(*ana_event.config.bitrate_bps)); + return rtc::nullopt; + }; + ProcessPoints( + GetAnaBitrateBps, parsed_log_.audio_network_adaptation_events(), + begin_time_, &time_series); plot->AppendTimeSeries(std::move(time_series)); plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); plot->SetSuggestedYAxis(0, 1, "Bitrate (bps)", kBottomMargin, kTopMargin); @@ -1778,14 +1462,16 @@ void EventLogAnalyzer::CreateAudioEncoderTargetBitrateGraph(Plot* plot) { void EventLogAnalyzer::CreateAudioEncoderFrameLengthGraph(Plot* plot) { TimeSeries time_series("Audio encoder frame length", LineStyle::kLine, PointStyle::kHighlight); - ProcessPoints( - [](const AudioNetworkAdaptationEvent& ana_event) { + auto GetAnaFrameLengthMs = + [](const LoggedAudioNetworkAdaptationEvent& ana_event) { if (ana_event.config.frame_length_ms) return rtc::Optional( static_cast(*ana_event.config.frame_length_ms)); return rtc::Optional(); - }, - audio_network_adaptation_events_, begin_time_, &time_series); + }; + ProcessPoints( + GetAnaFrameLengthMs, parsed_log_.audio_network_adaptation_events(), + begin_time_, &time_series); plot->AppendTimeSeries(std::move(time_series)); plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); plot->SetSuggestedYAxis(0, 1, "Frame length (ms)", kBottomMargin, kTopMargin); @@ -1795,14 +1481,16 @@ void EventLogAnalyzer::CreateAudioEncoderFrameLengthGraph(Plot* plot) { void EventLogAnalyzer::CreateAudioEncoderPacketLossGraph(Plot* plot) { TimeSeries time_series("Audio encoder uplink packet loss fraction", LineStyle::kLine, PointStyle::kHighlight); - ProcessPoints( - [](const AudioNetworkAdaptationEvent& ana_event) { + auto GetAnaPacketLoss = + [](const LoggedAudioNetworkAdaptationEvent& ana_event) { if (ana_event.config.uplink_packet_loss_fraction) return rtc::Optional(static_cast( *ana_event.config.uplink_packet_loss_fraction)); return rtc::Optional(); - }, - audio_network_adaptation_events_, begin_time_, &time_series); + }; + ProcessPoints( + GetAnaPacketLoss, parsed_log_.audio_network_adaptation_events(), + begin_time_, &time_series); plot->AppendTimeSeries(std::move(time_series)); plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); plot->SetSuggestedYAxis(0, 10, "Percent lost packets", kBottomMargin, @@ -1813,14 +1501,16 @@ void EventLogAnalyzer::CreateAudioEncoderPacketLossGraph(Plot* plot) { void EventLogAnalyzer::CreateAudioEncoderEnableFecGraph(Plot* plot) { TimeSeries time_series("Audio encoder FEC", LineStyle::kLine, PointStyle::kHighlight); - ProcessPoints( - [](const AudioNetworkAdaptationEvent& ana_event) { + auto GetAnaFecEnabled = + [](const LoggedAudioNetworkAdaptationEvent& ana_event) { if (ana_event.config.enable_fec) return rtc::Optional( static_cast(*ana_event.config.enable_fec)); return rtc::Optional(); - }, - audio_network_adaptation_events_, begin_time_, &time_series); + }; + ProcessPoints( + GetAnaFecEnabled, parsed_log_.audio_network_adaptation_events(), + begin_time_, &time_series); plot->AppendTimeSeries(std::move(time_series)); plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); plot->SetSuggestedYAxis(0, 1, "FEC (false/true)", kBottomMargin, kTopMargin); @@ -1830,14 +1520,16 @@ void EventLogAnalyzer::CreateAudioEncoderEnableFecGraph(Plot* plot) { void EventLogAnalyzer::CreateAudioEncoderEnableDtxGraph(Plot* plot) { TimeSeries time_series("Audio encoder DTX", LineStyle::kLine, PointStyle::kHighlight); - ProcessPoints( - [](const AudioNetworkAdaptationEvent& ana_event) { + auto GetAnaDtxEnabled = + [](const LoggedAudioNetworkAdaptationEvent& ana_event) { if (ana_event.config.enable_dtx) return rtc::Optional( static_cast(*ana_event.config.enable_dtx)); return rtc::Optional(); - }, - audio_network_adaptation_events_, begin_time_, &time_series); + }; + ProcessPoints( + GetAnaDtxEnabled, parsed_log_.audio_network_adaptation_events(), + begin_time_, &time_series); plot->AppendTimeSeries(std::move(time_series)); plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); plot->SetSuggestedYAxis(0, 1, "DTX (false/true)", kBottomMargin, kTopMargin); @@ -1847,14 +1539,16 @@ void EventLogAnalyzer::CreateAudioEncoderEnableDtxGraph(Plot* plot) { void EventLogAnalyzer::CreateAudioEncoderNumChannelsGraph(Plot* plot) { TimeSeries time_series("Audio encoder number of channels", LineStyle::kLine, PointStyle::kHighlight); - ProcessPoints( - [](const AudioNetworkAdaptationEvent& ana_event) { + auto GetAnaNumChannels = + [](const LoggedAudioNetworkAdaptationEvent& ana_event) { if (ana_event.config.num_channels) return rtc::Optional( static_cast(*ana_event.config.num_channels)); return rtc::Optional(); - }, - audio_network_adaptation_events_, begin_time_, &time_series); + }; + ProcessPoints( + GetAnaNumChannels, parsed_log_.audio_network_adaptation_events(), + begin_time_, &time_series); plot->AppendTimeSeries(std::move(time_series)); plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); plot->SetSuggestedYAxis(0, 1, "Number of channels (1 (mono)/2 (stereo))", @@ -1866,9 +1560,9 @@ class NetEqStreamInput : public test::NetEqInput { public: // Does not take any ownership, and all pointers must refer to valid objects // that outlive the one constructed. - NetEqStreamInput(const std::vector* packet_stream, - const std::vector* output_events_us, - rtc::Optional end_time_us) + NetEqStreamInput(const std::vector* packet_stream, + const std::vector* output_events_us, + rtc::Optional end_time_us) : packet_stream_(*packet_stream), packet_stream_it_(packet_stream_.begin()), output_events_us_it_(output_events_us->begin()), @@ -1882,11 +1576,11 @@ class NetEqStreamInput : public test::NetEqInput { if (packet_stream_it_ == packet_stream_.end()) { return rtc::nullopt; } - if (end_time_us_ && packet_stream_it_->timestamp > *end_time_us_) { + if (end_time_us_ && packet_stream_it_->rtp.log_time_us() > *end_time_us_) { return rtc::nullopt; } // Convert from us to ms. - return packet_stream_it_->timestamp / 1000; + return packet_stream_it_->rtp.log_time_us() / 1000; } rtc::Optional NextOutputEventTime() const override { @@ -1905,14 +1599,14 @@ class NetEqStreamInput : public test::NetEqInput { return std::unique_ptr(); } std::unique_ptr packet_data(new PacketData()); - packet_data->header = packet_stream_it_->header; + packet_data->header = packet_stream_it_->rtp.header; // Convert from us to ms. - packet_data->time_ms = packet_stream_it_->timestamp / 1000.0; + packet_data->time_ms = packet_stream_it_->rtp.log_time_us() / 1000.0; // This is a header-only "dummy" packet. Set the payload to all zeros, with // length according to the virtual length. - packet_data->payload.SetSize(packet_stream_it_->total_length - - packet_stream_it_->header_length); + packet_data->payload.SetSize(packet_stream_it_->rtp.total_length - + packet_stream_it_->rtp.header_length); std::fill_n(packet_data->payload.data(), packet_data->payload.size(), 0); ++packet_stream_it_; @@ -1931,15 +1625,15 @@ class NetEqStreamInput : public test::NetEqInput { if (packet_stream_it_ == packet_stream_.end()) { return rtc::nullopt; } - return packet_stream_it_->header; + return packet_stream_it_->rtp.header; } private: - const std::vector& packet_stream_; - std::vector::const_iterator packet_stream_it_; - std::vector::const_iterator output_events_us_it_; - const std::vector::const_iterator output_events_us_end_; - const rtc::Optional end_time_us_; + const std::vector& packet_stream_; + std::vector::const_iterator packet_stream_it_; + std::vector::const_iterator output_events_us_it_; + const std::vector::const_iterator output_events_us_end_; + const rtc::Optional end_time_us_; }; namespace { @@ -1947,9 +1641,9 @@ namespace { // the test and returns the NetEqDelayAnalyzer object that was used to // instrument the test. std::unique_ptr CreateNetEqTestAndRun( - const std::vector* packet_stream, - const std::vector* output_events_us, - rtc::Optional end_time_us, + const std::vector* packet_stream, + const std::vector* output_events_us, + rtc::Optional end_time_us, const std::string& replacement_file_name, int file_sample_rate_hz) { std::unique_ptr input( @@ -2004,34 +1698,35 @@ void EventLogAnalyzer::CreateAudioJitterBufferGraph( const std::string& replacement_file_name, int file_sample_rate_hz, Plot* plot) { - const auto& incoming_audio_kv = std::find_if( - rtp_packets_.begin(), rtp_packets_.end(), - [this](std::pair> kv) { - return kv.first.GetDirection() == kIncomingPacket && - this->IsAudioSsrc(kv.first); - }); - if (incoming_audio_kv == rtp_packets_.end()) { + const std::vector* audio_packets = nullptr; + uint32_t ssrc; + for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) { + if (IsAudioSsrc(kIncomingPacket, stream.ssrc)) { + audio_packets = &stream.incoming_packets; + ssrc = stream.ssrc; + break; + } + } + if (audio_packets == nullptr) { // No incoming audio stream found. return; } - const uint32_t ssrc = incoming_audio_kv->first.GetSsrc(); - - std::map>::const_iterator output_events_it = - audio_playout_events_.find(ssrc); - if (output_events_it == audio_playout_events_.end()) { + std::map>::const_iterator output_events_it = + parsed_log_.audio_playout_events().find(ssrc); + if (output_events_it == parsed_log_.audio_playout_events().end()) { // Could not find output events with SSRC matching the input audio stream. // Using the first available stream of output events. - output_events_it = audio_playout_events_.cbegin(); + output_events_it = parsed_log_.audio_playout_events().cbegin(); } - rtc::Optional end_time_us = + rtc::Optional end_time_us = log_segments_.empty() ? rtc::nullopt - : rtc::Optional(log_segments_.front().second); + : rtc::Optional(log_segments_.front().second); auto delay_cb = CreateNetEqTestAndRun( - &incoming_audio_kv->second, &output_events_it->second, end_time_us, + audio_packets, &output_events_it->second, end_time_us, replacement_file_name, file_sample_rate_hz); std::vector send_times_s; @@ -2047,28 +1742,27 @@ void EventLogAnalyzer::CreateAudioJitterBufferGraph( RTC_DCHECK_EQ(send_times_s.size(), playout_delay_ms.size()); RTC_DCHECK_EQ(send_times_s.size(), target_delay_ms.size()); - std::map time_series_packet_arrival; - std::map time_series_relative_packet_arrival; - std::map time_series_play_time; - std::map time_series_target_time; + std::map time_series_packet_arrival; + std::map time_series_relative_packet_arrival; + std::map time_series_play_time; + std::map time_series_target_time; float min_y_axis = 0.f; float max_y_axis = 0.f; - const StreamId stream_id = incoming_audio_kv->first; for (size_t i = 0; i < send_times_s.size(); ++i) { - time_series_packet_arrival[stream_id].points.emplace_back( + time_series_packet_arrival[ssrc].points.emplace_back( TimeSeriesPoint(send_times_s[i], arrival_delay_ms[i])); - time_series_relative_packet_arrival[stream_id].points.emplace_back( + time_series_relative_packet_arrival[ssrc].points.emplace_back( TimeSeriesPoint(send_times_s[i], corrected_arrival_delay_ms[i])); min_y_axis = std::min(min_y_axis, corrected_arrival_delay_ms[i]); max_y_axis = std::max(max_y_axis, corrected_arrival_delay_ms[i]); if (playout_delay_ms[i]) { - time_series_play_time[stream_id].points.emplace_back( + time_series_play_time[ssrc].points.emplace_back( TimeSeriesPoint(send_times_s[i], *playout_delay_ms[i])); min_y_axis = std::min(min_y_axis, *playout_delay_ms[i]); max_y_axis = std::max(max_y_axis, *playout_delay_ms[i]); } if (target_delay_ms[i]) { - time_series_target_time[stream_id].points.emplace_back( + time_series_target_time[ssrc].points.emplace_back( TimeSeriesPoint(send_times_s[i], *target_delay_ms[i])); min_y_axis = std::min(min_y_axis, *target_delay_ms[i]); max_y_axis = std::max(max_y_axis, *target_delay_ms[i]); @@ -2106,7 +1800,7 @@ void EventLogAnalyzer::CreateAudioJitterBufferGraph( void EventLogAnalyzer::CreateIceCandidatePairConfigGraph(Plot* plot) { std::map configs_by_cp_id; - for (const auto& config : ice_candidate_pair_configs_) { + for (const auto& config : parsed_log_.ice_candidate_pair_configs()) { if (configs_by_cp_id.find(config.candidate_pair_id) == configs_by_cp_id.end()) { const std::string candidate_pair_desc = @@ -2118,7 +1812,7 @@ void EventLogAnalyzer::CreateIceCandidatePairConfigGraph(Plot* plot) { candidate_pair_desc_by_id_[config.candidate_pair_id] = candidate_pair_desc; } - float x = ToCallTime(config.timestamp); + float x = ToCallTime(config.log_time_us()); float y = static_cast(config.type); configs_by_cp_id[config.candidate_pair_id].points.emplace_back(x, y); } @@ -2142,7 +1836,7 @@ std::string EventLogAnalyzer::GetCandidatePairLogDescriptionFromId( candidate_pair_desc_by_id_.end()) { return candidate_pair_desc_by_id_[candidate_pair_id]; } - for (const auto& config : ice_candidate_pair_configs_) { + for (const auto& config : parsed_log_.ice_candidate_pair_configs()) { // TODO(qingsi): Add the handling of the "Updated" config event after the // visualization of property change for candidate pairs is introduced. if (candidate_pair_desc_by_id_.find(config.candidate_pair_id) == @@ -2158,7 +1852,7 @@ std::string EventLogAnalyzer::GetCandidatePairLogDescriptionFromId( void EventLogAnalyzer::CreateIceConnectivityCheckGraph(Plot* plot) { std::map checks_by_cp_id; - for (const auto& event : ice_candidate_pair_events_) { + for (const auto& event : parsed_log_.ice_candidate_pair_events()) { if (checks_by_cp_id.find(event.candidate_pair_id) == checks_by_cp_id.end()) { checks_by_cp_id[event.candidate_pair_id] = TimeSeries( @@ -2166,7 +1860,7 @@ void EventLogAnalyzer::CreateIceConnectivityCheckGraph(Plot* plot) { GetCandidatePairLogDescriptionFromId(event.candidate_pair_id), LineStyle::kNone, PointStyle::kHighlight); } - float x = ToCallTime(event.timestamp); + float x = ToCallTime(event.log_time_us()); float y = static_cast(event.type); checks_by_cp_id[event.candidate_pair_id].points.emplace_back(x, y); } @@ -2182,163 +1876,176 @@ void EventLogAnalyzer::CreateIceConnectivityCheckGraph(Plot* plot) { plot->SetTitle("[IceEventLog] ICE connectivity checks"); } -void EventLogAnalyzer::Notification( - std::unique_ptr notification) { - notifications_.push_back(std::move(notification)); -} - void EventLogAnalyzer::PrintNotifications(FILE* file) { - if (notifications_.size() == 0) - return; fprintf(file, "========== TRIAGE NOTIFICATIONS ==========\n"); - for (const auto& notification : notifications_) { - rtc::Optional call_timestamp = notification->Time(); - if (call_timestamp.has_value()) { - fprintf(file, "%3.3lf s : %s\n", call_timestamp.value(), - notification->ToString().c_str()); - } else { - fprintf(file, " : %s\n", notification->ToString().c_str()); - } + for (const auto& alert : incoming_rtp_recv_time_gaps_) { + fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str()); + } + for (const auto& alert : incoming_rtcp_recv_time_gaps_) { + fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str()); + } + for (const auto& alert : outgoing_rtp_send_time_gaps_) { + fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str()); + } + for (const auto& alert : outgoing_rtcp_send_time_gaps_) { + fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str()); + } + for (const auto& alert : incoming_seq_num_jumps_) { + fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str()); + } + for (const auto& alert : incoming_capture_time_jumps_) { + fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str()); + } + for (const auto& alert : outgoing_seq_num_jumps_) { + fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str()); + } + for (const auto& alert : outgoing_capture_time_jumps_) { + fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str()); + } + for (const auto& alert : outgoing_high_loss_alerts_) { + fprintf(file, " : %s\n", alert.ToString().c_str()); } fprintf(file, "========== END TRIAGE NOTIFICATIONS ==========\n"); } +void EventLogAnalyzer::CreateStreamGapAlerts(PacketDirection direction) { + // With 100 packets/s (~800kbps), false positives would require 10 s without + // data. + constexpr int64_t kMaxSeqNumJump = 1000; + // With a 90 kHz clock, false positives would require 10 s without data. + constexpr int64_t kMaxCaptureTimeJump = 900000; + + int64_t end_time_us = log_segments_.empty() + ? std::numeric_limits::max() + : log_segments_.front().second; + + SeqNumUnwrapper seq_num_unwrapper; + rtc::Optional last_seq_num; + SeqNumUnwrapper capture_time_unwrapper; + rtc::Optional last_capture_time; + // Check for gaps in sequence numbers and capture timestamps. + for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { + for (const auto& packet : stream.packet_view) { + if (packet.log_time_us() > end_time_us) { + // Only process the first (LOG_START, LOG_END) segment. + break; + } + + int64_t seq_num = seq_num_unwrapper.Unwrap(packet.header.sequenceNumber); + if (last_seq_num.has_value() && + std::abs(seq_num - last_seq_num.value()) > kMaxSeqNumJump) { + Alert_SeqNumJump(direction, ToCallTime(packet.log_time_us()), + packet.header.ssrc); + } + last_seq_num.emplace(seq_num); + + int64_t capture_time = + capture_time_unwrapper.Unwrap(packet.header.timestamp); + if (last_capture_time.has_value() && + std::abs(capture_time - last_capture_time.value()) > + kMaxCaptureTimeJump) { + Alert_CaptureTimeJump(direction, ToCallTime(packet.log_time_us()), + packet.header.ssrc); + } + last_capture_time.emplace(capture_time); + } + } +} + +void EventLogAnalyzer::CreateTransmissionGapAlerts(PacketDirection direction) { + constexpr int64_t kMaxRtpTransmissionGap = 500000; + constexpr int64_t kMaxRtcpTransmissionGap = 2000000; + int64_t end_time_us = log_segments_.empty() + ? std::numeric_limits::max() + : log_segments_.front().second; + + // TODO(terelius): The parser could provide a list of all packets, ordered + // by time, for each direction. + std::multimap rtp_in_direction; + for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { + for (const LoggedRtpPacket& rtp_packet : stream.packet_view) + rtp_in_direction.emplace(rtp_packet.log_time_us(), &rtp_packet); + } + rtc::Optional last_rtp_time; + for (const auto& kv : rtp_in_direction) { + int64_t timestamp = kv.first; + if (timestamp > end_time_us) { + // Only process the first (LOG_START, LOG_END) segment. + break; + } + int64_t duration = timestamp - last_rtp_time.value_or(0); + if (last_rtp_time.has_value() && duration > kMaxRtpTransmissionGap) { + // No packet sent/received for more than 500 ms. + Alert_RtpLogTimeGap(direction, ToCallTime(timestamp), duration / 1000); + } + last_rtp_time.emplace(timestamp); + } + + rtc::Optional last_rtcp_time; + if (direction == kIncomingPacket) { + for (const auto& rtcp : parsed_log_.incoming_rtcp_packets()) { + if (rtcp.log_time_us() > end_time_us) { + // Only process the first (LOG_START, LOG_END) segment. + break; + } + int64_t duration = rtcp.log_time_us() - last_rtcp_time.value_or(0); + if (last_rtcp_time.has_value() && duration > kMaxRtcpTransmissionGap) { + // No feedback sent/received for more than 2000 ms. + Alert_RtcpLogTimeGap(direction, ToCallTime(rtcp.log_time_us()), + duration / 1000); + } + last_rtcp_time.emplace(rtcp.log_time_us()); + } + } else { + for (const auto& rtcp : parsed_log_.outgoing_rtcp_packets()) { + if (rtcp.log_time_us() > end_time_us) { + // Only process the first (LOG_START, LOG_END) segment. + break; + } + int64_t duration = rtcp.log_time_us() - last_rtcp_time.value_or(0); + if (last_rtcp_time.has_value() && duration > kMaxRtcpTransmissionGap) { + // No feedback sent/received for more than 2000 ms. + Alert_RtcpLogTimeGap(direction, ToCallTime(rtcp.log_time_us()), + duration / 1000); + } + last_rtcp_time.emplace(rtcp.log_time_us()); + } + } +} + // TODO(terelius): Notifications could possibly be generated by the same code // that produces the graphs. There is some code duplication that could be // avoided, but that might be solved anyway when we move functionality from the // analyzer to the parser. void EventLogAnalyzer::CreateTriageNotifications() { - uint64_t end_time_us = log_segments_.empty() - ? std::numeric_limits::max() - : log_segments_.front().second; - // Check for gaps in sequence numbers and capture timestamps. - for (auto& kv : rtp_packets_) { - StreamId stream_id = kv.first; - const std::vector& packet_stream = kv.second; + CreateStreamGapAlerts(kIncomingPacket); + CreateStreamGapAlerts(kOutgoingPacket); + CreateTransmissionGapAlerts(kIncomingPacket); + CreateTransmissionGapAlerts(kOutgoingPacket); - SeqNumUnwrapper seq_no_unwrapper; - rtc::Optional last_seq_no; - SeqNumUnwrapper timestamp_unwrapper; - rtc::Optional last_timestamp; - for (const auto& packet : packet_stream) { - if (packet.timestamp > end_time_us) { - // Only process the first (LOG_START, LOG_END) segment. - break; - } - int64_t seq_no = seq_no_unwrapper.Unwrap(packet.header.sequenceNumber); - if (last_seq_no.has_value() && - std::abs(seq_no - last_seq_no.value()) > 1000) { - // With roughly 100 packets per second (~800kbps), this would require 10 - // seconds without data to trigger incorrectly. - if (stream_id.GetDirection() == kIncomingPacket) { - Notification(rtc::MakeUnique( - ToCallTime(packet.timestamp), packet.header.ssrc)); - } else { - Notification(rtc::MakeUnique( - ToCallTime(packet.timestamp), packet.header.ssrc)); - } - } - last_seq_no.emplace(seq_no); - int64_t timestamp = timestamp_unwrapper.Unwrap(packet.header.timestamp); - if (last_timestamp.has_value() && - std::abs(timestamp - last_timestamp.value()) > 900000) { - // With a 90 kHz clock, this would require 10 seconds without data to - // trigger incorrectly. - if (stream_id.GetDirection() == kIncomingPacket) { - Notification(rtc::MakeUnique( - ToCallTime(packet.timestamp), packet.header.ssrc)); - } else { - Notification(rtc::MakeUnique( - ToCallTime(packet.timestamp), packet.header.ssrc)); - } - } - last_timestamp.emplace(timestamp); - } - } - - // Check for gaps in RTP and RTCP streams - for (const auto direction : - {PacketDirection::kIncomingPacket, PacketDirection::kOutgoingPacket}) { - // TODO(terelius): The parser could provide a list of all packets, ordered - // by time, for each direction. - std::multimap rtp_in_direction; - for (const auto& kv : rtp_packets_) { - if (kv.first.GetDirection() == direction) { - for (const LoggedRtpPacket& rtp_packet : kv.second) - rtp_in_direction.emplace(rtp_packet.timestamp, &rtp_packet); - } - } - rtc::Optional last_rtp_packet; - for (const auto& kv : rtp_in_direction) { - uint64_t timestamp = kv.first; - if (timestamp > end_time_us) { - // Only process the first (LOG_START, LOG_END) segment. - break; - } - int64_t duration = timestamp - last_rtp_packet.value_or(0); - if (last_rtp_packet.has_value() && duration > 500000) { - // No incoming packet for more than 500 ms. - if (direction == kIncomingPacket) { - Notification(rtc::MakeUnique( - ToCallTime(timestamp), duration / 1000)); - } else { - Notification(rtc::MakeUnique( - ToCallTime(timestamp), duration / 1000)); - } - } - last_rtp_packet.emplace(timestamp); - } - - // TODO(terelius): The parser could provide a list of all packets, ordered - // by time, for each direction. - std::multimap rtcp_in_direction; - for (const auto& kv : rtcp_packets_) { - if (kv.first.GetDirection() == direction) { - for (const LoggedRtcpPacket& rtcp_packet : kv.second) - rtcp_in_direction.emplace(rtcp_packet.timestamp, &rtcp_packet); - } - } - rtc::Optional last_incoming_rtcp_packet; - for (const auto& kv : rtcp_in_direction) { - uint64_t timestamp = kv.first; - if (timestamp > end_time_us) { - // Only process the first (LOG_START, LOG_END) segment. - break; - } - int64_t duration = timestamp - last_incoming_rtcp_packet.value_or(0); - if (last_incoming_rtcp_packet.has_value() && duration > 2000000) { - // No incoming feedback for more than 2000 ms. - if (direction == kIncomingPacket) { - Notification(rtc::MakeUnique( - ToCallTime(timestamp), duration / 1000)); - } else { - Notification(rtc::MakeUnique( - ToCallTime(timestamp), duration / 1000)); - } - } - last_incoming_rtcp_packet.emplace(timestamp); - } - } + int64_t end_time_us = log_segments_.empty() + ? std::numeric_limits::max() + : log_segments_.front().second; + constexpr double kMaxLossFraction = 0.05; // Loss feedback int64_t total_lost_packets = 0; int64_t total_expected_packets = 0; - for (auto& bwe_update : bwe_loss_updates_) { - if (bwe_update.timestamp > end_time_us) { + for (auto& bwe_update : parsed_log_.bwe_loss_updates()) { + if (bwe_update.log_time_us() > end_time_us) { // Only process the first (LOG_START, LOG_END) segment. break; } - int64_t lost_packets = static_cast(bwe_update.fraction_loss) / 255 * + int64_t lost_packets = static_cast(bwe_update.fraction_lost) / 255 * bwe_update.expected_packets; total_lost_packets += lost_packets; total_expected_packets += bwe_update.expected_packets; } double avg_outgoing_loss = static_cast(total_lost_packets) / total_expected_packets; - if (avg_outgoing_loss > 0.05) { - Notification(rtc::MakeUnique(avg_outgoing_loss)); + if (avg_outgoing_loss > kMaxLossFraction) { + Alert_OutgoingHighLoss(avg_outgoing_loss); } } -} // namespace plotting } // namespace webrtc diff --git a/rtc_tools/event_log_visualizer/analyzer.h b/rtc_tools/event_log_visualizer/analyzer.h index a8fedb8341..b37de21dcf 100644 --- a/rtc_tools/event_log_visualizer/analyzer.h +++ b/rtc_tools/event_log_visualizer/analyzer.h @@ -18,54 +18,12 @@ #include #include -#include "logging/rtc_event_log/rtc_event_log_parser.h" -#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h" -#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" -#include "modules/rtp_rtcp/source/rtcp_packet.h" -#include "rtc_base/function_view.h" +#include "logging/rtc_event_log/rtc_event_log_parser2.h" +#include "rtc_base/strings/string_builder.h" #include "rtc_tools/event_log_visualizer/plot_base.h" #include "rtc_tools/event_log_visualizer/triage_notifications.h" namespace webrtc { -namespace plotting { - -struct LoggedRtpPacket { - LoggedRtpPacket(uint64_t timestamp, - RTPHeader header, - size_t header_length, - size_t total_length) - : timestamp(timestamp), - header(header), - header_length(header_length), - total_length(total_length) {} - uint64_t timestamp; - // TODO(terelius): This allocates space for 15 CSRCs even if none are used. - RTPHeader header; - size_t header_length; - size_t total_length; -}; - -struct LoggedRtcpPacket { - LoggedRtcpPacket(uint64_t timestamp, - RTCPPacketType rtcp_type, - std::unique_ptr rtcp_packet) - : timestamp(timestamp), type(rtcp_type), packet(std::move(rtcp_packet)) {} - uint64_t timestamp; - RTCPPacketType type; - std::unique_ptr packet; -}; - -struct LossBasedBweUpdate { - uint64_t timestamp; - int32_t new_bitrate; - uint8_t fraction_loss; - int32_t expected_packets; -}; - -struct AudioNetworkAdaptationEvent { - uint64_t timestamp; - AudioEncoderRuntimeConfig config; -}; class EventLogAnalyzer { public: @@ -74,14 +32,13 @@ class EventLogAnalyzer { // modified while the EventLogAnalyzer is being used. explicit EventLogAnalyzer(const ParsedRtcEventLog& log); - void CreatePacketGraph(PacketDirection desired_direction, Plot* plot); + void CreatePacketGraph(PacketDirection direction, Plot* plot); - void CreateAccumulatedPacketsGraph(PacketDirection desired_direction, - Plot* plot); + void CreateAccumulatedPacketsGraph(PacketDirection direction, Plot* plot); void CreatePlayoutGraph(Plot* plot); - void CreateAudioLevelGraph(Plot* plot); + void CreateAudioLevelGraph(PacketDirection direction, Plot* plot); void CreateSequenceNumberGraph(Plot* plot); @@ -92,19 +49,20 @@ class EventLogAnalyzer { void CreateFractionLossGraph(Plot* plot); - void CreateTotalBitrateGraph(PacketDirection desired_direction, - Plot* plot, - bool show_detector_state = false, - bool show_alr_state = false); + void CreateTotalIncomingBitrateGraph(Plot* plot); + void CreateTotalOutgoingBitrateGraph(Plot* plot, + bool show_detector_state = false, + bool show_alr_state = false); - void CreateStreamBitrateGraph(PacketDirection desired_direction, Plot* plot); + void CreateStreamBitrateGraph(PacketDirection direction, Plot* plot); void CreateSendSideBweSimulationGraph(Plot* plot); void CreateReceiveSideBweSimulationGraph(Plot* plot); void CreateNetworkDelayFeedbackGraph(Plot* plot); void CreatePacerDelayGraph(Plot* plot); - void CreateTimestampGraph(Plot* plot); + + void CreateTimestampGraph(PacketDirection direction, Plot* plot); void CreateAudioEncoderTargetBitrateGraph(Plot* plot); void CreateAudioEncoderFrameLengthGraph(Plot* plot); @@ -119,55 +77,114 @@ class EventLogAnalyzer { void CreateIceCandidatePairConfigGraph(Plot* plot); void CreateIceConnectivityCheckGraph(Plot* plot); - // Returns a vector of capture and arrival timestamps for the video frames - // of the stream with the most number of frames. - std::vector> GetFrameTimestamps() const; - void CreateTriageNotifications(); void PrintNotifications(FILE* file); private: - class StreamId { - public: - StreamId(uint32_t ssrc, webrtc::PacketDirection direction) - : ssrc_(ssrc), direction_(direction) {} - bool operator<(const StreamId& other) const { - return std::tie(ssrc_, direction_) < - std::tie(other.ssrc_, other.direction_); + bool IsRtxSsrc(PacketDirection direction, uint32_t ssrc) const { + if (direction == kIncomingPacket) { + return parsed_log_.incoming_rtx_ssrcs().find(ssrc) != + parsed_log_.incoming_rtx_ssrcs().end(); + } else { + return parsed_log_.outgoing_rtx_ssrcs().find(ssrc) != + parsed_log_.outgoing_rtx_ssrcs().end(); } - bool operator==(const StreamId& other) const { - return std::tie(ssrc_, direction_) == - std::tie(other.ssrc_, other.direction_); + } + + bool IsVideoSsrc(PacketDirection direction, uint32_t ssrc) const { + if (direction == kIncomingPacket) { + return parsed_log_.incoming_video_ssrcs().find(ssrc) != + parsed_log_.incoming_video_ssrcs().end(); + } else { + return parsed_log_.outgoing_video_ssrcs().find(ssrc) != + parsed_log_.outgoing_video_ssrcs().end(); } - uint32_t GetSsrc() const { return ssrc_; } - webrtc::PacketDirection GetDirection() const { return direction_; } + } - private: - uint32_t ssrc_; - webrtc::PacketDirection direction_; - }; + bool IsAudioSsrc(PacketDirection direction, uint32_t ssrc) const { + if (direction == kIncomingPacket) { + return parsed_log_.incoming_audio_ssrcs().find(ssrc) != + parsed_log_.incoming_audio_ssrcs().end(); + } else { + return parsed_log_.outgoing_audio_ssrcs().find(ssrc) != + parsed_log_.outgoing_audio_ssrcs().end(); + } + } - template - void CreateAccumulatedPacketsTimeSeries( - PacketDirection desired_direction, - Plot* plot, - const std::map>& packets, - const std::string& label_prefix); + template + void CreateAccumulatedPacketsTimeSeries(Plot* plot, + const IterableType& packets, + const std::string& label); - bool IsRtxSsrc(StreamId stream_id) const; + void CreateStreamGapAlerts(PacketDirection direction); + void CreateTransmissionGapAlerts(PacketDirection direction); - bool IsVideoSsrc(StreamId stream_id) const; - - bool IsAudioSsrc(StreamId stream_id) const; - - std::string GetStreamName(StreamId stream_id) const; - - rtc::Optional EstimateRtpClockFrequency( - const std::vector& packets) const; + std::string GetStreamName(PacketDirection direction, uint32_t ssrc) const { + char buffer[200]; + rtc::SimpleStringBuilder name(buffer); + if (IsAudioSsrc(direction, ssrc)) { + name << "Audio "; + } else if (IsVideoSsrc(direction, ssrc)) { + name << "Video "; + } else { + name << "Unknown "; + } + if (IsRtxSsrc(direction, ssrc)) { + name << "RTX "; + } + if (direction == kIncomingPacket) + name << "(In) "; + else + name << "(Out) "; + name << "SSRC " << ssrc; + return name.str(); + } float ToCallTime(int64_t timestamp) const; - void Notification(std::unique_ptr notification); + void Alert_RtpLogTimeGap(PacketDirection direction, + float time_seconds, + int64_t duration) { + if (direction == kIncomingPacket) { + incoming_rtp_recv_time_gaps_.emplace_back(time_seconds, duration); + } else { + outgoing_rtp_send_time_gaps_.emplace_back(time_seconds, duration); + } + } + + void Alert_RtcpLogTimeGap(PacketDirection direction, + float time_seconds, + int64_t duration) { + if (direction == kIncomingPacket) { + incoming_rtcp_recv_time_gaps_.emplace_back(time_seconds, duration); + } else { + outgoing_rtcp_send_time_gaps_.emplace_back(time_seconds, duration); + } + } + + void Alert_SeqNumJump(PacketDirection direction, + float time_seconds, + uint32_t ssrc) { + if (direction == kIncomingPacket) { + incoming_seq_num_jumps_.emplace_back(time_seconds, ssrc); + } else { + outgoing_seq_num_jumps_.emplace_back(time_seconds, ssrc); + } + } + + void Alert_CaptureTimeJump(PacketDirection direction, + float time_seconds, + uint32_t ssrc) { + if (direction == kIncomingPacket) { + incoming_capture_time_jumps_.emplace_back(time_seconds, ssrc); + } else { + outgoing_capture_time_jumps_.emplace_back(time_seconds, ssrc); + } + } + + void Alert_OutgoingHighLoss(double avg_loss_fraction) { + outgoing_high_loss_alerts_.emplace_back(avg_loss_fraction); + } std::string GetCandidatePairLogDescriptionFromId(uint32_t candidate_pair_id); @@ -177,50 +194,19 @@ class EventLogAnalyzer { // If left empty, all SSRCs will be considered relevant. std::vector desired_ssrc_; - // Tracks what each stream is configured for. Note that a single SSRC can be - // in several sets. For example, the SSRC used for sending video over RTX - // will appear in both video_ssrcs_ and rtx_ssrcs_. In the unlikely case that - // an SSRC is reconfigured to a different media type mid-call, it will also - // appear in multiple sets. - std::set rtx_ssrcs_; - std::set video_ssrcs_; - std::set audio_ssrcs_; - - // Maps a stream identifier consisting of ssrc and direction to the parsed - // RTP headers in that stream. Header extensions are parsed if the stream - // has been configured. - std::map> rtp_packets_; - - std::map> rtcp_packets_; - - // Maps an SSRC to the timestamps of parsed audio playout events. - std::map> audio_playout_events_; - // Stores the timestamps for all log segments, in the form of associated start // and end events. - std::vector> log_segments_; + std::vector> log_segments_; - // A list of all updates from the send-side loss-based bandwidth estimator. - std::vector bwe_loss_updates_; - - std::vector audio_network_adaptation_events_; - - std::vector - bwe_probe_cluster_created_events_; - - std::vector bwe_probe_result_events_; - - std::vector bwe_delay_updates_; - - std::vector> notifications_; - - std::vector alr_state_events_; - - std::vector - ice_candidate_pair_configs_; - - std::vector - ice_candidate_pair_events_; + std::vector incoming_rtp_recv_time_gaps_; + std::vector incoming_rtcp_recv_time_gaps_; + std::vector outgoing_rtp_send_time_gaps_; + std::vector outgoing_rtcp_send_time_gaps_; + std::vector incoming_seq_num_jumps_; + std::vector incoming_capture_time_jumps_; + std::vector outgoing_seq_num_jumps_; + std::vector outgoing_capture_time_jumps_; + std::vector outgoing_high_loss_alerts_; std::map candidate_pair_desc_by_id_; @@ -228,18 +214,17 @@ class EventLogAnalyzer { // The generated data points will be |step_| microseconds apart. // Only events occuring at most |window_duration_| microseconds before the // current data point will be part of the average. - uint64_t window_duration_; - uint64_t step_; + int64_t window_duration_; + int64_t step_; // First and last events of the log. - uint64_t begin_time_; - uint64_t end_time_; + int64_t begin_time_; + int64_t end_time_; // Duration (in seconds) of log file. float call_duration_s_; }; -} // namespace plotting } // namespace webrtc #endif // RTC_TOOLS_EVENT_LOG_VISUALIZER_ANALYZER_H_ diff --git a/rtc_tools/event_log_visualizer/main.cc b/rtc_tools/event_log_visualizer/main.cc index 2e7a79ec3d..3dce29001a 100644 --- a/rtc_tools/event_log_visualizer/main.cc +++ b/rtc_tools/event_log_visualizer/main.cc @@ -10,7 +10,7 @@ #include -#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "logging/rtc_event_log/rtc_event_log_parser2.h" #include "rtc_base/flags.h" #include "rtc_tools/event_log_visualizer/analyzer.h" #include "rtc_tools/event_log_visualizer/plot_base.h" @@ -143,10 +143,15 @@ DEFINE_bool(show_alr_state, false, "Show the state ALR state on the total bitrate graph"); -DEFINE_bool( - print_triage_notifications, - false, - "Print triage notifications, i.e. a list of suspicious looking events."); +DEFINE_bool(parse_unconfigured_header_extensions, + true, + "Attempt to parse unconfigured header extensions using the default " + "WebRTC mapping. This can give very misleading results if the " + "application negotiates a different mapping."); + +DEFINE_bool(print_triage_alerts, + false, + "Print triage alerts, i.e. a list of potential problems."); void SetAllPlotFlags(bool setting); @@ -209,7 +214,13 @@ int main(int argc, char* argv[]) { std::string filename = argv[1]; - webrtc::ParsedRtcEventLog parsed_log; + webrtc::ParsedRtcEventLog::UnconfiguredHeaderExtensions header_extensions = + webrtc::ParsedRtcEventLog::UnconfiguredHeaderExtensions::kDontParse; + if (FLAG_parse_unconfigured_header_extensions) { + header_extensions = webrtc::ParsedRtcEventLog:: + UnconfiguredHeaderExtensions::kAttemptWebrtcDefaultConfig; + } + webrtc::ParsedRtcEventLog parsed_log(header_extensions); if (!parsed_log.ParseFile(filename)) { std::cerr << "Could not parse the entire log file." << std::endl; @@ -218,31 +229,34 @@ int main(int argc, char* argv[]) { << std::endl; } - webrtc::plotting::EventLogAnalyzer analyzer(parsed_log); - std::unique_ptr collection( - new webrtc::plotting::PythonPlotCollection()); + webrtc::EventLogAnalyzer analyzer(parsed_log); + std::unique_ptr collection( + new webrtc::PythonPlotCollection()); if (FLAG_plot_incoming_packet_sizes) { - analyzer.CreatePacketGraph(webrtc::PacketDirection::kIncomingPacket, + analyzer.CreatePacketGraph(webrtc::kIncomingPacket, collection->AppendNewPlot()); } if (FLAG_plot_outgoing_packet_sizes) { - analyzer.CreatePacketGraph(webrtc::PacketDirection::kOutgoingPacket, + analyzer.CreatePacketGraph(webrtc::kOutgoingPacket, collection->AppendNewPlot()); } if (FLAG_plot_incoming_packet_count) { - analyzer.CreateAccumulatedPacketsGraph( - webrtc::PacketDirection::kIncomingPacket, collection->AppendNewPlot()); + analyzer.CreateAccumulatedPacketsGraph(webrtc::kIncomingPacket, + collection->AppendNewPlot()); } if (FLAG_plot_outgoing_packet_count) { - analyzer.CreateAccumulatedPacketsGraph( - webrtc::PacketDirection::kOutgoingPacket, collection->AppendNewPlot()); + analyzer.CreateAccumulatedPacketsGraph(webrtc::kOutgoingPacket, + collection->AppendNewPlot()); } if (FLAG_plot_audio_playout) { analyzer.CreatePlayoutGraph(collection->AppendNewPlot()); } if (FLAG_plot_audio_level) { - analyzer.CreateAudioLevelGraph(collection->AppendNewPlot()); + analyzer.CreateAudioLevelGraph(webrtc::kIncomingPacket, + collection->AppendNewPlot()); + analyzer.CreateAudioLevelGraph(webrtc::kOutgoingPacket, + collection->AppendNewPlot()); } if (FLAG_plot_incoming_sequence_number_delta) { analyzer.CreateSequenceNumberGraph(collection->AppendNewPlot()); @@ -257,23 +271,19 @@ int main(int argc, char* argv[]) { analyzer.CreateIncomingPacketLossGraph(collection->AppendNewPlot()); } if (FLAG_plot_incoming_bitrate) { - analyzer.CreateTotalBitrateGraph(webrtc::PacketDirection::kIncomingPacket, - collection->AppendNewPlot(), - FLAG_show_detector_state, - FLAG_show_alr_state); + analyzer.CreateTotalIncomingBitrateGraph(collection->AppendNewPlot()); } if (FLAG_plot_outgoing_bitrate) { - analyzer.CreateTotalBitrateGraph(webrtc::PacketDirection::kOutgoingPacket, - collection->AppendNewPlot(), - FLAG_show_detector_state, - FLAG_show_alr_state); + analyzer.CreateTotalOutgoingBitrateGraph(collection->AppendNewPlot(), + FLAG_show_detector_state, + FLAG_show_alr_state); } if (FLAG_plot_incoming_stream_bitrate) { - analyzer.CreateStreamBitrateGraph(webrtc::PacketDirection::kIncomingPacket, + analyzer.CreateStreamBitrateGraph(webrtc::kIncomingPacket, collection->AppendNewPlot()); } if (FLAG_plot_outgoing_stream_bitrate) { - analyzer.CreateStreamBitrateGraph(webrtc::PacketDirection::kOutgoingPacket, + analyzer.CreateStreamBitrateGraph(webrtc::kOutgoingPacket, collection->AppendNewPlot()); } if (FLAG_plot_simulated_receiveside_bwe) { @@ -289,7 +299,10 @@ int main(int argc, char* argv[]) { analyzer.CreateFractionLossGraph(collection->AppendNewPlot()); } if (FLAG_plot_timestamps) { - analyzer.CreateTimestampGraph(collection->AppendNewPlot()); + analyzer.CreateTimestampGraph(webrtc::kIncomingPacket, + collection->AppendNewPlot()); + analyzer.CreateTimestampGraph(webrtc::kOutgoingPacket, + collection->AppendNewPlot()); } if (FLAG_plot_pacer_delay) { analyzer.CreatePacerDelayGraph(collection->AppendNewPlot()); @@ -333,7 +346,7 @@ int main(int argc, char* argv[]) { collection->Draw(); - if (FLAG_print_triage_notifications) { + if (FLAG_print_triage_alerts) { analyzer.CreateTriageNotifications(); analyzer.PrintNotifications(stderr); } diff --git a/rtc_tools/event_log_visualizer/plot_base.cc b/rtc_tools/event_log_visualizer/plot_base.cc index 7ff4ef9c2c..9a21393792 100644 --- a/rtc_tools/event_log_visualizer/plot_base.cc +++ b/rtc_tools/event_log_visualizer/plot_base.cc @@ -15,7 +15,6 @@ #include "rtc_base/checks.h" namespace webrtc { -namespace plotting { void Plot::SetXAxis(float min_value, float max_value, @@ -85,5 +84,4 @@ void Plot::AppendTimeSeriesIfNotEmpty(TimeSeries&& time_series) { } } -} // namespace plotting } // namespace webrtc diff --git a/rtc_tools/event_log_visualizer/plot_base.h b/rtc_tools/event_log_visualizer/plot_base.h index 700ffbf02d..e73f004937 100644 --- a/rtc_tools/event_log_visualizer/plot_base.h +++ b/rtc_tools/event_log_visualizer/plot_base.h @@ -16,7 +16,6 @@ #include namespace webrtc { -namespace plotting { enum class LineStyle { kNone, // No line connecting the points. Used to create scatter plots. @@ -173,7 +172,6 @@ class PlotCollection { std::vector > plots_; }; -} // namespace plotting } // namespace webrtc #endif // RTC_TOOLS_EVENT_LOG_VISUALIZER_PLOT_BASE_H_ diff --git a/rtc_tools/event_log_visualizer/plot_protobuf.cc b/rtc_tools/event_log_visualizer/plot_protobuf.cc index e5e0a8b094..e986a74fdc 100644 --- a/rtc_tools/event_log_visualizer/plot_protobuf.cc +++ b/rtc_tools/event_log_visualizer/plot_protobuf.cc @@ -13,7 +13,6 @@ #include namespace webrtc { -namespace plotting { ProtobufPlot::ProtobufPlot() {} @@ -83,5 +82,4 @@ Plot* ProtobufPlotCollection::AppendNewPlot() { return plot; } -} // namespace plotting } // namespace webrtc diff --git a/rtc_tools/event_log_visualizer/plot_protobuf.h b/rtc_tools/event_log_visualizer/plot_protobuf.h index 5c5cce1242..f59d303f1e 100644 --- a/rtc_tools/event_log_visualizer/plot_protobuf.h +++ b/rtc_tools/event_log_visualizer/plot_protobuf.h @@ -17,7 +17,6 @@ RTC_POP_IGNORING_WUNDEF() #include "rtc_tools/event_log_visualizer/plot_base.h" namespace webrtc { -namespace plotting { class ProtobufPlot final : public Plot { public: @@ -36,7 +35,6 @@ class ProtobufPlotCollection final : public PlotCollection { void ExportProtobuf(webrtc::analytics::ChartCollection* collection); }; -} // namespace plotting } // namespace webrtc #endif // RTC_TOOLS_EVENT_LOG_VISUALIZER_PLOT_PROTOBUF_H_ diff --git a/rtc_tools/event_log_visualizer/plot_python.cc b/rtc_tools/event_log_visualizer/plot_python.cc index 8f406e2898..37c4d84b0a 100644 --- a/rtc_tools/event_log_visualizer/plot_python.cc +++ b/rtc_tools/event_log_visualizer/plot_python.cc @@ -17,7 +17,6 @@ #include "rtc_base/checks.h" namespace webrtc { -namespace plotting { PythonPlot::PythonPlot() {} @@ -180,5 +179,4 @@ Plot* PythonPlotCollection::AppendNewPlot() { return plot; } -} // namespace plotting } // namespace webrtc diff --git a/rtc_tools/event_log_visualizer/plot_python.h b/rtc_tools/event_log_visualizer/plot_python.h index 2a5a66c31c..61d17a0de3 100644 --- a/rtc_tools/event_log_visualizer/plot_python.h +++ b/rtc_tools/event_log_visualizer/plot_python.h @@ -13,7 +13,6 @@ #include "rtc_tools/event_log_visualizer/plot_base.h" namespace webrtc { -namespace plotting { class PythonPlot final : public Plot { public: @@ -30,7 +29,6 @@ class PythonPlotCollection final : public PlotCollection { Plot* AppendNewPlot() override; }; -} // namespace plotting } // namespace webrtc #endif // RTC_TOOLS_EVENT_LOG_VISUALIZER_PLOT_PYTHON_H_ diff --git a/rtc_tools/event_log_visualizer/triage_notifications.h b/rtc_tools/event_log_visualizer/triage_notifications.h index 641e2ae6b0..49e0620934 100644 --- a/rtc_tools/event_log_visualizer/triage_notifications.h +++ b/rtc_tools/event_log_visualizer/triage_notifications.h @@ -14,130 +14,136 @@ #include namespace webrtc { -namespace plotting { -class TriageNotification { - public: - TriageNotification() : time_seconds_() {} - explicit TriageNotification(float time_seconds) - : time_seconds_(time_seconds) {} - virtual ~TriageNotification() = default; - virtual std::string ToString() = 0; - rtc::Optional Time() { return time_seconds_; } - - private: - rtc::Optional time_seconds_; -}; - -class IncomingRtpReceiveTimeGap : public TriageNotification { +class IncomingRtpReceiveTimeGap { public: IncomingRtpReceiveTimeGap(float time_seconds, int64_t duration) - : TriageNotification(time_seconds), duration_(duration) {} - std::string ToString() { + : time_seconds_(time_seconds), duration_(duration) {} + float Time() const { return time_seconds_; } + std::string ToString() const { return std::string("No RTP packets received for ") + std::to_string(duration_) + std::string(" ms"); } private: + float time_seconds_; int64_t duration_; }; -class IncomingRtcpReceiveTimeGap : public TriageNotification { +class IncomingRtcpReceiveTimeGap { public: IncomingRtcpReceiveTimeGap(float time_seconds, int64_t duration) - : TriageNotification(time_seconds), duration_(duration) {} - std::string ToString() { + : time_seconds_(time_seconds), duration_(duration) {} + float Time() const { return time_seconds_; } + std::string ToString() const { return std::string("No RTCP packets received for ") + std::to_string(duration_) + std::string(" ms"); } private: + float time_seconds_; int64_t duration_; }; -class OutgoingRtpSendTimeGap : public TriageNotification { +class OutgoingRtpSendTimeGap { public: OutgoingRtpSendTimeGap(float time_seconds, int64_t duration) - : TriageNotification(time_seconds), duration_(duration) {} - std::string ToString() { + : time_seconds_(time_seconds), duration_(duration) {} + float Time() const { return time_seconds_; } + std::string ToString() const { return std::string("No RTP packets sent for ") + std::to_string(duration_) + std::string(" ms"); } private: + float time_seconds_; int64_t duration_; }; -class OutgoingRtcpSendTimeGap : public TriageNotification { +class OutgoingRtcpSendTimeGap { public: OutgoingRtcpSendTimeGap(float time_seconds, int64_t duration) - : TriageNotification(time_seconds), duration_(duration) {} - std::string ToString() { + : time_seconds_(time_seconds), duration_(duration) {} + float Time() const { return time_seconds_; } + std::string ToString() const { return std::string("No RTCP packets sent for ") + std::to_string(duration_) + std::string(" ms"); } private: + float time_seconds_; int64_t duration_; }; -class IncomingSeqNoJump : public TriageNotification { +class IncomingSeqNumJump { public: - IncomingSeqNoJump(float time_seconds, uint32_t ssrc) - : TriageNotification(time_seconds), ssrc_(ssrc) {} - std::string ToString() { + IncomingSeqNumJump(float time_seconds, uint32_t ssrc) + : time_seconds_(time_seconds), ssrc_(ssrc) {} + float Time() const { return time_seconds_; } + std::string ToString() const { return std::string("Sequence number jumps on incoming SSRC ") + std::to_string(ssrc_); } private: + float time_seconds_; + uint32_t ssrc_; }; -class IncomingCaptureTimeJump : public TriageNotification { +class IncomingCaptureTimeJump { public: IncomingCaptureTimeJump(float time_seconds, uint32_t ssrc) - : TriageNotification(time_seconds), ssrc_(ssrc) {} - std::string ToString() { + : time_seconds_(time_seconds), ssrc_(ssrc) {} + float Time() const { return time_seconds_; } + std::string ToString() const { return std::string("Capture timestamp jumps on incoming SSRC ") + std::to_string(ssrc_); } private: + float time_seconds_; + uint32_t ssrc_; }; -class OutgoingSeqNoJump : public TriageNotification { +class OutgoingSeqNoJump { public: OutgoingSeqNoJump(float time_seconds, uint32_t ssrc) - : TriageNotification(time_seconds), ssrc_(ssrc) {} - std::string ToString() { + : time_seconds_(time_seconds), ssrc_(ssrc) {} + float Time() const { return time_seconds_; } + std::string ToString() const { return std::string("Sequence number jumps on outgoing SSRC ") + std::to_string(ssrc_); } private: + float time_seconds_; + uint32_t ssrc_; }; -class OutgoingCaptureTimeJump : public TriageNotification { +class OutgoingCaptureTimeJump { public: OutgoingCaptureTimeJump(float time_seconds, uint32_t ssrc) - : TriageNotification(time_seconds), ssrc_(ssrc) {} - std::string ToString() { + : time_seconds_(time_seconds), ssrc_(ssrc) {} + float Time() const { return time_seconds_; } + std::string ToString() const { return std::string("Capture timestamp jumps on outgoing SSRC ") + std::to_string(ssrc_); } private: + float time_seconds_; + uint32_t ssrc_; }; -class OutgoingHighLoss : public TriageNotification { +class OutgoingHighLoss { public: explicit OutgoingHighLoss(double avg_loss_fraction) : avg_loss_fraction_(avg_loss_fraction) {} - std::string ToString() { + std::string ToString() const { return std::string("High average loss (") + std::to_string(avg_loss_fraction_ * 100) + std::string("%) across the call."); @@ -147,7 +153,6 @@ class OutgoingHighLoss : public TriageNotification { double avg_loss_fraction_; }; -} // namespace plotting } // namespace webrtc #endif // RTC_TOOLS_EVENT_LOG_VISUALIZER_TRIAGE_NOTIFICATIONS_H_