Initial implementation of PayloadTypePicker
Bug: webrtc:360058654
Change-Id: I3183939a32744e9389ae2431cc04f8aa517d7efa
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/359761
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42805}
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index fe9bb65..68c1097 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -404,6 +404,18 @@
]
}
+rtc_library("payload_type_picker") {
+ sources = [
+ "payload_type_picker.cc",
+ "payload_type_picker.h",
+ ]
+ deps = [
+ "../api:rtc_error",
+ "../media:codec",
+ "../rtc_base:strong_alias",
+ ]
+}
+
rtc_source_set("peer_connection_factory_proxy") {
visibility = [ ":*" ]
sources = [ "peer_connection_factory_proxy.h" ]
@@ -1080,6 +1092,7 @@
":ice_server_parsing",
":jsep_transport_controller",
":legacy_stats_collector",
+ ":payload_type_picker",
":peer_connection_internal",
":peer_connection_message_handler",
":rtc_stats_collector",
@@ -1963,6 +1976,7 @@
"jsep_transport_controller_unittest.cc",
"jsep_transport_unittest.cc",
"media_session_unittest.cc",
+ "payload_type_picker_unittest.cc",
"rtcp_mux_filter_unittest.cc",
"rtp_transport_unittest.cc",
"sctp_transport_unittest.cc",
@@ -1992,6 +2006,7 @@
":libjingle_peerconnection",
":media_protocol_names",
":media_session",
+ ":payload_type_picker",
":pc_test_utils",
":rtc_pc",
":rtcp_mux_filter",
diff --git a/pc/payload_type_picker.cc b/pc/payload_type_picker.cc
new file mode 100644
index 0000000..9a6368e
--- /dev/null
+++ b/pc/payload_type_picker.cc
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2024 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 "pc/payload_type_picker.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "api/rtc_error.h"
+#include "media/base/codec.h"
+
+namespace webrtc {
+
+namespace {
+
+// Note: The only fields we need from a Codec are the type (audio/video),
+// the subtype (vp8/h264/....), the clock rate, the channel count, and the
+// fmtp parameters. The use of cricket::Codec, which contains more fields,
+// is only a temporary measure.
+
+bool MatchesForSdp(const cricket::Codec& codec_1,
+ const cricket::Codec& codec_2) {
+ return codec_1.name == codec_2.name && codec_1.type == codec_2.type &&
+ codec_1.channels == codec_2.channels &&
+ codec_1.clockrate == codec_2.clockrate &&
+ codec_1.params == codec_2.params;
+}
+
+} // namespace
+
+RTCErrorOr<PayloadType> PayloadTypePicker::SuggestMapping(
+ cricket::Codec codec) {
+ return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented yet");
+}
+
+RTCError PayloadTypePicker::AddMapping(PayloadType payload_type,
+ cricket::Codec codec) {
+ return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented yet");
+}
+
+RTCError PayloadTypeRecorder::AddMapping(PayloadType payload_type,
+ cricket::Codec codec) {
+ if (payload_type_to_codec_.find(payload_type) !=
+ payload_type_to_codec_.end()) {
+ return RTCError(RTCErrorType::INVALID_PARAMETER,
+ "Attempt to insert duplicate mapping for PT");
+ }
+ payload_type_to_codec_.emplace(payload_type, codec);
+ suggester_.AddMapping(payload_type, codec);
+ return RTCError::OK();
+}
+
+std::vector<std::pair<PayloadType, cricket::Codec>>
+PayloadTypeRecorder::GetMappings() {
+ return std::vector<std::pair<PayloadType, cricket::Codec>>{};
+}
+
+RTCErrorOr<PayloadType> PayloadTypeRecorder::LookupPayloadType(
+ cricket::Codec codec) {
+ // Note that having multiple PTs mapping to the same codec is NOT an error.
+ // In this case, we return the first found (not deterministic).
+ auto result = std::find_if(
+ payload_type_to_codec_.begin(), payload_type_to_codec_.end(),
+ [codec](const auto& iter) { return MatchesForSdp(iter.second, codec); });
+ if (result == payload_type_to_codec_.end()) {
+ return RTCError(RTCErrorType::INVALID_PARAMETER,
+ "No payload type found for codec");
+ }
+ return result->first;
+}
+
+RTCErrorOr<cricket::Codec> PayloadTypeRecorder::LookupCodec(
+ PayloadType payload_type) {
+ auto result = payload_type_to_codec_.find(payload_type);
+ if (result == payload_type_to_codec_.end()) {
+ return RTCError(RTCErrorType::INVALID_PARAMETER, "No such payload type");
+ }
+ return result->second;
+}
+
+void PayloadTypeRecorder::Checkpoint() {
+ checkpoint_payload_type_to_codec_ = payload_type_to_codec_;
+}
+void PayloadTypeRecorder::Rollback() {
+ payload_type_to_codec_ = checkpoint_payload_type_to_codec_;
+}
+
+} // namespace webrtc
diff --git a/pc/payload_type_picker.h b/pc/payload_type_picker.h
new file mode 100644
index 0000000..7cbc5ba
--- /dev/null
+++ b/pc/payload_type_picker.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 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 PC_PAYLOAD_TYPE_PICKER_H_
+#define PC_PAYLOAD_TYPE_PICKER_H_
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "api/rtc_error.h"
+#include "media/base/codec.h"
+#include "rtc_base/strong_alias.h"
+
+namespace webrtc {
+
+class PayloadType : public StrongAlias<class PayloadTypeTag, uint8_t> {
+ public:
+ // Non-explicit conversions from and to ints are to be deprecated and
+ // removed once calling code is upgraded.
+ PayloadType(uint8_t pt) { value_ = pt; } // NOLINT: explicit
+ constexpr operator uint8_t() const& { return value_; } // NOLINT: Explicit
+};
+
+class PayloadTypePicker {
+ public:
+ RTCErrorOr<PayloadType> SuggestMapping(cricket::Codec codec);
+ RTCError AddMapping(PayloadType payload_type, cricket::Codec codec);
+};
+
+class PayloadTypeRecorder {
+ public:
+ explicit PayloadTypeRecorder(PayloadTypePicker& suggester)
+ : suggester_(suggester) {}
+
+ RTCError AddMapping(PayloadType payload_type, cricket::Codec codec);
+ std::vector<std::pair<PayloadType, cricket::Codec>> GetMappings();
+ RTCErrorOr<PayloadType> LookupPayloadType(cricket::Codec codec);
+ RTCErrorOr<cricket::Codec> LookupCodec(PayloadType payload_type);
+ // Transaction support.
+ // Checkpoint() commits previous changes.
+ void Checkpoint();
+ // Rollback() rolls back to the previous checkpoint.
+ void Rollback();
+
+ private:
+ PayloadTypePicker& suggester_;
+ std::map<PayloadType, cricket::Codec> payload_type_to_codec_;
+ std::map<PayloadType, cricket::Codec> checkpoint_payload_type_to_codec_;
+};
+
+} // namespace webrtc
+
+#endif // PC_PAYLOAD_TYPE_PICKER_H_
diff --git a/pc/payload_type_picker_unittest.cc b/pc/payload_type_picker_unittest.cc
new file mode 100644
index 0000000..0e486bf
--- /dev/null
+++ b/pc/payload_type_picker_unittest.cc
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2024 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 "pc/payload_type_picker.h"
+
+#include "media/base/codec.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(PayloadTypePicker, PayloadTypeAssignmentWorks) {
+ // Note: This behavior is due to be deprecated and removed.
+ PayloadType pt_a(1);
+ PayloadType pt_b = 1; // Implicit conversion
+ EXPECT_EQ(pt_a, pt_b);
+ int pt_as_int = pt_a; // Implicit conversion
+ EXPECT_EQ(1, pt_as_int);
+}
+
+TEST(PayloadTypePicker, InstantiateTypes) {
+ PayloadTypePicker picker;
+ PayloadTypeRecorder recorder(picker);
+}
+
+TEST(PayloadTypePicker, StoreAndRecall) {
+ PayloadTypePicker picker;
+ PayloadTypeRecorder recorder(picker);
+ const PayloadType a_payload_type(123);
+ const PayloadType not_a_payload_type(44);
+ cricket::Codec a_codec = cricket::CreateVideoCodec(0, "vp8");
+ auto error = recorder.AddMapping(a_payload_type, a_codec);
+ ASSERT_TRUE(error.ok());
+ auto result = recorder.LookupCodec(a_payload_type);
+ ASSERT_TRUE(result.ok());
+ EXPECT_EQ(result.value(), a_codec);
+ auto result_pt = recorder.LookupPayloadType(a_codec);
+ ASSERT_TRUE(result_pt.ok());
+ EXPECT_EQ(result_pt.value(), a_payload_type);
+ EXPECT_FALSE(recorder.LookupCodec(not_a_payload_type).ok());
+}
+
+TEST(PayloadTypePicker, RollbackAndCommit) {
+ PayloadTypePicker picker;
+ PayloadTypeRecorder recorder(picker);
+ const PayloadType a_payload_type(123);
+ const PayloadType b_payload_type(124);
+ const PayloadType not_a_payload_type(44);
+
+ cricket::Codec a_codec = cricket::CreateVideoCodec(0, "vp8");
+
+ cricket::Codec b_codec = cricket::CreateVideoCodec(0, "vp9");
+ auto error = recorder.AddMapping(a_payload_type, a_codec);
+ ASSERT_TRUE(error.ok());
+ recorder.Checkpoint();
+ ASSERT_TRUE(recorder.AddMapping(b_payload_type, b_codec).ok());
+ {
+ auto result = recorder.LookupCodec(a_payload_type);
+ ASSERT_TRUE(result.ok());
+ EXPECT_EQ(result.value(), a_codec);
+ }
+ {
+ auto result = recorder.LookupCodec(b_payload_type);
+ ASSERT_TRUE(result.ok());
+ EXPECT_EQ(result.value(), b_codec);
+ }
+ recorder.Rollback();
+ {
+ auto result = recorder.LookupCodec(a_payload_type);
+ ASSERT_TRUE(result.ok());
+ EXPECT_EQ(result.value(), a_codec);
+ }
+ {
+ auto result = recorder.LookupCodec(b_payload_type);
+ ASSERT_FALSE(result.ok());
+ }
+ ASSERT_TRUE(recorder.AddMapping(b_payload_type, b_codec).ok());
+ // Rollback after a new checkpoint has no effect.
+ recorder.Checkpoint();
+ recorder.Rollback();
+ {
+ auto result = recorder.LookupCodec(b_payload_type);
+ ASSERT_TRUE(result.ok());
+ EXPECT_EQ(result.value(), b_codec);
+ }
+}
+
+} // namespace webrtc