Reland "Wire up proto-free event log format in encoder and parser."

This is a reland of 46333dbf6211ea197965c30fdbecbeb62bc81e5b

Original change's description:
> Wire up proto-free event log format in encoder and parser.
>
> Encode ALR state events as an example. The ALR state unit tests pass with the new format, but the tests are not enabled in this CL since the other event types aren't encoded yet.
>
> Bug: webrtc:11933
> Change-Id: I3ba22778b55f24e2e2bd7d95bb9b17de29ef899f
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/234520
> Reviewed-by: Sebastian Jansson <srte@webrtc.org>
> Commit-Queue: Björn Terelius <terelius@webrtc.org>
> Cr-Commit-Position: refs/heads/main@{#35752}

Bug: webrtc:11933
Change-Id: Ia8b23cfb134b61c9ef02aa21189ecbd239783c40
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/248141
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Commit-Queue: Björn Terelius <terelius@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35762}
diff --git a/logging/BUILD.gn b/logging/BUILD.gn
index 67c01fa..ce40528 100644
--- a/logging/BUILD.gn
+++ b/logging/BUILD.gn
@@ -37,6 +37,7 @@
   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_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_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 bd898a1..be9352a 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 @@
         encoder_ = std::make_unique<RtcEventLogEncoderNewFormat>();
         break;
       case RtcEventLog::EncodingType::ProtoFree:
-        // TODO(terelius): Enable test once the format has been wired up.
-        RTC_CHECK_NOTREACHED();
+        encoder_ = std::make_unique<RtcEventLogEncoderV3>();
         break;
     }
     encoded_ =
@@ -1290,8 +1290,7 @@
         encoder_ = std::make_unique<RtcEventLogEncoderNewFormat>();
         break;
       case RtcEventLog::EncodingType::ProtoFree:
-        // TODO(terelius): Enable test once the format has been wired up.
-        RTC_CHECK_NOTREACHED();
+        encoder_ = std::make_unique<RtcEventLogEncoderV3>();
         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 0000000..1fd5fa6
--- /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 <string>
+#include <vector>
+
+#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<RtcEventBeginLog> begin_log =
+      std::make_unique<RtcEventBeginLog>(Timestamp::Micros(timestamp_us),
+                                         Timestamp::Micros(utc_time_us));
+  std::vector<const RtcEvent*> 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<RtcEventEndLog> end_log =
+      std::make_unique<RtcEventEndLog>(Timestamp::Micros(timestamp_us));
+  std::vector<const RtcEvent*> 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<std::unique_ptr<RtcEvent>>::const_iterator begin,
+    std::deque<std::unique_ptr<RtcEvent>>::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<EventGroupKey, std::vector<const RtcEvent*>> 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 0000000..cb796ec
--- /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 <deque>
+#include <map>
+#include <memory>
+#include <string>
+
+#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<std::unique_ptr<RtcEvent>>::const_iterator begin,
+      std::deque<std::unique_ptr<RtcEvent>>::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<RtcEvent::Type,
+           std::function<std::string(rtc::ArrayView<const RtcEvent*>)>>
+      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 3c307b9..25941eb 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, LoggedAlrStateEvent, bool>
+    RtcEventAlrState::definition_;
 
 RtcEventAlrState::RtcEventAlrState(bool in_alr) : in_alr_(in_alr) {}
 
@@ -25,4 +28,11 @@
   return absl::WrapUnique<RtcEventAlrState>(new RtcEventAlrState(*this));
 }
 
