The new API stores events gathered by event type. For example, it is possible to ask fo a list of all incoming RTCP messages or all audio playout events. The new API is experimental and may change over next few weeks. Once it has stabilized and all unit tests and existing tools have been ported to the new API, the old one will be removed. This CL also updates the event_log_visualizer tool to use the new parser API. This is not a funcional change except for: - Incoming and outgoing audio level are now drawn in two separate plots. - Incoming and outgoing timstamps are now drawn in two separate plots. - RTCP count is no longer split into Video and Audio. It also counts all RTCP packets rather than only specific message types. - Slight timing difference in sendside BWE simulation due to only iterating over transport feedbacks and not over all RTCP packets. This timing changes are not visible in the plots. Media type for RTCP messages might not be identified correctly by rtc_event_log2text anymore. On the other hand, assigning a specific media type to an RTCP packet was a bit hacky to begin with. Bug: webrtc:8111 Change-Id: I8e7168302beb69b2e163a097a2a142b86dd4a26b Reviewed-on: https://webrtc-review.googlesource.com/60865 Reviewed-by: Minyue Li <minyue@webrtc.org> Reviewed-by: Sebastian Jansson <srte@webrtc.org> Commit-Queue: Björn Terelius <terelius@webrtc.org> Cr-Commit-Position: refs/heads/master@{#23015}
1249 lines
48 KiB
C++
1249 lines
48 KiB
C++
/*
|
|
* 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 <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;
|
|
}
|
|
|
|
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<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
|
|
|
|
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<int64_t>::max();
|
|
last_timestamp_ = std::numeric_limits<int64_t>::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<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);
|
|
}
|
|
|
|
// 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<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 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<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 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<unsigned>(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<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> ParsedRtcEventLog::GetVideoSendConfig(
|
|
size_t index) const {
|
|
RTC_CHECK_LT(index, GetNumberOfEvents());
|
|
return GetVideoSendConfig(events_[index]);
|
|
}
|
|
|
|
std::vector<rtclog::StreamConfig> ParsedRtcEventLog::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 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
|