webrtc/logging/rtc_event_log/rtc_event_log_parser.cc
Björn Terelius 00c12f6779 Add logging of decoded video frames.
This CL adds the possibility to log metainformation about
decoded frames in RTC event log, including encoding parsing
and tests. It will be wired up in a followup CL.


Bug: webrtc:8802
Change-Id: Ied598b266513d0f63fce0484d741af1782607e74
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/181061
Reviewed-by: Christoffer Rodbro <crodbro@webrtc.org>
Reviewed-by: Artem Titov <titovartem@webrtc.org>
Commit-Queue: Björn Terelius <terelius@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31873}
2020-08-06 17:33:24 +00:00

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