+RtcEventLogParseStatus RtcEventAlrState::Parse(
+    absl::string_view s,
+    bool batched,
+    std::vector<LoggedAlrStateEvent>& 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 74d6601..2bc7408 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 <memory>
+#include <string>
+#include <vector>
 
 #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 @@
 
   bool in_alr() const { return in_alr_; }
 
+  static std::string Encode(rtc::ArrayView<const RtcEvent*> batch) {
+    return RtcEventAlrState::definition_.EncodeBatch(batch);
+  }
+
+  static RtcEventLogParseStatus Parse(absl::string_view s,
+                                      bool batched,
+                                      std::vector<LoggedAlrStateEvent>& 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<RtcEventAlrState,
+                                      LoggedAlrStateEvent,
+                                      bool>
+      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 0000000..43dd7eb
--- /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 <memory>
+#include <string>
+#include <vector>
+
+#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 <typename EventType, typename LoggedType, typename T>
+struct RtcEventFieldDefinition {
+  const T EventType::*event_member;
+  T LoggedType::*logged_member;
+  FieldParameters params;
+};
+
+// Base case
+template <typename EventType, typename LoggedType, typename... Ts>
+class RtcEventDefinitionImpl {
+ public:
+  void EncodeImpl(EventEncoder&, rtc::ArrayView<const RtcEvent*>) const {}
+  RtcEventLogParseStatus ParseImpl(EventParser&,
+                                   rtc::ArrayView<LoggedType>) const {
+    return RtcEventLogParseStatus::Success();
+  }
+};
+
+// Recursive case
+template <typename EventType, typename LoggedType, typename T, typename... Ts>
+class RtcEventDefinitionImpl<EventType, LoggedType, T, Ts...> {
+ public:
+  constexpr RtcEventDefinitionImpl(
+      RtcEventFieldDefinition<EventType, LoggedType, T> field,
+      RtcEventFieldDefinition<EventType, LoggedType, Ts>... rest)
+      : field_(field), rest_(rest...) {}
+
+  void EncodeImpl(EventEncoder& encoder,
+                  rtc::ArrayView<const RtcEvent*> batch) const {
+    auto values = ExtractRtcEventMember(batch, field_.event_member);
+    encoder.EncodeField(field_.params, values);
+    rest_.EncodeImpl(encoder, batch);
+  }
+
+  RtcEventLogParseStatus ParseImpl(
+      EventParser& parser,
+      rtc::ArrayView<LoggedType> output_batch) const {
+    RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>> 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<EventType, LoggedType, T> field_;
+  RtcEventDefinitionImpl<EventType, LoggedType, Ts...> 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<RtcFoo, LoggedFoo, uint32_t, bool>(
+//   {"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 <typename EventType, typename LoggedType, typename... Ts>
+class RtcEventDefinition {
+ public:
+  constexpr RtcEventDefinition(
+      EventParameters params,
+      RtcEventFieldDefinition<EventType, LoggedType, Ts>... fields)
+      : params_(params), fields_(fields...) {}
+
+  std::string EncodeBatch(rtc::ArrayView<const RtcEvent*> batch) const {
+    EventEncoder encoder(params_, batch);
+    fields_.EncodeImpl(encoder, batch);
+    return encoder.AsString();
+  }
+
+  RtcEventLogParseStatus ParseBatch(absl::string_view s,
+                                    bool batched,
+                                    std::vector<LoggedType>& output) const {
+    EventParser parser;
+    auto status = parser.Initialize(s, batched);
+    if (!status.ok())
+      return status;
+
+    rtc::ArrayView<LoggedType> output_batch =
+        ExtendLoggedBatch(output, parser.NumEventsInBatch());
+
+    constexpr FieldParameters timestamp_params{"timestamp_ms",
+                                               FieldParameters::kTimestampField,
+                                               FieldType::kVarInt, 64};
+    RtcEventLogParseStatusOr<rtc::ArrayView<uint64_t>> 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<EventType, LoggedType, Ts...> 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 3b3eb18..7e114a2 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::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<uint64_t>(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 @@
       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 @@
   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<uint32_t>(RtcEvent::Type::BeginV3Log));
+      expect_begin_log_event = false;
+    }
+
+    switch (event_type) {
+      case static_cast<uint32_t>(RtcEvent::Type::BeginV3Log):
+        RtcEventBeginLog::Parse(event_fields, batched, start_log_events_);
+        break;
+      case static_cast<uint32_t>(RtcEvent::Type::EndV3Log):
+        RtcEventEndLog::Parse(event_fields, batched, stop_log_events_);
+        expect_begin_log_event = true;
+        break;
+      case static_cast<uint32_t>(RtcEvent::Type::AlrStateEvent):
+        RtcEventAlrState::Parse(event_fields, batched, alr_state_events_);
+        break;
+        // ADD NEW EVENTS HERE
+    }
+  }
+
+  return ParseStatus::Success();
+}
+
 template <typename T>
 void ParsedRtcEventLog::StoreFirstAndLastTimestamp(const std::vector<T>& 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 bbd8df2..a51b91c 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 @@
 
  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);