diff --git a/logging/BUILD.gn b/logging/BUILD.gn index 67c01fa50f..ce40528dd3 100644 --- a/logging/BUILD.gn +++ b/logging/BUILD.gn @@ -37,6 +37,7 @@ rtc_library("rtc_event_field") { sources = [ "rtc_event_log/events/fixed_length_encoding_parameters_v3.cc", "rtc_event_log/events/fixed_length_encoding_parameters_v3.h", + "rtc_event_log/events/rtc_event_definition.h", "rtc_event_log/events/rtc_event_field_encoding.cc", "rtc_event_log/events/rtc_event_field_encoding.h", "rtc_event_log/events/rtc_event_field_encoding_parser.cc", @@ -291,6 +292,7 @@ rtc_library("rtc_event_log_impl_encoder") { ":rtc_event_audio", ":rtc_event_begin_end", ":rtc_event_bwe", + ":rtc_event_field", ":rtc_event_frame_events", ":rtc_event_generic_packet_events", ":rtc_event_log2_proto", @@ -310,6 +312,8 @@ rtc_library("rtc_event_log_impl_encoder") { "rtc_event_log/encoder/rtc_event_log_encoder_legacy.h", "rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc", "rtc_event_log/encoder/rtc_event_log_encoder_new_format.h", + "rtc_event_log/encoder/rtc_event_log_encoder_v3.cc", + "rtc_event_log/encoder/rtc_event_log_encoder_v3.h", ] } } diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc index bd898a1275..be9352ab94 100644 --- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc @@ -16,6 +16,7 @@ #include "logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h" #include "logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h" #include "logging/rtc_event_log/events/rtc_event_alr_state.h" #include "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h" #include "logging/rtc_event_log/events/rtc_event_audio_playout.h" @@ -62,8 +63,7 @@ class RtcEventLogEncoderTest encoder_ = std::make_unique(); break; case RtcEventLog::EncodingType::ProtoFree: - // TODO(terelius): Enable test once the format has been wired up. - RTC_CHECK_NOTREACHED(); + encoder_ = std::make_unique(); break; } encoded_ = @@ -1290,8 +1290,7 @@ class RtcEventLogEncoderSimpleTest encoder_ = std::make_unique(); break; case RtcEventLog::EncodingType::ProtoFree: - // TODO(terelius): Enable test once the format has been wired up. - RTC_CHECK_NOTREACHED(); + encoder_ = std::make_unique(); break; } encoded_ = diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.cc new file mode 100644 index 0000000000..1fd5fa60bb --- /dev/null +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.cc @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2021 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/encoder/rtc_event_log_encoder_v3.h" + +#include +#include + +#include "absl/types/optional.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_common.h" +#include "logging/rtc_event_log/encoder/var_int.h" +#include "logging/rtc_event_log/events/rtc_event_alr_state.h" +#include "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h" +#include "logging/rtc_event_log/events/rtc_event_audio_playout.h" +#include "logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_begin_log.h" +#include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h" +#include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h" +#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" +#include "logging/rtc_event_log/events/rtc_event_end_log.h" +#include "logging/rtc_event_log/events/rtc_event_frame_decoded.h" +#include "logging/rtc_event_log/events/rtc_event_generic_ack_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_received.h" +#include "logging/rtc_event_log/events/rtc_event_generic_packet_sent.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" +#include "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h" +#include "logging/rtc_event_log/events/rtc_event_probe_result_failure.h" +#include "logging/rtc_event_log/events/rtc_event_probe_result_success.h" +#include "logging/rtc_event_log/events/rtc_event_remote_estimate.h" +#include "logging/rtc_event_log/events/rtc_event_route_change.h" +#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h" +#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h" +#include "logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h" +#include "logging/rtc_event_log/events/rtc_event_video_send_stream_config.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +std::string RtcEventLogEncoderV3::EncodeLogStart(int64_t timestamp_us, + int64_t utc_time_us) { + std::unique_ptr begin_log = + std::make_unique(Timestamp::Micros(timestamp_us), + Timestamp::Micros(utc_time_us)); + std::vector batch; + batch.push_back(begin_log.get()); + + std::string encoded_event = RtcEventBeginLog::Encode(batch); + + return encoded_event; +} + +std::string RtcEventLogEncoderV3::EncodeLogEnd(int64_t timestamp_us) { + std::unique_ptr end_log = + std::make_unique(Timestamp::Micros(timestamp_us)); + std::vector batch; + batch.push_back(end_log.get()); + + std::string encoded_event = RtcEventEndLog::Encode(batch); + + return encoded_event; +} + +RtcEventLogEncoderV3::RtcEventLogEncoderV3() { + encoders_[RtcEvent::Type::AlrStateEvent] = RtcEventAlrState::Encode; +} + +std::string RtcEventLogEncoderV3::EncodeBatch( + std::deque>::const_iterator begin, + std::deque>::const_iterator end) { + struct EventGroupKey { + // Events are grouped by event type. For compression efficiency, + // events can optionally have a secondary key, in most cases the + // SSRC. + RtcEvent::Type type; + uint32_t secondary_group_key; + + bool operator<(EventGroupKey other) const { + return type < other.type || + (type == other.type && + secondary_group_key < other.secondary_group_key); + } + }; + + std::map> event_groups; + + for (auto it = begin; it != end; ++it) { + event_groups[{(*it)->GetType(), (*it)->GetGroupKey()}].push_back(it->get()); + } + + std::string encoded_output; + for (auto& kv : event_groups) { + auto it = encoders_.find(kv.first.type); + RTC_DCHECK(it != encoders_.end()); + if (it != encoders_.end()) { + auto& encoder = it->second; + // TODO(terelius): Use some "string builder" or preallocate? + encoded_output += encoder(kv.second); + } + } + + return encoded_output; +} + +} // namespace webrtc diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h b/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h new file mode 100644 index 0000000000..cb796ec562 --- /dev/null +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_V3_H_ +#define LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_V3_H_ + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "logging/rtc_event_log/encoder/rtc_event_log_encoder.h" +#include "logging/rtc_event_log/events/rtc_event_definition.h" + +namespace webrtc { + +class RtcEventLogEncoderV3 final : public RtcEventLogEncoder { + public: + RtcEventLogEncoderV3(); + ~RtcEventLogEncoderV3() override = default; + + std::string EncodeBatch( + std::deque>::const_iterator begin, + std::deque>::const_iterator end) override; + + std::string EncodeLogStart(int64_t timestamp_us, + int64_t utc_time_us) override; + std::string EncodeLogEnd(int64_t timestamp_us) override; + + private: + std::map)>> + encoders_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_ENCODER_RTC_EVENT_LOG_ENCODER_V3_H_ diff --git a/logging/rtc_event_log/events/rtc_event_alr_state.cc b/logging/rtc_event_log/events/rtc_event_alr_state.cc index 3c307b9ca0..25941eb16b 100644 --- a/logging/rtc_event_log/events/rtc_event_alr_state.cc +++ b/logging/rtc_event_log/events/rtc_event_alr_state.cc @@ -13,6 +13,9 @@ #include "absl/memory/memory.h" namespace webrtc { +constexpr RtcEvent::Type RtcEventAlrState::kType; +constexpr RtcEventDefinition + RtcEventAlrState::definition_; RtcEventAlrState::RtcEventAlrState(bool in_alr) : in_alr_(in_alr) {} @@ -25,4 +28,11 @@ std::unique_ptr RtcEventAlrState::Copy() const { return absl::WrapUnique(new RtcEventAlrState(*this)); } +RtcEventLogParseStatus RtcEventAlrState::Parse( + absl::string_view s, + bool batched, + std::vector& output) { + return RtcEventAlrState::definition_.ParseBatch(s, batched, output); +} + } // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_alr_state.h b/logging/rtc_event_log/events/rtc_event_alr_state.h index 74d66015ef..2bc74089e0 100644 --- a/logging/rtc_event_log/events/rtc_event_alr_state.h +++ b/logging/rtc_event_log/events/rtc_event_alr_state.h @@ -12,12 +12,30 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_ALR_STATE_H_ #include +#include +#include #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_definition.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" namespace webrtc { +struct LoggedAlrStateEvent { + LoggedAlrStateEvent() = default; + LoggedAlrStateEvent(Timestamp timestamp, bool in_alr) + : timestamp(timestamp), in_alr(in_alr) {} + + int64_t log_time_us() const { return timestamp.us(); } + int64_t log_time_ms() const { return timestamp.ms(); } + + Timestamp timestamp = Timestamp::MinusInfinity(); + bool in_alr; +}; + class RtcEventAlrState final : public RtcEvent { public: static constexpr Type kType = Type::AlrStateEvent; @@ -32,22 +50,26 @@ class RtcEventAlrState final : public RtcEvent { bool in_alr() const { return in_alr_; } + static std::string Encode(rtc::ArrayView batch) { + return RtcEventAlrState::definition_.EncodeBatch(batch); + } + + static RtcEventLogParseStatus Parse(absl::string_view s, + bool batched, + std::vector& output); + private: RtcEventAlrState(const RtcEventAlrState& other); const bool in_alr_; -}; -struct LoggedAlrStateEvent { - LoggedAlrStateEvent() = default; - LoggedAlrStateEvent(Timestamp timestamp, bool in_alr) - : timestamp(timestamp), in_alr(in_alr) {} - - int64_t log_time_us() const { return timestamp.us(); } - int64_t log_time_ms() const { return timestamp.ms(); } - - Timestamp timestamp = Timestamp::MinusInfinity(); - bool in_alr; + static constexpr RtcEventDefinition + definition_{{"AlrState", RtcEventAlrState::kType}, + {&RtcEventAlrState::in_alr_, + &LoggedAlrStateEvent::in_alr, + {"in_alr", /*id=*/1, FieldType::kFixed8, /*width=*/1}}}; }; } // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_definition.h b/logging/rtc_event_log/events/rtc_event_definition.h new file mode 100644 index 0000000000..43dd7eb18b --- /dev/null +++ b/logging/rtc_event_log/events/rtc_event_definition.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DEFINITION_H_ +#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DEFINITION_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" +#include "logging/rtc_event_log/events/rtc_event_field_extraction.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +template +struct RtcEventFieldDefinition { + const T EventType::*event_member; + T LoggedType::*logged_member; + FieldParameters params; +}; + +// Base case +template +class RtcEventDefinitionImpl { + public: + void EncodeImpl(EventEncoder&, rtc::ArrayView) const {} + RtcEventLogParseStatus ParseImpl(EventParser&, + rtc::ArrayView) const { + return RtcEventLogParseStatus::Success(); + } +}; + +// Recursive case +template +class RtcEventDefinitionImpl { + public: + constexpr RtcEventDefinitionImpl( + RtcEventFieldDefinition field, + RtcEventFieldDefinition... rest) + : field_(field), rest_(rest...) {} + + void EncodeImpl(EventEncoder& encoder, + rtc::ArrayView batch) const { + auto values = ExtractRtcEventMember(batch, field_.event_member); + encoder.EncodeField(field_.params, values); + rest_.EncodeImpl(encoder, batch); + } + + RtcEventLogParseStatus ParseImpl( + EventParser& parser, + rtc::ArrayView output_batch) const { + RtcEventLogParseStatusOr> result = + parser.ParseNumericField(field_.params); + if (!result.ok()) + return result.status(); + PopulateRtcEventMember(result.value(), field_.logged_member, output_batch); + + return rest_.ParseImpl(parser, output_batch); + } + + private: + RtcEventFieldDefinition field_; + RtcEventDefinitionImpl rest_; +}; + +// The RtcEventDefinition sets up a mapping between the fields +// in an RtcEvent and the corresponding fields in the parsed struct. +// For example, an RtcFoo class containing two fields; `uint32_t bar` +// and `bool baz` (a log timestamp is always implicitly added) +// might have a definition +// RtcEventDefinition( +// {"foo", RtcFoo::Type}, +// {&RtcFoo::bar_, &LoggedFoo::bar, {"bar", 1, FieldType::kVarInt, 32}}, +// {&RtcFoo::baz_, &LoggedFoo::baz, {"baz", 2, FieldType::kFixed8, 1}}, +// ); +// In addition to defining string names to aid debugging, +// this specifies that +// * RtcFoo::Type uniquely identifies an RtcFoo in the encoded stream +// * The `bar` field has ID 1, is encoded as a VarInt +// (when not delta compressed), and wraps around after 32 bits. +// * The `baz` field has ID 2, is encoded as an 8-bit field +// (when not delta compressed), and wraps around after 1 bit. +// Note that the numerical field and event IDs can't be changed since +// that would break compatibility with old logs. +// In most cases (including all cases where wrap around isn't +// expected), the wrap around should be equal to the bitwidth of +// the field. +template +class RtcEventDefinition { + public: + constexpr RtcEventDefinition( + EventParameters params, + RtcEventFieldDefinition... fields) + : params_(params), fields_(fields...) {} + + std::string EncodeBatch(rtc::ArrayView batch) const { + EventEncoder encoder(params_, batch); + fields_.EncodeImpl(encoder, batch); + return encoder.AsString(); + } + + RtcEventLogParseStatus ParseBatch(absl::string_view s, + bool batched, + std::vector& output) const { + EventParser parser; + auto status = parser.Initialize(s, batched); + if (!status.ok()) + return status; + + rtc::ArrayView output_batch = + ExtendLoggedBatch(output, parser.NumEventsInBatch()); + + constexpr FieldParameters timestamp_params{"timestamp_ms", + FieldParameters::kTimestampField, + FieldType::kVarInt, 64}; + RtcEventLogParseStatusOr> result = + parser.ParseNumericField(timestamp_params); + if (!result.ok()) + return result.status(); + PopulateRtcEventTimestamp(result.value(), &LoggedType::timestamp, + output_batch); + + return fields_.ParseImpl(parser, output_batch); + + return RtcEventLogParseStatus::Success(); + } + + private: + EventParameters params_; + RtcEventDefinitionImpl fields_; +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DEFINITION_H_ diff --git a/logging/rtc_event_log/rtc_event_log_parser.cc b/logging/rtc_event_log/rtc_event_log_parser.cc index 3b3eb18f72..7e114a257c 100644 --- a/logging/rtc_event_log/rtc_event_log_parser.cc +++ b/logging/rtc_event_log/rtc_event_log_parser.cc @@ -53,6 +53,12 @@ return ParsedRtcEventLog::ParseStatus::Error(#X, __FILE__, __LINE__); \ } while (0) +#define RTC_PARSE_CHECK_OR_RETURN_MESSAGE(X, M) \ + do { \ + if (!(X)) \ + return ParsedRtcEventLog::ParseStatus::Error((M), __FILE__, __LINE__); \ + } while (0) + #define RTC_PARSE_CHECK_OR_RETURN_OP(OP, X, Y) \ do { \ if (!((X)OP(Y))) \ @@ -1267,18 +1273,34 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStream( ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStreamInternal( absl::string_view s) { constexpr uint64_t kMaxEventSize = 10000000; // Sanity check. + // Protobuf defines the message tag as + // (field_number << 3) | wire_type. In the legacy encoding, the field number + // is supposed to be 1 and the wire type for a length-delimited field is 2. + // In the new encoding we still expect the wire type to be 2, but the field + // number will be greater than 1. + constexpr uint64_t kExpectedV1Tag = (1 << 3) | 2; + bool success = false; + + // "Peek" at the first varint. + absl::string_view event_start = s; + uint64_t tag = 0; + std::tie(success, std::ignore) = DecodeVarInt(s, &tag); + if (!success) { + RTC_LOG(LS_WARNING) << "Failed to read varint from beginning of event log."; + RTC_PARSE_WARN_AND_RETURN_SUCCESS_IF(allow_incomplete_logs_, + kIncompleteLogError); + return ParseStatus::Error("Failed to read field tag varint", __FILE__, + __LINE__); + } + s = event_start; + + if (tag >> 1 == static_cast(RtcEvent::Type::BeginV3Log)) { + return ParseStreamInternalV3(s); + } while (!s.empty()) { - absl::string_view event_start = s; - bool success = false; - - // 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; - uint64_t tag = 0; + // If not, "reset" event_start and read the field tag for the next event. + event_start = s; std::tie(success, s) = DecodeVarInt(s, &tag); if (!success) { RTC_LOG(LS_WARNING) @@ -1288,6 +1310,7 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStreamInternal( return ParseStatus::Error("Failed to read field tag varint", __FILE__, __LINE__); } + constexpr uint64_t kWireTypeMask = 0x07; const uint64_t wire_type = tag & kWireTypeMask; if (wire_type != 2) { @@ -1357,6 +1380,58 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStreamInternal( return ParseStatus::Success(); } +ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStreamInternalV3( + absl::string_view s) { + constexpr uint64_t kMaxEventSize = 10000000; // Sanity check. + bool expect_begin_log_event = true; + bool success = false; + + while (!s.empty()) { + // Read event type. + uint64_t event_tag = 0; + std::tie(success, s) = DecodeVarInt(s, &event_tag); + RTC_PARSE_CHECK_OR_RETURN_MESSAGE(success, "Failed to read event type."); + bool batched = event_tag & 1; + uint64_t event_type = event_tag >> 1; + + // Read event size + uint64_t event_size_bytes = 0; + std::tie(success, s) = DecodeVarInt(s, &event_size_bytes); + RTC_PARSE_CHECK_OR_RETURN_MESSAGE(success, "Failed to read event size."); + if (event_size_bytes > kMaxEventSize || event_size_bytes > s.size()) { + RTC_LOG(LS_WARNING) << "Event size is too large."; + RTC_PARSE_CHECK_OR_RETURN_LE(event_size_bytes, kMaxEventSize); + RTC_PARSE_CHECK_OR_RETURN_LE(event_size_bytes, s.size()); + } + + // Read remaining event fields into a buffer. + absl::string_view event_fields = s.substr(0, event_size_bytes); + s = s.substr(event_size_bytes); + + if (expect_begin_log_event) { + RTC_PARSE_CHECK_OR_RETURN_EQ( + event_type, static_cast(RtcEvent::Type::BeginV3Log)); + expect_begin_log_event = false; + } + + switch (event_type) { + case static_cast(RtcEvent::Type::BeginV3Log): + RtcEventBeginLog::Parse(event_fields, batched, start_log_events_); + break; + case static_cast(RtcEvent::Type::EndV3Log): + RtcEventEndLog::Parse(event_fields, batched, stop_log_events_); + expect_begin_log_event = true; + break; + case static_cast(RtcEvent::Type::AlrStateEvent): + RtcEventAlrState::Parse(event_fields, batched, alr_state_events_); + break; + // ADD NEW EVENTS HERE + } + } + + return ParseStatus::Success(); +} + template void ParsedRtcEventLog::StoreFirstAndLastTimestamp(const std::vector& v) { if (v.empty()) diff --git a/logging/rtc_event_log/rtc_event_log_parser.h b/logging/rtc_event_log/rtc_event_log_parser.h index bbd8df28c0..a51b91cda5 100644 --- a/logging/rtc_event_log/rtc_event_log_parser.h +++ b/logging/rtc_event_log/rtc_event_log_parser.h @@ -584,6 +584,7 @@ class ParsedRtcEventLog { private: ABSL_MUST_USE_RESULT ParseStatus ParseStreamInternal(absl::string_view s); + ABSL_MUST_USE_RESULT ParseStatus ParseStreamInternalV3(absl::string_view s); ABSL_MUST_USE_RESULT ParseStatus StoreParsedLegacyEvent(const rtclog::Event& event);