|  | /* | 
|  | *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include "logging/rtc_event_log/rtc_event_log_parser_new.h" | 
|  |  | 
|  | #include <stdint.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <fstream> | 
|  | #include <istream>  // no-presubmit-check TODO(webrtc:8982) | 
|  | #include <limits> | 
|  | #include <map> | 
|  | #include <utility> | 
|  |  | 
|  | #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; | 
|  | } | 
|  |  | 
|  | ParsedRtcEventLogNew::EventType GetRuntimeEventType( | 
|  | rtclog::Event::EventType event_type) { | 
|  | switch (event_type) { | 
|  | case rtclog::Event::UNKNOWN_EVENT: | 
|  | return ParsedRtcEventLogNew::EventType::UNKNOWN_EVENT; | 
|  | case rtclog::Event::LOG_START: | 
|  | return ParsedRtcEventLogNew::EventType::LOG_START; | 
|  | case rtclog::Event::LOG_END: | 
|  | return ParsedRtcEventLogNew::EventType::LOG_END; | 
|  | case rtclog::Event::RTP_EVENT: | 
|  | return ParsedRtcEventLogNew::EventType::RTP_EVENT; | 
|  | case rtclog::Event::RTCP_EVENT: | 
|  | return ParsedRtcEventLogNew::EventType::RTCP_EVENT; | 
|  | case rtclog::Event::AUDIO_PLAYOUT_EVENT: | 
|  | return ParsedRtcEventLogNew::EventType::AUDIO_PLAYOUT_EVENT; | 
|  | case rtclog::Event::LOSS_BASED_BWE_UPDATE: | 
|  | return ParsedRtcEventLogNew::EventType::LOSS_BASED_BWE_UPDATE; | 
|  | case rtclog::Event::DELAY_BASED_BWE_UPDATE: | 
|  | return ParsedRtcEventLogNew::EventType::DELAY_BASED_BWE_UPDATE; | 
|  | case rtclog::Event::VIDEO_RECEIVER_CONFIG_EVENT: | 
|  | return ParsedRtcEventLogNew::EventType::VIDEO_RECEIVER_CONFIG_EVENT; | 
|  | case rtclog::Event::VIDEO_SENDER_CONFIG_EVENT: | 
|  | return ParsedRtcEventLogNew::EventType::VIDEO_SENDER_CONFIG_EVENT; | 
|  | case rtclog::Event::AUDIO_RECEIVER_CONFIG_EVENT: | 
|  | return ParsedRtcEventLogNew::EventType::AUDIO_RECEIVER_CONFIG_EVENT; | 
|  | case rtclog::Event::AUDIO_SENDER_CONFIG_EVENT: | 
|  | return ParsedRtcEventLogNew::EventType::AUDIO_SENDER_CONFIG_EVENT; | 
|  | case rtclog::Event::AUDIO_NETWORK_ADAPTATION_EVENT: | 
|  | return ParsedRtcEventLogNew::EventType::AUDIO_NETWORK_ADAPTATION_EVENT; | 
|  | case rtclog::Event::BWE_PROBE_CLUSTER_CREATED_EVENT: | 
|  | return ParsedRtcEventLogNew::EventType::BWE_PROBE_CLUSTER_CREATED_EVENT; | 
|  | case rtclog::Event::BWE_PROBE_RESULT_EVENT: | 
|  | // Probe successes and failures are currently stored in the same proto | 
|  | // message, we are moving towards separate messages. Probe results | 
|  | // therefore need special treatment in the parser. | 
|  | return ParsedRtcEventLogNew::EventType::UNKNOWN_EVENT; | 
|  | case rtclog::Event::ALR_STATE_EVENT: | 
|  | return ParsedRtcEventLogNew::EventType::ALR_STATE_EVENT; | 
|  | case rtclog::Event::ICE_CANDIDATE_PAIR_CONFIG: | 
|  | return ParsedRtcEventLogNew::EventType::ICE_CANDIDATE_PAIR_CONFIG; | 
|  | case rtclog::Event::ICE_CANDIDATE_PAIR_EVENT: | 
|  | return ParsedRtcEventLogNew::EventType::ICE_CANDIDATE_PAIR_EVENT; | 
|  | } | 
|  | return ParsedRtcEventLogNew::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<AudioLevel>(webrtc::RtpExtension::kAudioLevelDefaultId); | 
|  | default_map.Register<TransmissionOffset>( | 
|  | webrtc::RtpExtension::kTimestampOffsetDefaultId); | 
|  | default_map.Register<AbsoluteSendTime>( | 
|  | webrtc::RtpExtension::kAbsSendTimeDefaultId); | 
|  | default_map.Register<VideoOrientation>( | 
|  | webrtc::RtpExtension::kVideoRotationDefaultId); | 
|  | default_map.Register<VideoContentTypeExtension>( | 
|  | webrtc::RtpExtension::kVideoContentTypeDefaultId); | 
|  | default_map.Register<VideoTimingExtension>( | 
|  | webrtc::RtpExtension::kVideoTimingDefaultId); | 
|  | default_map.Register<TransportSequenceNumber>( | 
|  | webrtc::RtpExtension::kTransportSequenceNumberDefaultId); | 
|  | default_map.Register<PlayoutDelayLimits>( | 
|  | webrtc::RtpExtension::kPlayoutDelayDefaultId); | 
|  | return default_map; | 
|  | } | 
|  |  | 
|  | std::pair<uint64_t, bool> 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<uint64_t>(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<RtpExtension>* header_extensions, | 
|  | const RepeatedPtrField<rtclog::RtpHeaderExtension>& | 
|  | 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 | 
|  |  | 
|  | ParsedRtcEventLogNew::ParsedRtcEventLogNew( | 
|  | UnconfiguredHeaderExtensions parse_unconfigured_header_extensions) | 
|  | : parse_unconfigured_header_extensions_( | 
|  | parse_unconfigured_header_extensions) { | 
|  | Clear(); | 
|  | } | 
|  |  | 
|  | void ParsedRtcEventLogNew::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_failure_events_.clear(); | 
|  | bwe_probe_success_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<int64_t>::max(); | 
|  | last_timestamp_ = std::numeric_limits<int64_t>::min(); | 
|  |  | 
|  | incoming_rtp_extensions_maps_.clear(); | 
|  | outgoing_rtp_extensions_maps_.clear(); | 
|  | } | 
|  |  | 
|  | bool ParsedRtcEventLogNew::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 ParsedRtcEventLogNew::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 ParsedRtcEventLogNew::ParseStream( | 
|  | std::istream& stream) {  // no-presubmit-check TODO(webrtc:8982) | 
|  | Clear(); | 
|  | bool success = ParseStreamInternal(stream); | 
|  |  | 
|  | // ParseStreamInternal stores the RTP packets in a map indexed by SSRC. | 
|  | // Since we dont need rapid lookup based on SSRC after parsing, we move the | 
|  | // packets_streams from map to vector. | 
|  | incoming_rtp_packets_by_ssrc_.reserve(incoming_rtp_packets_map_.size()); | 
|  | for (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); | 
|  | } | 
|  | incoming_rtp_packets_map_.clear(); | 
|  | 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); | 
|  | } | 
|  | outgoing_rtp_packets_map_.clear(); | 
|  |  | 
|  | // Build PacketViews for easier iteration over RTP packets | 
|  | for (const auto& stream : incoming_rtp_packets_by_ssrc_) { | 
|  | incoming_rtp_packet_views_by_ssrc_.emplace_back( | 
|  | LoggedRtpStreamView(stream.ssrc, stream.incoming_packets.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 success; | 
|  | } | 
|  |  | 
|  | bool ParsedRtcEventLogNew::ParseStreamInternal( | 
|  | std::istream& stream) {  // no-presubmit-check TODO(webrtc:8982) | 
|  | const size_t kMaxEventSize = (1u << 16) - 1; | 
|  | std::vector<char> 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<int>(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); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void ParsedRtcEventLogNew::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 (GetEventType(event)) { | 
|  | case ParsedRtcEventLogNew::EventType::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 ParsedRtcEventLogNew::EventType::VIDEO_SENDER_CONFIG_EVENT: { | 
|  | std::vector<rtclog::StreamConfig> 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 ParsedRtcEventLogNew::EventType::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 ParsedRtcEventLogNew::EventType::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 ParsedRtcEventLogNew::EventType::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 ParsedRtcEventLogNew::EventType::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 ParsedRtcEventLogNew::EventType::LOG_START: { | 
|  | start_log_events_.push_back(LoggedStartEvent(GetTimestamp(event))); | 
|  | break; | 
|  | } | 
|  | case ParsedRtcEventLogNew::EventType::LOG_END: { | 
|  | stop_log_events_.push_back(LoggedStopEvent(GetTimestamp(event))); | 
|  | break; | 
|  | } | 
|  | case ParsedRtcEventLogNew::EventType::AUDIO_PLAYOUT_EVENT: { | 
|  | LoggedAudioPlayoutEvent playout_event = GetAudioPlayout(event); | 
|  | audio_playout_events_[playout_event.ssrc].push_back(playout_event); | 
|  | break; | 
|  | } | 
|  | case ParsedRtcEventLogNew::EventType::LOSS_BASED_BWE_UPDATE: { | 
|  | bwe_loss_updates_.push_back(GetLossBasedBweUpdate(event)); | 
|  | break; | 
|  | } | 
|  | case ParsedRtcEventLogNew::EventType::DELAY_BASED_BWE_UPDATE: { | 
|  | bwe_delay_updates_.push_back(GetDelayBasedBweUpdate(event)); | 
|  | break; | 
|  | } | 
|  | case ParsedRtcEventLogNew::EventType::AUDIO_NETWORK_ADAPTATION_EVENT: { | 
|  | LoggedAudioNetworkAdaptationEvent ana_event = | 
|  | GetAudioNetworkAdaptation(event); | 
|  | audio_network_adaptation_events_.push_back(ana_event); | 
|  | break; | 
|  | } | 
|  | case ParsedRtcEventLogNew::EventType::BWE_PROBE_CLUSTER_CREATED_EVENT: { | 
|  | bwe_probe_cluster_created_events_.push_back( | 
|  | GetBweProbeClusterCreated(event)); | 
|  | break; | 
|  | } | 
|  | case ParsedRtcEventLogNew::EventType::BWE_PROBE_FAILURE_EVENT: { | 
|  | bwe_probe_failure_events_.push_back(GetBweProbeFailure(event)); | 
|  | break; | 
|  | } | 
|  | case ParsedRtcEventLogNew::EventType::BWE_PROBE_SUCCESS_EVENT: { | 
|  | bwe_probe_success_events_.push_back(GetBweProbeSuccess(event)); | 
|  | break; | 
|  | } | 
|  | case ParsedRtcEventLogNew::EventType::ALR_STATE_EVENT: { | 
|  | alr_state_events_.push_back(GetAlrState(event)); | 
|  | break; | 
|  | } | 
|  | case ParsedRtcEventLogNew::EventType::ICE_CANDIDATE_PAIR_CONFIG: { | 
|  | ice_candidate_pair_configs_.push_back(GetIceCandidatePairConfig(event)); | 
|  | break; | 
|  | } | 
|  | case ParsedRtcEventLogNew::EventType::ICE_CANDIDATE_PAIR_EVENT: { | 
|  | ice_candidate_pair_events_.push_back(GetIceCandidatePairEvent(event)); | 
|  | break; | 
|  | } | 
|  | case ParsedRtcEventLogNew::EventType::UNKNOWN_EVENT: { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t ParsedRtcEventLogNew::GetNumberOfEvents() const { | 
|  | return events_.size(); | 
|  | } | 
|  |  | 
|  | int64_t ParsedRtcEventLogNew::GetTimestamp(size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | const rtclog::Event& event = events_[index]; | 
|  | return GetTimestamp(event); | 
|  | } | 
|  |  | 
|  | int64_t ParsedRtcEventLogNew::GetTimestamp(const rtclog::Event& event) const { | 
|  | RTC_CHECK(event.has_timestamp_us()); | 
|  | return event.timestamp_us(); | 
|  | } | 
|  |  | 
|  | ParsedRtcEventLogNew::EventType ParsedRtcEventLogNew::GetEventType( | 
|  | size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | const rtclog::Event& event = events_[index]; | 
|  | return GetEventType(event); | 
|  | } | 
|  |  | 
|  | ParsedRtcEventLogNew::EventType ParsedRtcEventLogNew::GetEventType( | 
|  | const rtclog::Event& event) const { | 
|  | RTC_CHECK(event.has_type()); | 
|  | if (event.type() == rtclog::Event::BWE_PROBE_RESULT_EVENT) { | 
|  | RTC_CHECK(event.has_probe_result()); | 
|  | RTC_CHECK(event.probe_result().has_result()); | 
|  | if (event.probe_result().result() == rtclog::BweProbeResult::SUCCESS) | 
|  | return ParsedRtcEventLogNew::EventType::BWE_PROBE_SUCCESS_EVENT; | 
|  | return ParsedRtcEventLogNew::EventType::BWE_PROBE_FAILURE_EVENT; | 
|  | } | 
|  | return GetRuntimeEventType(event.type()); | 
|  | } | 
|  |  | 
|  | // The header must have space for at least IP_PACKET_SIZE bytes. | 
|  | const webrtc::RtpHeaderExtensionMap* ParsedRtcEventLogNew::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* ParsedRtcEventLogNew::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<size_t>(IP_PACKET_SIZE)); | 
|  | memcpy(header, rtp_packet.header().data(), rtp_packet.header().size()); | 
|  | uint32_t ssrc = ByteReader<uint32_t>::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 ParsedRtcEventLogNew::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 ParsedRtcEventLogNew::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<unsigned>(IP_PACKET_SIZE)); | 
|  | memcpy(packet, rtcp_packet.packet_data().data(), | 
|  | rtcp_packet.packet_data().size()); | 
|  | } | 
|  | } | 
|  |  | 
|  | rtclog::StreamConfig ParsedRtcEventLogNew::GetVideoReceiveConfig( | 
|  | size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | return GetVideoReceiveConfig(events_[index]); | 
|  | } | 
|  |  | 
|  | rtclog::StreamConfig ParsedRtcEventLogNew::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<uint32_t, const rtclog::RtxConfig> rtx_map; | 
|  | for (int i = 0; i < receiver_config.rtx_map_size(); i++) { | 
|  | const rtclog::RtxMap& map = receiver_config.rtx_map(i); | 
|  | RTC_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<rtclog::StreamConfig> ParsedRtcEventLogNew::GetVideoSendConfig( | 
|  | size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | return GetVideoSendConfig(events_[index]); | 
|  | } | 
|  |  | 
|  | std::vector<rtclog::StreamConfig> ParsedRtcEventLogNew::GetVideoSendConfig( | 
|  | const rtclog::Event& event) const { | 
|  | std::vector<rtclog::StreamConfig> 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 ParsedRtcEventLogNew::GetAudioReceiveConfig( | 
|  | size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | return GetAudioReceiveConfig(events_[index]); | 
|  | } | 
|  |  | 
|  | rtclog::StreamConfig ParsedRtcEventLogNew::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 ParsedRtcEventLogNew::GetAudioSendConfig( | 
|  | size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | return GetAudioSendConfig(events_[index]); | 
|  | } | 
|  |  | 
|  | rtclog::StreamConfig ParsedRtcEventLogNew::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 ParsedRtcEventLogNew::GetAudioPlayout( | 
|  | size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | const rtclog::Event& event = events_[index]; | 
|  | return GetAudioPlayout(event); | 
|  | } | 
|  |  | 
|  | LoggedAudioPlayoutEvent ParsedRtcEventLogNew::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 ParsedRtcEventLogNew::GetLossBasedBweUpdate( | 
|  | size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | const rtclog::Event& event = events_[index]; | 
|  | return GetLossBasedBweUpdate(event); | 
|  | } | 
|  |  | 
|  | LoggedBweLossBasedUpdate ParsedRtcEventLogNew::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 ParsedRtcEventLogNew::GetDelayBasedBweUpdate( | 
|  | size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | const rtclog::Event& event = events_[index]; | 
|  | return GetDelayBasedBweUpdate(event); | 
|  | } | 
|  |  | 
|  | LoggedBweDelayBasedUpdate ParsedRtcEventLogNew::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 | 
|  | ParsedRtcEventLogNew::GetAudioNetworkAdaptation(size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | const rtclog::Event& event = events_[index]; | 
|  | return GetAudioNetworkAdaptation(event); | 
|  | } | 
|  |  | 
|  | LoggedAudioNetworkAdaptationEvent | 
|  | ParsedRtcEventLogNew::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 | 
|  | ParsedRtcEventLogNew::GetBweProbeClusterCreated(size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | const rtclog::Event& event = events_[index]; | 
|  | return GetBweProbeClusterCreated(event); | 
|  | } | 
|  |  | 
|  | LoggedBweProbeClusterCreatedEvent | 
|  | ParsedRtcEventLogNew::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; | 
|  | } | 
|  |  | 
|  | LoggedBweProbeFailureEvent ParsedRtcEventLogNew::GetBweProbeFailure( | 
|  | size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | const rtclog::Event& event = events_[index]; | 
|  | return GetBweProbeFailure(event); | 
|  | } | 
|  |  | 
|  | LoggedBweProbeFailureEvent ParsedRtcEventLogNew::GetBweProbeFailure( | 
|  | 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(); | 
|  | RTC_CHECK(pr_event.has_result()); | 
|  | RTC_CHECK_NE(pr_event.result(), rtclog::BweProbeResult::SUCCESS); | 
|  |  | 
|  | LoggedBweProbeFailureEvent 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::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(); | 
|  | } | 
|  | RTC_CHECK(!pr_event.has_bitrate_bps()); | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | LoggedBweProbeSuccessEvent ParsedRtcEventLogNew::GetBweProbeSuccess( | 
|  | size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | const rtclog::Event& event = events_[index]; | 
|  | return GetBweProbeSuccess(event); | 
|  | } | 
|  |  | 
|  | LoggedBweProbeSuccessEvent ParsedRtcEventLogNew::GetBweProbeSuccess( | 
|  | 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(); | 
|  | RTC_CHECK(pr_event.has_result()); | 
|  | RTC_CHECK_EQ(pr_event.result(), rtclog::BweProbeResult::SUCCESS); | 
|  |  | 
|  | LoggedBweProbeSuccessEvent res; | 
|  | res.timestamp_us = GetTimestamp(event); | 
|  | RTC_CHECK(pr_event.has_id()); | 
|  | res.id = pr_event.id(); | 
|  | RTC_CHECK(pr_event.has_bitrate_bps()); | 
|  | res.bitrate_bps = pr_event.bitrate_bps(); | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | LoggedAlrStateEvent ParsedRtcEventLogNew::GetAlrState(size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | const rtclog::Event& event = events_[index]; | 
|  | return GetAlrState(event); | 
|  | } | 
|  |  | 
|  | LoggedAlrStateEvent ParsedRtcEventLogNew::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 ParsedRtcEventLogNew::GetIceCandidatePairConfig( | 
|  | size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | const rtclog::Event& rtc_event = events_[index]; | 
|  | return GetIceCandidatePairConfig(rtc_event); | 
|  | } | 
|  |  | 
|  | LoggedIceCandidatePairConfig ParsedRtcEventLogNew::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 ParsedRtcEventLogNew::GetIceCandidatePairEvent( | 
|  | size_t index) const { | 
|  | RTC_CHECK_LT(index, GetNumberOfEvents()); | 
|  | const rtclog::Event& rtc_event = events_[index]; | 
|  | return GetIceCandidatePairEvent(rtc_event); | 
|  | } | 
|  |  | 
|  | LoggedIceCandidatePairEvent ParsedRtcEventLogNew::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. | 
|  | ParsedRtcEventLogNew::MediaType ParsedRtcEventLogNew::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 |