diff --git a/logging/BUILD.gn b/logging/BUILD.gn index 16537e2dcf..71cb41327f 100644 --- a/logging/BUILD.gn +++ b/logging/BUILD.gn @@ -291,17 +291,58 @@ rtc_library("rtc_event_number_encodings") { ] } -# TODO(eladalon): Break down into (1) encoder and (2) decoder; we don't need -# the decoder code in the WebRTC library, only in unit tests and tools. -rtc_library("rtc_event_log_impl_encoder") { +rtc_library("rtc_event_log_blob_encoding") { sources = [ "rtc_event_log/encoder/blob_encoding.cc", "rtc_event_log/encoder/blob_encoding.h", - "rtc_event_log/encoder/delta_encoding.cc", - "rtc_event_log/encoder/delta_encoding.h", + ] + deps = [ + ":rtc_event_number_encodings", + "../rtc_base:checks", + "../rtc_base:logging", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_library("rtc_event_log_optional_blob_encoding") { + sources = [ "rtc_event_log/encoder/optional_blob_encoding.cc", "rtc_event_log/encoder/optional_blob_encoding.h", ] + deps = [ + "../rtc_base:bit_buffer", + "../rtc_base:bitstream_reader", + "../rtc_base:checks", + "../rtc_base:logging", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_library("rtc_event_log_delta_encoding") { + sources = [ + "rtc_event_log/encoder/delta_encoding.cc", + "rtc_event_log/encoder/delta_encoding.h", + ] + deps = [ + ":rtc_event_number_encodings", + "../rtc_base:bit_buffer", + "../rtc_base:bitstream_reader", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:safe_conversions", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_library("rtc_event_log_impl_encoder") { + sources = [] defines = [] @@ -327,6 +368,7 @@ rtc_library("rtc_event_log_impl_encoder") { if (rtc_enable_protobuf) { deps += [ + ":dependency_descriptor_encoder_decoder", ":ice_log", ":rtc_event_audio", ":rtc_event_begin_end", @@ -336,6 +378,8 @@ rtc_library("rtc_event_log_impl_encoder") { ":rtc_event_generic_packet_events", ":rtc_event_log2_proto", ":rtc_event_log_api", + ":rtc_event_log_blob_encoding", + ":rtc_event_log_delta_encoding", ":rtc_event_log_proto", ":rtc_event_pacing", ":rtc_event_rtp_rtcp", @@ -357,6 +401,39 @@ rtc_library("rtc_event_log_impl_encoder") { } } +if (rtc_enable_protobuf) { + rtc_library("dependency_descriptor_encoder_decoder") { + sources = [ + "rtc_event_log/dependency_descriptor_encoder_decoder.cc", + "rtc_event_log/dependency_descriptor_encoder_decoder.h", + ] + deps = [ + ":rtc_event_log2_proto_include", + ":rtc_event_log_delta_encoding", + ":rtc_event_log_optional_blob_encoding", + ":rtc_event_log_parse_status", + ":rtc_event_log_proto", # Why does this need to be included here? + "../rtc_base:bitstream_reader", + "../rtc_base:checks", + "../rtc_base:logging", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] + } +} + +if (rtc_enable_protobuf) { + rtc_source_set("rtc_event_log2_proto_include") { + sources = [ "rtc_event_log/rtc_event_log2_proto_include.h" ] + deps = [ + ":rtc_event_log2_proto", + "../rtc_base:ignore_wundef", + ] + } +} + # TODO(bugs.webrtc.org/6463): For backwards compatibility; delete as # soon as downstream dependencies are updated. rtc_source_set("rtc_event_log_impl_output") { @@ -439,6 +516,7 @@ if (rtc_enable_protobuf) { ] deps = [ + ":dependency_descriptor_encoder_decoder", ":ice_log", ":rtc_event_audio", ":rtc_event_begin_end", @@ -446,6 +524,8 @@ if (rtc_enable_protobuf) { ":rtc_event_frame_events", ":rtc_event_generic_packet_events", ":rtc_event_log2_proto", + ":rtc_event_log_blob_encoding", + ":rtc_event_log_delta_encoding", ":rtc_event_log_impl_encoder", ":rtc_event_log_proto", ":rtc_event_number_encodings", @@ -488,6 +568,7 @@ if (rtc_enable_protobuf) { testonly = true assert(rtc_enable_protobuf) sources = [ + "rtc_event_log/dependency_descriptor_encoder_decoder_unittest.cc", "rtc_event_log/encoder/blob_encoding_unittest.cc", "rtc_event_log/encoder/delta_encoding_unittest.cc", "rtc_event_log/encoder/optional_blob_encoding_unittest.cc", @@ -502,6 +583,7 @@ if (rtc_enable_protobuf) { "rtc_event_log/rtc_event_processor_unittest.cc", ] deps = [ + ":dependency_descriptor_encoder_decoder", ":ice_log", ":rtc_event_audio", ":rtc_event_bwe", @@ -509,8 +591,12 @@ if (rtc_enable_protobuf) { ":rtc_event_frame_events", ":rtc_event_generic_packet_events", ":rtc_event_log2_proto", + ":rtc_event_log2_proto_include", + ":rtc_event_log_blob_encoding", + ":rtc_event_log_delta_encoding", ":rtc_event_log_impl", ":rtc_event_log_impl_encoder", + ":rtc_event_log_optional_blob_encoding", ":rtc_event_log_parser", ":rtc_event_log_proto", ":rtc_event_number_encodings", diff --git a/logging/rtc_event_log/dependency_descriptor_encoder_decoder.cc b/logging/rtc_event_log/dependency_descriptor_encoder_decoder.cc new file mode 100644 index 0000000000..c74c8c4b63 --- /dev/null +++ b/logging/rtc_event_log/dependency_descriptor_encoder_decoder.cc @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2023 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/dependency_descriptor_encoder_decoder.h" + +#include +#include + +#include "logging/rtc_event_log/encoder/delta_encoding.h" +#include "logging/rtc_event_log/encoder/optional_blob_encoding.h" +#include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" +#include "logging/rtc_event_log/rtc_event_log2_proto_include.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +// static +absl::optional +RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + const std::vector>& raw_dd_data) { + if (raw_dd_data.empty()) { + return {}; + } + + for (const auto& dd : raw_dd_data) { + if (!dd.empty() && dd.size() < 3) { + RTC_LOG(LS_WARNING) << "DependencyDescriptor size not valid."; + return {}; + } + } + + rtclog2::DependencyDescriptorsWireInfo res; + const rtc::ArrayView& base_dd = raw_dd_data[0]; + auto delta_dds = + rtc::MakeArrayView(raw_dd_data.data(), raw_dd_data.size()).subview(1); + + // Start and end bit. + { + absl::optional start_end_bit; + if (!base_dd.empty()) { + start_end_bit = (base_dd[0] >> 6); + res.set_start_end_bit(*start_end_bit); + } + if (!delta_dds.empty()) { + std::vector> values(delta_dds.size()); + for (size_t i = 0; i < delta_dds.size(); ++i) { + if (!delta_dds[i].empty()) { + values[i] = delta_dds[i][0] >> 6; + } + } + std::string encoded_deltas = EncodeDeltas(start_end_bit, values); + if (!encoded_deltas.empty()) { + res.set_start_end_bit_deltas(encoded_deltas); + } + } + } + + // Template IDs. + { + absl::optional template_id; + if (!base_dd.empty()) { + template_id = (base_dd[0] & 0b0011'1111); + res.set_template_id(*template_id); + } + + if (!delta_dds.empty()) { + std::vector> values(delta_dds.size()); + for (size_t i = 0; i < delta_dds.size(); ++i) { + if (!delta_dds[i].empty()) { + values[i] = delta_dds[i][0] & 0b0011'1111; + } + } + std::string encoded_deltas = EncodeDeltas(template_id, values); + if (!encoded_deltas.empty()) { + res.set_template_id_deltas(encoded_deltas); + } + } + } + + // Frame IDs. + { + absl::optional frame_id; + if (!base_dd.empty()) { + frame_id = (uint16_t{base_dd[1]} << 8) + base_dd[2]; + res.set_frame_id(*frame_id); + } + + if (!delta_dds.empty()) { + std::vector> values(delta_dds.size()); + for (size_t i = 0; i < delta_dds.size(); ++i) { + if (!delta_dds[i].empty()) { + values[i] = (uint16_t{delta_dds[i][1]} << 8) + delta_dds[i][2]; + } + } + std::string encoded_deltas = EncodeDeltas(frame_id, values); + if (!encoded_deltas.empty()) { + res.set_frame_id_deltas(encoded_deltas); + } + } + } + + // Extended info + { + std::vector> values(raw_dd_data.size()); + for (size_t i = 0; i < raw_dd_data.size(); ++i) { + if (raw_dd_data[i].size() > 3) { + auto extended_info = raw_dd_data[i].subview(3); + values[i] = {reinterpret_cast(extended_info.data()), + extended_info.size()}; + } + } + + std::string encoded_blobs = EncodeOptionalBlobs(values); + if (!encoded_blobs.empty()) { + res.set_extended_infos(encoded_blobs); + } + } + + return res; +} + +// static +RtcEventLogParseStatusOr>> +RtcEventLogDependencyDescriptorEncoderDecoder::Decode( + const rtclog2::DependencyDescriptorsWireInfo& dd_wire_info, + size_t num_packets) { + if (num_packets == 0) { + return {std::vector>()}; + } + + std::vector> res(num_packets); + + absl::optional start_end_bit_base; + if (dd_wire_info.has_start_end_bit()) { + start_end_bit_base = dd_wire_info.start_end_bit(); + } + absl::optional template_id_base; + if (dd_wire_info.has_template_id()) { + template_id_base = dd_wire_info.template_id(); + } + absl::optional frame_id_base; + if (dd_wire_info.has_frame_id()) { + frame_id_base = dd_wire_info.frame_id(); + } + + std::vector> start_end_bit_deltas; + if (dd_wire_info.has_start_end_bit_deltas()) { + start_end_bit_deltas = DecodeDeltas(dd_wire_info.start_end_bit_deltas(), + start_end_bit_base, num_packets - 1); + RTC_DCHECK(start_end_bit_deltas.empty() || + start_end_bit_deltas.size() == (num_packets - 1)); + } + std::vector> template_id_deltas; + if (dd_wire_info.has_template_id_deltas()) { + template_id_deltas = DecodeDeltas(dd_wire_info.template_id_deltas(), + template_id_base, num_packets - 1); + RTC_DCHECK(template_id_deltas.empty() || + template_id_deltas.size() == (num_packets - 1)); + } + std::vector> frame_id_deltas; + if (dd_wire_info.has_frame_id_deltas()) { + frame_id_deltas = DecodeDeltas(dd_wire_info.frame_id_deltas(), + frame_id_base, num_packets - 1); + RTC_DCHECK(frame_id_deltas.empty() || + frame_id_deltas.size() == (num_packets - 1)); + } + std::vector> extended_infos; + if (dd_wire_info.has_extended_infos()) { + extended_infos = + DecodeOptionalBlobs(dd_wire_info.extended_infos(), num_packets); + } + + auto recreate_raw_dd = [&](int i, const absl::optional& be, + const absl::optional& tid, + const absl::optional& fid) { + absl::string_view ext; + if (!extended_infos.empty() && extended_infos[i].has_value()) { + ext = *extended_infos[i]; + } + if (be.has_value() && tid.has_value() && fid.has_value()) { + res[i].reserve(3 + ext.size()); + res[i].push_back((*be << 6) | *tid); + res[i].push_back(*fid >> 8); + res[i].push_back(*fid); + if (!ext.empty()) { + res[i].insert(res[i].end(), ext.begin(), ext.end()); + } + } else if (be.has_value() || tid.has_value() || fid.has_value()) { + RTC_PARSE_RETURN_ERROR("Not all required fields present."); + } else if (!ext.empty()) { + RTC_PARSE_RETURN_ERROR( + "Extended info present without required fields present."); + } + + return RtcEventLogParseStatus::Success(); + }; + + RTC_RETURN_IF_ERROR( + recreate_raw_dd(0, start_end_bit_base, template_id_base, frame_id_base)); + + for (size_t i = 1; i < num_packets; ++i) { + RTC_RETURN_IF_ERROR(recreate_raw_dd( + i, + start_end_bit_deltas.empty() ? start_end_bit_base + : start_end_bit_deltas[i - 1], + template_id_deltas.empty() ? template_id_base + : template_id_deltas[i - 1], + frame_id_deltas.empty() ? frame_id_base : frame_id_deltas[i - 1])); + } + + return res; +} + +} // namespace webrtc diff --git a/logging/rtc_event_log/dependency_descriptor_encoder_decoder.h b/logging/rtc_event_log/dependency_descriptor_encoder_decoder.h new file mode 100644 index 0000000000..6729a38b6d --- /dev/null +++ b/logging/rtc_event_log/dependency_descriptor_encoder_decoder.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 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_DEPENDENCY_DESCRIPTOR_ENCODER_DECODER_H_ +#define LOGGING_RTC_EVENT_LOG_DEPENDENCY_DESCRIPTOR_ENCODER_DECODER_H_ + +#include + +#include "absl/types/optional.h" +#include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" +#include "logging/rtc_event_log/rtc_event_log2_proto_include.h" +#include "rtc_base/bitstream_reader.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +class RtcEventLogDependencyDescriptorEncoderDecoder { + public: + static absl::optional Encode( + const std::vector>& raw_dd_data); + static RtcEventLogParseStatusOr>> Decode( + const rtclog2::DependencyDescriptorsWireInfo& dd_wire_info, + size_t num_packets); +}; + +} // namespace webrtc + +#endif // LOGGING_RTC_EVENT_LOG_DEPENDENCY_DESCRIPTOR_ENCODER_DECODER_H_ diff --git a/logging/rtc_event_log/dependency_descriptor_encoder_decoder_unittest.cc b/logging/rtc_event_log/dependency_descriptor_encoder_decoder_unittest.cc new file mode 100644 index 0000000000..04c7147e28 --- /dev/null +++ b/logging/rtc_event_log/dependency_descriptor_encoder_decoder_unittest.cc @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2023 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/dependency_descriptor_encoder_decoder.h" + +#include +#include + +#include "logging/rtc_event_log/encoder/delta_encoding.h" +#include "logging/rtc_event_log/encoder/optional_blob_encoding.h" +#include "logging/rtc_event_log/rtc_event_log2_proto_include.h" +#include "test/gmock.h" +#include "test/gtest.h" + +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::IsEmpty; + +namespace webrtc { +namespace { + +constexpr uint64_t kNone = 0; +constexpr uint64_t kEnd = 1; +constexpr uint64_t kBegin = 2; +constexpr uint64_t kBeginEnd = 3; + +class DdBuilder { + public: + DdBuilder() : raw_dd(3) {} + DdBuilder& B() { + raw_dd[0] |= 1 << 7; + return *this; + } + DdBuilder& E() { + raw_dd[0] |= 1 << 6; + return *this; + } + DdBuilder& Tid(uint8_t id) { + RTC_DCHECK_LE(id, 0x3F); + raw_dd[0] |= id; + return *this; + } + DdBuilder& Fid(uint16_t id) { + raw_dd[1] = id >> 8; + raw_dd[2] = id; + return *this; + } + DdBuilder& Ext(const std::string& ext) { + raw_dd.insert(raw_dd.end(), ext.begin(), ext.end()); + return *this; + } + std::vector Build() { return raw_dd; } + + private: + std::vector raw_dd; +}; + +TEST(RtcEventLogDependencyDescriptorEncoding, SinglePacket) { + auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + {DdBuilder().B().E().Tid(12).Fid(345).Ext("abc").Build()}); + EXPECT_THAT(encoded->start_end_bit(), Eq(kBeginEnd)); + EXPECT_THAT(encoded->template_id(), Eq(12u)); + EXPECT_THAT(encoded->frame_id(), Eq(345u)); + EXPECT_THAT(encoded->extended_infos(), Eq(EncodeOptionalBlobs({{"abc"}}))); + EXPECT_THAT(encoded->has_start_end_bit_deltas(), Eq(false)); + EXPECT_THAT(encoded->has_template_id_deltas(), Eq(false)); + EXPECT_THAT(encoded->has_frame_id_deltas(), Eq(false)); +} + +TEST(RtcEventLogDependencyDescriptorEncoding, MultiplePackets) { + auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + {DdBuilder().B().E().Tid(1).Fid(1).Ext("abc").Build(), + DdBuilder().B().Tid(2).Fid(3).Ext("def").Build(), + DdBuilder().E().Tid(2).Fid(3).Build()}); + + EXPECT_THAT(encoded->start_end_bit(), Eq(kBeginEnd)); + EXPECT_THAT(encoded->template_id(), Eq(1u)); + EXPECT_THAT(encoded->frame_id(), Eq(1u)); + EXPECT_THAT(encoded->extended_infos(), + Eq(EncodeOptionalBlobs({{"abc"}, {"def"}, {}}))); + EXPECT_THAT(encoded->start_end_bit_deltas(), + Eq(EncodeDeltas({kBeginEnd}, {{kBegin}, {kEnd}}))); + EXPECT_THAT(encoded->template_id_deltas(), Eq(EncodeDeltas({1}, {{2}, {2}}))); + EXPECT_THAT(encoded->frame_id_deltas(), Eq(EncodeDeltas({1}, {{3}, {3}}))); +} + +TEST(RtcEventLogDependencyDescriptorEncoding, MultiplePacketsOneFrame) { + auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + {DdBuilder().B().Tid(1).Fid(1).Ext("abc").Build(), + DdBuilder().Tid(1).Fid(1).Build(), + DdBuilder().E().Tid(1).Fid(1).Build()}); + + EXPECT_THAT(encoded->start_end_bit(), Eq(kBegin)); + EXPECT_THAT(encoded->template_id(), Eq(1u)); + EXPECT_THAT(encoded->frame_id(), Eq(1u)); + EXPECT_THAT(encoded->extended_infos(), + Eq(EncodeOptionalBlobs({{"abc"}, {}, {}}))); + EXPECT_THAT(encoded->start_end_bit_deltas(), + Eq(EncodeDeltas({kBegin}, {{kNone}, {kEnd}}))); + EXPECT_THAT(encoded->has_template_id_deltas(), Eq(false)); + EXPECT_THAT(encoded->has_frame_id_deltas(), Eq(false)); +} + +TEST(RtcEventLogDependencyDescriptorEncoding, FirstPacketMissingDd) { + auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + {{}, + DdBuilder().B().E().Tid(1).Fid(1).Build(), + DdBuilder().E().Tid(2).Fid(3).Ext("three!").Build()}); + + EXPECT_THAT(encoded->has_start_end_bit(), Eq(false)); + EXPECT_THAT(encoded->has_template_id(), Eq(false)); + EXPECT_THAT(encoded->has_frame_id(), Eq(false)); + + EXPECT_THAT(encoded->extended_infos(), + Eq(EncodeOptionalBlobs({{}, {}, {"three!"}}))); + EXPECT_THAT(encoded->start_end_bit_deltas(), + Eq(EncodeDeltas({}, {{kBeginEnd}, {kEnd}}))); + EXPECT_THAT(encoded->template_id_deltas(), Eq(EncodeDeltas({}, {{1}, {2}}))); + EXPECT_THAT(encoded->frame_id_deltas(), Eq(EncodeDeltas({}, {{1}, {3}}))); +} + +TEST(RtcEventLogDependencyDescriptorEncoding, SomePacketMissingDd) { + auto encoded = RtcEventLogDependencyDescriptorEncoderDecoder::Encode( + {DdBuilder().B().E().Tid(1).Fid(1).Build(), + {}, + DdBuilder().E().Tid(2).Fid(3).Build()}); + + EXPECT_THAT(encoded->start_end_bit(), Eq(kBeginEnd)); + EXPECT_THAT(encoded->template_id(), Eq(1u)); + EXPECT_THAT(encoded->frame_id(), Eq(1u)); + EXPECT_THAT(encoded->extended_infos(), Eq(EncodeOptionalBlobs({{}, {}, {}}))); + EXPECT_THAT(encoded->start_end_bit_deltas(), + Eq(EncodeDeltas({kBeginEnd}, {{}, {kEnd}}))); + EXPECT_THAT(encoded->template_id_deltas(), Eq(EncodeDeltas({1}, {{}, {2}}))); + EXPECT_THAT(encoded->frame_id_deltas(), Eq(EncodeDeltas({1}, {{}, {3}}))); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, SinglePacket) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_start_end_bit(kBeginEnd); + encoded.set_template_id(1); + encoded.set_frame_id(2); + encoded.set_extended_infos(EncodeOptionalBlobs({"abc"})); + + auto decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 1); + + auto expected = DdBuilder().B().E().Tid(1).Fid(2).Ext("abc").Build(); + ASSERT_THAT(decoded.ok(), Eq(true)); + EXPECT_THAT(decoded.value(), ElementsAre(expected)); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, MultiplePackets) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_start_end_bit(kBeginEnd); + encoded.set_template_id(1); + encoded.set_frame_id(1); + encoded.set_extended_infos(EncodeOptionalBlobs({{"abc"}, {"def"}, {}})); + encoded.set_start_end_bit_deltas( + EncodeDeltas({kBeginEnd}, {{kBegin}, {kEnd}})); + encoded.set_template_id_deltas(EncodeDeltas({1}, {{2}, {2}})); + encoded.set_frame_id_deltas(EncodeDeltas({1}, {{3}, {3}})); + + auto decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 3); + + auto first_expected = DdBuilder().B().E().Tid(1).Fid(1).Ext("abc").Build(); + auto second_expected = DdBuilder().B().Tid(2).Fid(3).Ext("def").Build(); + auto third_expected = DdBuilder().E().Tid(2).Fid(3).Build(); + ASSERT_THAT(decoded.ok(), Eq(true)); + EXPECT_THAT(decoded.value(), + ElementsAre(first_expected, second_expected, third_expected)); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, MultiplePacketsOneFrame) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_start_end_bit(kBegin); + encoded.set_template_id(1u); + encoded.set_frame_id(1u); + encoded.set_extended_infos(EncodeOptionalBlobs({{"abc"}, {}, {}})); + encoded.set_start_end_bit_deltas(EncodeDeltas({kBegin}, {{kNone}, {kEnd}})); + + auto decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 3); + + auto first_expected = DdBuilder().B().Tid(1).Fid(1).Ext("abc").Build(); + auto second_expected = DdBuilder().Tid(1).Fid(1).Build(); + auto third_expected = DdBuilder().E().Tid(1).Fid(1).Build(); + ASSERT_THAT(decoded.ok(), Eq(true)); + EXPECT_THAT(decoded.value(), + ElementsAre(first_expected, second_expected, third_expected)); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, FirstPacketMissingDd) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_extended_infos(EncodeOptionalBlobs({{}, {}, {"three!"}})); + encoded.set_start_end_bit_deltas(EncodeDeltas({}, {{kBeginEnd}, {kEnd}})); + encoded.set_template_id_deltas(EncodeDeltas({}, {{1}, {2}})); + encoded.set_frame_id_deltas(EncodeDeltas({}, {{1}, {3}})); + + auto decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 3); + + auto second_expected = DdBuilder().B().E().Tid(1).Fid(1).Build(); + auto third_expected = DdBuilder().E().Tid(2).Fid(3).Ext("three!").Build(); + ASSERT_THAT(decoded.ok(), Eq(true)); + EXPECT_THAT(decoded.value(), + ElementsAre(IsEmpty(), second_expected, third_expected)); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, SomePacketMissingDd) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_start_end_bit(kBeginEnd); + encoded.set_template_id(1u); + encoded.set_frame_id(1u); + encoded.set_extended_infos(EncodeOptionalBlobs({{}, {}, {}})); + encoded.set_start_end_bit_deltas(EncodeDeltas({kBeginEnd}, {{}, {kEnd}})); + encoded.set_template_id_deltas(EncodeDeltas({1}, {{}, {2}})); + encoded.set_frame_id_deltas(EncodeDeltas({1}, {{}, {3}})); + + auto decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 3); + + auto first_expected = DdBuilder().B().E().Tid(1).Fid(1).Build(); + auto third_expected = DdBuilder().E().Tid(2).Fid(3).Build(); + ASSERT_THAT(decoded.ok(), Eq(true)); + EXPECT_THAT(decoded.value(), + ElementsAre(first_expected, IsEmpty(), third_expected)); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, ExtendedWithoutRequiredFields) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_start_end_bit_deltas(EncodeDeltas({}, {{kNone}})); + encoded.set_template_id_deltas(EncodeDeltas({}, {{1}})); + encoded.set_frame_id_deltas(EncodeDeltas({}, {{1}})); + encoded.set_extended_infos(EncodeOptionalBlobs({{"Should not be here"}, {}})); + + auto decoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(encoded, 2); + + ASSERT_THAT(decoded.ok(), Eq(false)); +} + +TEST(RtcEventLogDependencyDescriptorDecoding, MissingRequiredField) { + rtclog2::DependencyDescriptorsWireInfo encoded; + encoded.set_start_end_bit(kBeginEnd); + encoded.set_template_id(1u); + encoded.set_frame_id(1u); + + { + rtclog2::DependencyDescriptorsWireInfo missing = encoded; + missing.clear_start_end_bit(); + EXPECT_THAT( + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(missing, 1).ok(), + Eq(false)); + } + + { + rtclog2::DependencyDescriptorsWireInfo missing = encoded; + missing.clear_template_id(); + EXPECT_THAT( + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(missing, 1).ok(), + Eq(false)); + } + + { + rtclog2::DependencyDescriptorsWireInfo missing = encoded; + missing.clear_frame_id(); + EXPECT_THAT( + RtcEventLogDependencyDescriptorEncoderDecoder::Decode(missing, 1).ok(), + Eq(false)); + } +} + +} // namespace +} // namespace webrtc diff --git a/logging/rtc_event_log/events/rtc_event_log_parse_status.h b/logging/rtc_event_log/events/rtc_event_log_parse_status.h index e7ee5afe30..e7d6de7d60 100644 --- a/logging/rtc_event_log/events/rtc_event_log_parse_status.h +++ b/logging/rtc_event_log/events/rtc_event_log_parse_status.h @@ -17,6 +17,11 @@ #include "absl/strings/string_view.h" #include "rtc_base/checks.h" +#define RTC_PARSE_RETURN_ERROR(X) \ + do { \ + return RtcEventLogParseStatus::Error(#X, __FILE__, __LINE__); \ + } while (0) + #define RTC_PARSE_CHECK_OR_RETURN(X) \ do { \ if (!(X)) \ diff --git a/logging/rtc_event_log/rtc_event_log2.proto b/logging/rtc_event_log/rtc_event_log2.proto index 2545be87e5..1cf5a06ebe 100644 --- a/logging/rtc_event_log/rtc_event_log2.proto +++ b/logging/rtc_event_log/rtc_event_log2.proto @@ -103,6 +103,27 @@ message GenericAckReceived { optional bytes receive_acked_packet_time_ms_deltas = 20; } +message DependencyDescriptorsWireInfo { + // required + // Base and delta encoded B and E bits represented as two bit numbers. + optional uint32 start_end_bit = 1; + optional bytes start_end_bit_deltas = 2; + + // required + // Base and delta encoded template IDs, represented as six bit numbers. + optional uint32 template_id = 3; + optional bytes template_id_deltas = 4; + + // required + // Base and delta compressed frame IDs. + optional uint32 frame_id = 5; + optional bytes frame_id_deltas = 6; + + // optional - set if any DD contains extended information. + // The extended info encoded as optional blobs. + optional bytes extended_infos = 7; +} + message IncomingRtpPackets { // required optional int64 timestamp_ms = 1; @@ -148,6 +169,8 @@ message IncomingRtpPackets { // `audio_level` and `voice_activity` are always used in conjunction. optional uint32 audio_level = 19; optional bool voice_activity = 20; + // Encodes all DD information in the batch, not just the base event. + optional DependencyDescriptorsWireInfo dependency_descriptor = 21; // TODO(terelius): Add other header extensions like playout delay? // Delta encodings. @@ -217,6 +240,8 @@ message OutgoingRtpPackets { optional uint32 audio_level = 19; optional bool voice_activity = 20; // TODO(terelius): Add other header extensions like playout delay? + // Encodes all DD information in the batch, not just the base event. + optional DependencyDescriptorsWireInfo dependency_descriptor = 21; // Delta encodings. optional bytes timestamp_ms_deltas = 101; diff --git a/logging/rtc_event_log/rtc_event_log2_proto_include.h b/logging/rtc_event_log/rtc_event_log2_proto_include.h new file mode 100644 index 0000000000..3e43103897 --- /dev/null +++ b/logging/rtc_event_log/rtc_event_log2_proto_include.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 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_RTC_EVENT_LOG2_PROTO_INCLUDE_H_ +#define LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG2_PROTO_INCLUDE_H_ + +#include "rtc_base/ignore_wundef.h" + +// *.pb.h files are generated at build-time by the protobuf compiler. +RTC_PUSH_IGNORING_WUNDEF() +#ifdef WEBRTC_ANDROID_PLATFORM_BUILD +#include "external/webrtc/webrtc/logging/rtc_event_log/rtc_event_log2.pb.h" +#else +#include "logging/rtc_event_log/rtc_event_log2.pb.h" +#endif +RTC_POP_IGNORING_WUNDEF() + +#endif // LOGGING_RTC_EVENT_LOG_RTC_EVENT_LOG2_PROTO_INCLUDE_H_