Use the PayloadType class rather than uint8_t in Codec class

Also replace "int" with "PayloadType" in a number of interfaces, and
make appropriate adjustments.

This is one step towards controlling the issuing and consumption
of PayloadType values.

Bug: webrtc:360058654
Change-Id: I146bc4e3e9966b98864277eee6f52b75ba10d89a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/460021
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47286}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index 5e0e0f4..3555ec0 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -340,6 +340,7 @@
   ]
   deps = [
     ":candidate",
+    ":payload_type",
     ":ref_count",
     ":rtc_error",
     ":rtp_parameters",
diff --git a/api/payload_type.h b/api/payload_type.h
index 9d4a443..dc40b1f 100644
--- a/api/payload_type.h
+++ b/api/payload_type.h
@@ -11,31 +11,64 @@
 #ifndef API_PAYLOAD_TYPE_H_
 #define API_PAYLOAD_TYPE_H_
 
-#include <cstdint>
+#include <optional>
 
 #include "absl/strings/str_format.h"
 #include "rtc_base/strong_alias.h"
 
 namespace webrtc {
 
-class PayloadType : public StrongAlias<class PayloadTypeTag, uint8_t> {
+class PayloadType : public StrongAlias<class PayloadTypeTag, int> {
  public:
+  // The default constructor makes a NotSet.
+  PayloadType() : StrongAlias(-1) {}
   // 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
-  static bool IsValid(PayloadType id, bool rtcp_mux) {
+  constexpr PayloadType(int pt) : StrongAlias(pt) {  // NOLINT: explicit
+    // The number of tests that use invalid values is high enough that
+    // this DCHECK can't be deployed yet.
+    // Also, allow -1 as argument as a temporary measure. Those calls should
+    // eventually be replaced with PayloadType::NotSet() values.
+    // Intended check:
+    // RTC_DCHECK(pt >= -1 && pt <= 127) << "Payload type " << pt << " is
+    // invalid";
+  }
+
+  constexpr operator int() const& { return value(); }  // NOLINT: explicit
+
+  // Factory function to create a value if you need to check for
+  // values in the valid range.
+  static std::optional<PayloadType> Create(int pt) {
+    if (pt < 0 || pt > 127) {
+      return std::nullopt;
+    }
+    return PayloadType(pt);
+  }
+  // Factory function for the NotSet value. This should be the only way
+  // to create a value outside the valid range.
+  static constexpr PayloadType NotSet() { return PayloadType(Internal{}, -1); }
+  bool Valid(bool rtcp_mux = false) {
     // A payload type is a 7-bit value in the RTP header, so max = 127.
     // If RTCP multiplexing is used, the numbers from 64 to 95 are reserved
     // for RTCP packets.
-    if (rtcp_mux && (id > 63 && id < 96)) {
+    if (rtcp_mux && (value() > 63 && value() < 96)) {
       return false;
     }
-    return id >= 0 && id <= 127;
+    return value() >= 0 && value() <= 127;
   }
+  // Older interface to validity check.
+  static bool IsValid(PayloadType id, bool rtcp_mux) {
+    return id.Valid(rtcp_mux);
+  }
+  bool IsSet() { return value() >= 0; }
+
+ private:
+  class Internal {};
+  // Allow -1 for "NotSet"
+  explicit constexpr PayloadType(Internal tag, int pt) : StrongAlias(pt) {}
   template <typename Sink>
   friend void AbslStringify(Sink& sink, const PayloadType pt) {
-    absl::Format(&sink, "%d", pt.value_);
+    absl::Format(&sink, "%d", pt.value());
   }
 };
 
diff --git a/api/webrtc_sdp.cc b/api/webrtc_sdp.cc
index aab9251..67eee80 100644
--- a/api/webrtc_sdp.cc
+++ b/api/webrtc_sdp.cc
@@ -33,6 +33,7 @@
 #include "api/candidate.h"
 #include "api/jsep.h"
 #include "api/media_types.h"
+#include "api/payload_type.h"
 #include "api/rtc_error.h"
 #include "api/rtp_parameters.h"
 #include "api/rtp_transceiver_direction.h"
@@ -192,7 +193,7 @@
 
 // RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
 // types.
-const int kWildcardPayloadType = -1;
+constexpr PayloadType kWildcardPayloadType = PayloadType::NotSet();
 
 // Check if passed character is a "token-char" from RFC 4566.
 // https://datatracker.ietf.org/doc/html/rfc4566#section-9
@@ -1013,7 +1014,7 @@
   return true;
 }
 
-void WriteFmtpHeader(int payload_type, StringBuilder* os) {
+void WriteFmtpHeader(PayloadType payload_type, StringBuilder* os) {
   // fmtp header: a=fmtp:`payload_type` <parameters>
   // Add a=fmtp
   InitAttrLine(kAttributeFmtp, os);
@@ -1021,7 +1022,7 @@
   *os << kSdpDelimiterColon << payload_type;
 }
 
-void WritePacketizationHeader(int payload_type, StringBuilder* os) {
+void WritePacketizationHeader(PayloadType payload_type, StringBuilder* os) {
   // packetization header: a=packetization:`payload_type` <packetization_format>
   // Add a=packetization
   InitAttrLine(kAttributePacketization, os);
@@ -1029,7 +1030,7 @@
   *os << kSdpDelimiterColon << payload_type;
 }
 
-void WriteRtcpFbHeader(int payload_type, StringBuilder* os) {
+void WriteRtcpFbHeader(PayloadType payload_type, StringBuilder* os) {
   // rtcp-fb header: a=rtcp-fb:`payload_type`
   // <parameters>/<ccm <ccm_parameters>>
   // Add a=rtcp-fb
@@ -2035,7 +2036,7 @@
 // with that payload type.
 Codec GetCodecWithPayloadType(MediaType type,
                               const std::vector<Codec>& codecs,
-                              int payload_type) {
+                              PayloadType payload_type) {
   const Codec* codec = FindCodecById(codecs, payload_type);
   if (codec)
     return *codec;
@@ -2050,7 +2051,7 @@
 // Adds or updates existing codec corresponding to `payload_type` according
 // to `parameters`.
 void UpdateCodec(MediaContentDescription* content_desc,
-                 int payload_type,
+                 PayloadType payload_type,
                  const CodecParameterMap& parameters) {
   // Codec might already have been populated (from rtpmap).
   Codec new_codec = GetCodecWithPayloadType(
@@ -2062,7 +2063,7 @@
 // Adds or updates existing codec corresponding to `payload_type` according
 // to `feedback_param`.
 void UpdateCodec(MediaContentDescription* content_desc,
-                 int payload_type,
+                 PayloadType payload_type,
                  const FeedbackParam& feedback_param) {
   // Codec might already have been populated (from rtpmap).
   Codec new_codec = GetCodecWithPayloadType(
@@ -2098,11 +2099,12 @@
     return false;
   }
 
-  int payload_type = 0;
-  if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
+  int pt_int = 0;
+  if (!GetPayloadTypeFromString(line_payload, payload_type_str, &pt_int,
                                 error)) {
     return false;
   }
+  PayloadType payload_type = PayloadType(pt_int);
 
   // Parse out format specific parameters.
   CodecParameterMap codec_params;
@@ -2224,7 +2226,7 @@
 
 // Updates or creates a new codec entry in the audio description with according
 // to `name`, `clockrate`, `bitrate`, and `channels`.
-void UpdateCodec(int payload_type,
+void UpdateCodec(PayloadType payload_type,
                  absl::string_view name,
                  int clockrate,
                  int bitrate,
@@ -2243,7 +2245,7 @@
 
 // Updates or creates a new codec entry in the video description according to
 // `name`, `width`, `height`, and `framerate`.
-void UpdateCodec(int payload_type,
+void UpdateCodec(PayloadType payload_type,
                  absl::string_view name,
                  MediaContentDescription* desc) {
   // Codec may already be populated with (only) optional parameters
@@ -2358,7 +2360,7 @@
 // Adds or updates existing video codec corresponding to `payload_type`
 // according to `packetization`.
 void UpdateVideoCodecPacketization(MediaContentDescription* desc,
-                                   int payload_type,
+                                   PayloadType payload_type,
                                    absl::string_view packetization) {
   if (packetization != kPacketizationParamRaw) {
     // Ignore unsupported packetization attribute.
@@ -2389,11 +2391,11 @@
                 &payload_type_string, error)) {
     return false;
   }
-  int payload_type;
-  if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
-                                error)) {
+  int pt_int;
+  if (!GetPayloadTypeFromString(line, payload_type_string, &pt_int, error)) {
     return false;
   }
+  PayloadType payload_type = PayloadType(pt_int);
   absl::string_view packetization = packetization_fields[1];
   UpdateVideoCodecPacketization(media_desc, payload_type, packetization);
   return true;
@@ -2416,12 +2418,13 @@
                 error)) {
     return false;
   }
-  int payload_type = kWildcardPayloadType;
+  PayloadType payload_type = kWildcardPayloadType;
   if (payload_type_string != "*") {
-    if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
-                                  error)) {
+    int pt_int;
+    if (!GetPayloadTypeFromString(line, payload_type_string, &pt_int, error)) {
       return false;
     }
+    payload_type = PayloadType(pt_int);
   }
   absl::string_view id = rtcp_fb_fields[1];
   std::string param = "";
diff --git a/call/BUILD.gn b/call/BUILD.gn
index 232c66a..b116163 100644
--- a/call/BUILD.gn
+++ b/call/BUILD.gn
@@ -530,6 +530,7 @@
         "../api:mock_audio_mixer",
         "../api:mock_frame_transformer",
         "../api:mock_video_codec_factory",
+        "../api:payload_type",
         "../api:rtp_headers",
         "../api:rtp_parameters",
         "../api:scoped_refptr",
diff --git a/call/payload_type_picker_unittest.cc b/call/payload_type_picker_unittest.cc
index 46e0de7..562bb2c 100644
--- a/call/payload_type_picker_unittest.cc
+++ b/call/payload_type_picker_unittest.cc
@@ -13,6 +13,7 @@
 #include <string>
 
 #include "absl/strings/str_cat.h"
+#include "api/payload_type.h"
 #include "api/video_codecs/sdp_video_format.h"
 #include "call/payload_type.h"
 #include "media/base/codec.h"
@@ -96,7 +97,6 @@
   PayloadTypeRecorder recorder(picker);
   const PayloadType a_payload_type(123);
   const PayloadType b_payload_type(124);
-  const PayloadType not_a_payload_type(44);
 
   Codec a_codec = CreateVideoCodec(0, "vp8");
 
diff --git a/media/BUILD.gn b/media/BUILD.gn
index e292762..15c2cf9 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -356,6 +356,7 @@
     "../rtc_base:stringutils",
     "../rtc_base/network:sent_packet",
     "../video/config:encoder_config",
+    "//third_party/abseil-cpp/absl/container:flat_hash_map",
     "//third_party/abseil-cpp/absl/functional:any_invocable",
     "//third_party/abseil-cpp/absl/strings",
     "//third_party/abseil-cpp/absl/strings:string_view",
@@ -374,6 +375,7 @@
   ]
   deps = [
     ":media_constants",
+    "../api:payload_type",
     "../api:rtp_parameters",
     "../api/audio_codecs:audio_codecs_api",
     "../api/video_codecs:scalability_mode",
@@ -400,6 +402,7 @@
   deps = [
     ":codec",
     ":media_constants",
+    "../api:payload_type",
     "../api:rtc_error",
     "../rtc_base:checks",
     "../rtc_base:logging",
@@ -970,6 +973,7 @@
         "../api:mock_video_codec_factory",
         "../api:mock_video_decoder",
         "../api:mock_video_encoder",
+        "../api:payload_type",
         "../api:priority",
         "../api:ref_count",
         "../api:rtc_error",
diff --git a/media/base/codec.cc b/media/base/codec.cc
index 8379f99..535fcd8 100644
--- a/media/base/codec.cc
+++ b/media/base/codec.cc
@@ -22,6 +22,7 @@
 #include "absl/strings/str_cat.h"
 #include "api/audio_codecs/audio_format.h"
 #include "api/media_types.h"
+#include "api/payload_type.h"
 #include "api/rtp_parameters.h"
 #include "api/video_codecs/h264_profile_level_id.h"
 #include "api/video_codecs/sdp_video_format.h"
@@ -97,10 +98,10 @@
   return false;
 }
 
-Codec::Codec(Type type, int id, const std::string& name, int clockrate)
+Codec::Codec(Type type, PayloadType id, const std::string& name, int clockrate)
     : Codec(type, id, name, clockrate, 0) {}
 Codec::Codec(Type type,
-             int id,
+             PayloadType id,
              const std::string& name,
              int clockrate,
              size_t channels)
@@ -115,18 +116,22 @@
 
 Codec::Codec(Type type)
     : Codec(type,
-            kIdNotSet,
+            PayloadType::NotSet(),
             "",
             type == Type::kVideo ? kDefaultVideoClockRateHz
                                  : kDefaultAudioClockRateHz) {}
 
 Codec::Codec(const SdpAudioFormat& c)
-    : Codec(Type::kAudio, kIdNotSet, c.name, c.clockrate_hz, c.num_channels) {
+    : Codec(Type::kAudio,
+            PayloadType::NotSet(),
+            c.name,
+            c.clockrate_hz,
+            c.num_channels) {
   params = c.parameters;
 }
 
 Codec::Codec(const SdpVideoFormat& c)
-    : Codec(Type::kVideo, kIdNotSet, c.name, kVideoCodecClockrate) {
+    : Codec(Type::kVideo, PayloadType::NotSet(), c.name, kVideoCodecClockrate) {
   params = c.parameters;
   scalability_modes = c.scalability_modes;
 }
@@ -290,20 +295,25 @@
   return sb.str();
 }
 
-Codec CreateAudioRtxCodec(int rtx_payload_type, int associated_payload_type) {
+Codec CreateAudioRtxCodec(PayloadType rtx_payload_type,
+                          PayloadType associated_payload_type) {
   Codec rtx_codec = CreateAudioCodec(rtx_payload_type, kRtxCodecName,
                                      kDefaultAudioClockRateHz, 1);
-  rtx_codec.SetParam(kCodecParamAssociatedPayloadType, associated_payload_type);
+  rtx_codec.SetParam(kCodecParamAssociatedPayloadType,
+                     associated_payload_type.value());
   return rtx_codec;
 }
 
-Codec CreateVideoRtxCodec(int rtx_payload_type, int associated_payload_type) {
+Codec CreateVideoRtxCodec(PayloadType rtx_payload_type,
+                          PayloadType associated_payload_type) {
   Codec rtx_codec = CreateVideoCodec(rtx_payload_type, kRtxCodecName);
-  rtx_codec.SetParam(kCodecParamAssociatedPayloadType, associated_payload_type);
+  rtx_codec.SetParam(kCodecParamAssociatedPayloadType,
+                     associated_payload_type.value());
   return rtx_codec;
 }
 
-const Codec* FindCodecById(const std::vector<Codec>& codecs, int payload_type) {
+const Codec* FindCodecById(const std::vector<Codec>& codecs,
+                           PayloadType payload_type) {
   for (const auto& codec : codecs) {
     if (codec.id == payload_type)
       return &codec;
@@ -395,7 +405,7 @@
   }
 }
 
-Codec CreateAudioCodec(int id,
+Codec CreateAudioCodec(PayloadType id,
                        const std::string& name,
                        int clockrate,
                        size_t channels) {
@@ -407,10 +417,10 @@
 }
 
 Codec CreateVideoCodec(const std::string& name) {
-  return CreateVideoCodec(Codec::kIdNotSet, name);
+  return CreateVideoCodec(PayloadType::NotSet(), name);
 }
 
-Codec CreateVideoCodec(int id, const std::string& name) {
+Codec CreateVideoCodec(PayloadType id, const std::string& name) {
   Codec c(Codec::Type::kVideo, id, name, kVideoCodecClockrate);
   if (absl::EqualsIgnoreCase(kH264CodecName, name)) {
     // This default is set for all H.264 codecs created because
@@ -426,7 +436,7 @@
   return Codec(c);
 }
 
-Codec CreateVideoCodec(int id, const SdpVideoFormat& sdp) {
+Codec CreateVideoCodec(PayloadType id, const SdpVideoFormat& sdp) {
   Codec c = CreateVideoCodec(sdp);
   c.id = id;
   return c;
diff --git a/media/base/codec.h b/media/base/codec.h
index bc970c5..5f19744 100644
--- a/media/base/codec.h
+++ b/media/base/codec.h
@@ -20,6 +20,7 @@
 #include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
 #include "api/audio_codecs/audio_format.h"
+#include "api/payload_type.h"
 #include "api/rtp_parameters.h"
 #include "api/video_codecs/scalability_mode.h"
 #include "api/video_codecs/sdp_video_format.h"
@@ -87,7 +88,7 @@
   static const int kIdNotSet = -1;
 
   Type type;
-  int id;
+  PayloadType id;
   std::string name;
   int clockrate;
 
@@ -173,7 +174,11 @@
   std::string ToString() const;
 
   // Default constructor, for initialization.
-  Codec() : Codec(Type::kAudio, kIdNotSet, "", kDefaultAudioClockRateHz) {}
+  Codec()
+      : Codec(Type::kAudio,
+              PayloadType::NotSet(),
+              "",
+              kDefaultAudioClockRateHz) {}
   Codec& operator=(const Codec& c);
   Codec& operator=(Codec&& c);
 
@@ -183,7 +188,7 @@
 
   template <typename Sink>
   friend void AbslStringify(Sink& sink, const Codec& c) {
-    absl::Format(&sink, "[%d:", c.id);
+    absl::Format(&sink, "[%v:", c.id);
     switch (c.type) {
       case Codec::Type::kAudio:
         sink.Append("audio/");
@@ -208,9 +213,9 @@
   // Creates an empty codec.
   explicit Codec(Type type);
   // Creates a codec with the given parameters.
-  Codec(Type type, int id, const std::string& name, int clockrate);
+  Codec(Type type, PayloadType id, const std::string& name, int clockrate);
   Codec(Type type,
-        int id,
+        PayloadType id,
         const std::string& name,
         int clockrate,
         size_t channels);
@@ -218,36 +223,38 @@
   explicit Codec(const SdpAudioFormat& c);
   explicit Codec(const SdpVideoFormat& c);
 
-  friend Codec CreateAudioCodec(int id,
+  friend Codec CreateAudioCodec(PayloadType id,
                                 const std::string& name,
                                 int clockrate,
                                 size_t channels);
   friend Codec CreateAudioCodec(const SdpAudioFormat& c);
-  friend Codec CreateAudioRtxCodec(int rtx_payload_type,
-                                   int associated_payload_type);
-  friend Codec CreateVideoCodec(int id, const std::string& name);
+  friend Codec CreateAudioRtxCodec(PayloadType rtx_payload_type,
+                                   PayloadType associated_payload_type);
+  friend Codec CreateVideoCodec(PayloadType id, const std::string& name);
   friend Codec CreateVideoCodec(const SdpVideoFormat& c);
-  friend Codec CreateVideoRtxCodec(int rtx_payload_type,
-                                   int associated_payload_type);
+  friend Codec CreateVideoCodec(PayloadType id, const SdpVideoFormat& sdp);
 };
 
 using Codecs = std::vector<Codec>;
 
-Codec CreateAudioCodec(int id,
+Codec CreateAudioCodec(PayloadType id,
                        const std::string& name,
                        int clockrate,
                        size_t channels);
 Codec CreateAudioCodec(const SdpAudioFormat& c);
-Codec CreateAudioRtxCodec(int rtx_payload_type, int associated_payload_type);
+Codec CreateAudioRtxCodec(PayloadType rtx_payload_type,
+                          PayloadType associated_payload_type);
 Codec CreateVideoCodec(const std::string& name);
-Codec CreateVideoCodec(int id, const std::string& name);
+Codec CreateVideoCodec(PayloadType id, const std::string& name);
 Codec CreateVideoCodec(const SdpVideoFormat& c);
-Codec CreateVideoCodec(int id, const SdpVideoFormat& sdp);
-Codec CreateVideoRtxCodec(int rtx_payload_type, int associated_payload_type);
+Codec CreateVideoCodec(PayloadType id, const SdpVideoFormat& sdp);
+Codec CreateVideoRtxCodec(PayloadType rtx_payload_type,
+                          PayloadType associated_payload_type);
 
 // Get the codec setting associated with `payload_type`. If there
 // is no codec associated with that payload type it returns nullptr.
-const Codec* FindCodecById(const std::vector<Codec>& codecs, int payload_type);
+const Codec* FindCodecById(const std::vector<Codec>& codecs,
+                           PayloadType payload_type);
 
 bool HasLntf(const Codec& codec);
 bool HasNack(const Codec& codec);
diff --git a/media/base/codec_comparators.cc b/media/base/codec_comparators.cc
index 942e3a2..3b4b198 100644
--- a/media/base/codec_comparators.cc
+++ b/media/base/codec_comparators.cc
@@ -11,6 +11,7 @@
 
 #include <algorithm>
 #include <cstddef>
+#include <cstdint>
 #include <optional>
 #include <string>
 #include <vector>
@@ -20,6 +21,7 @@
 #include "absl/strings/match.h"
 #include "absl/strings/string_view.h"
 #include "api/media_types.h"
+#include "api/payload_type.h"
 #include "api/rtp_parameters.h"
 #include "api/video_codecs/av1_profile.h"
 #include "api/video_codecs/h264_profile_level_id.h"
@@ -119,9 +121,9 @@
 }
 
 bool ReferencedCodecsMatch(const std::vector<Codec>& codecs1,
-                           const int codec1_id,
+                           const PayloadType codec1_id,
                            const std::vector<Codec>& codecs2,
-                           const int codec2_id) {
+                           const PayloadType codec2_id) {
   const Codec* codec1 = FindCodecById(codecs1, codec1_id);
   const Codec* codec2 = FindCodecById(codecs2, codec2_id);
   return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
@@ -130,21 +132,25 @@
 bool MatchesWithReferenceAttributesAndComparator(
     const Codec& codec_to_match,
     const Codec& potential_match,
-    absl::AnyInvocable<bool(int, int)> reference_comparator) {
+    absl::AnyInvocable<bool(PayloadType, PayloadType)> reference_comparator) {
   if (!MatchesWithCodecRules(codec_to_match, potential_match)) {
     return false;
   }
   Codec::ResiliencyType resiliency_type = codec_to_match.GetResiliencyType();
   if (resiliency_type == Codec::ResiliencyType::kRtx) {
-    int apt_value_1 = 0;
-    int apt_value_2 = 0;
+    int apt_value_1_int = 0;
+    int apt_value_2_int = 0;
     if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
-                                 &apt_value_1) ||
+                                 &apt_value_1_int) ||
         !potential_match.GetParam(kCodecParamAssociatedPayloadType,
-                                  &apt_value_2)) {
+                                  &apt_value_2_int)) {
       RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
       return false;
     }
+    PayloadType apt_value_1 =
+        PayloadType(static_cast<uint8_t>(apt_value_1_int));
+    PayloadType apt_value_2 =
+        PayloadType(static_cast<uint8_t>(apt_value_2_int));
     if (reference_comparator(apt_value_1, apt_value_2)) {
       return true;
     }
@@ -196,8 +202,8 @@
     // codecs has an unassigned payload type or they have the same ID.
     if (codec_to_match.type == Codec::Type::kAudio &&
         codec_to_match.name == kRedCodecName &&
-        (codec_to_match.id == Codec::kIdNotSet ||
-         potential_match.id == Codec::kIdNotSet ||
+        (codec_to_match.id == PayloadType::NotSet() ||
+         potential_match.id == PayloadType::NotSet() ||
          codec_to_match.id == potential_match.id)) {
       return true;
     }
@@ -297,7 +303,8 @@
 
   bool matches_id;
   if ((is_id_in_dynamic_range && is_codec_id_in_dynamic_range) ||
-      left_codec.id == Codec::kIdNotSet || right_codec.id == Codec::kIdNotSet) {
+      left_codec.id == PayloadType::NotSet() ||
+      right_codec.id == PayloadType::NotSet()) {
     matches_id = absl::EqualsIgnoreCase(left_codec.name, right_codec.name);
   } else {
     matches_id = (left_codec.id == right_codec.id);
@@ -330,7 +337,7 @@
 
 bool MatchesWithReferenceAttributes(const Codec& codec1, const Codec& codec2) {
   return MatchesWithReferenceAttributesAndComparator(
-      codec1, codec2, [](int a, int b) { return a == b; });
+      codec1, codec2, [](PayloadType a, PayloadType b) { return a == b; });
 }
 
 // Finds a codec in `codecs2` that matches `codec_to_match`, which is
@@ -348,7 +355,7 @@
   for (const Codec& potential_match : codecs2) {
     if (MatchesWithReferenceAttributesAndComparator(
             codec_to_match, potential_match,
-            [&codecs1, &codecs2](int a, int b) {
+            [&codecs1, &codecs2](PayloadType a, PayloadType b) {
               return ReferencedCodecsMatch(codecs1, a, codecs2, b);
             })) {
       return potential_match;
diff --git a/media/base/codec_list.cc b/media/base/codec_list.cc
index 4be60c4..a293244 100644
--- a/media/base/codec_list.cc
+++ b/media/base/codec_list.cc
@@ -14,6 +14,7 @@
 #include <map>
 #include <vector>
 
+#include "api/payload_type.h"
 #include "api/rtc_error.h"
 #include "media/base/codec.h"
 #include "media/base/media_constants.h"
@@ -32,8 +33,8 @@
   // that there are no duplicates.
   for (size_t i = 0; i < codecs.size(); i++) {
     const Codec& codec = codecs[i];
-    if (codec.id != Codec::kIdNotSet) {
-      auto [it, success] = pt_to_index.insert({codec.id, i});
+    if (codec.id != PayloadType::NotSet()) {
+      auto [it, success] = pt_to_index.insert({codec.id, static_cast<int>(i)});
       if (!success) {
         RTC_LOG(LS_ERROR) << "Duplicate payload type in codec list, " << codec
                           << " and " << codecs[it->second]
@@ -56,7 +57,7 @@
         // TODO: https://issues.webrtc.org/384756622 - reject codec earlier and
         // enable check. RTC_DCHECK(apt_it != codec.params.end()); Until that is
         // fixed:
-        if (codec.id == Codec::kIdNotSet) {
+        if (codec.id == PayloadType::NotSet()) {
           // Should not have an apt parameter.
           if (apt_it != codec.params.end()) {
             RTC_LOG(LS_WARNING) << "Surprising condition: RTX codec without "
@@ -77,7 +78,7 @@
           return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER)
                            << "Non-numeric argument to rtx apt parameter");
         }
-        if (codec.id != Codec::kIdNotSet &&
+        if (codec.id != PayloadType::NotSet() &&
             pt_to_index.count(associated_pt) != 1) {
           RTC_LOG(LS_WARNING)
               << "Surprising condition: RTX codec APT not found: " << codec
diff --git a/media/base/media_channel.h b/media/base/media_channel.h
index cdc1928..7075694 100644
--- a/media/base/media_channel.h
+++ b/media/base/media_channel.h
@@ -22,6 +22,7 @@
 #include <utility>
 #include <vector>
 
+#include "absl/container/flat_hash_map.h"
 #include "absl/functional/any_invocable.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
@@ -705,7 +706,8 @@
 };
 
 // Maps from payload type to `RtpCodecParameters`.
-typedef std::map<int, RtpCodecParameters> RtpCodecParametersMap;
+typedef absl::flat_hash_map<decltype(Codec::id), RtpCodecParameters>
+    RtpCodecParametersMap;
 
 // Stats returned from VoiceMediaSendChannel.GetStats()
 struct VoiceMediaSendInfo {
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index 91f2c9b..6b3e0b5 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -1715,13 +1715,13 @@
   // we can omit the codec information for those here and only insert the
   // primary codec that is being used to send here.
   video_media_info->send_codecs.insert(std::make_pair(
-      send_codec()->codec.id, send_codec()->codec.ToCodecParameters()));
+      send_codec()->codec.id.value(), send_codec()->codec.ToCodecParameters()));
 
   for (const auto& it : send_codecs_) {
     auto codec_param_it = video_media_info->send_codecs.find(it.codec.id);
     if (codec_param_it == video_media_info->send_codecs.end()) {
       video_media_info->send_codecs.insert(
-          std::make_pair(it.codec.id, it.codec.ToCodecParameters()));
+          std::make_pair(it.codec.id.value(), it.codec.ToCodecParameters()));
     }
   }
 }
@@ -2472,7 +2472,8 @@
   VideoSenderInfo common_info;
   if (parameters_.codec_settings) {
     common_info.codec_name = parameters_.codec_settings->codec.name;
-    common_info.codec_payload_type = parameters_.codec_settings->codec.id;
+    common_info.codec_payload_type =
+        parameters_.codec_settings->codec.id.value();
   }
   // If SVC is used, one stream is configured but multiple encodings exist. This
   // is not spec-compliant, but it is how we've implemented SVC so this affects
@@ -3223,7 +3224,7 @@
                  *receiver.codec_payload_type == c.id;
         });
     if (codec != recv_params_.codecs.end()) {
-      video_media_info->receive_codecs.insert(
+      video_media_info->receive_codecs.emplace(
           std::make_pair(codec->id, codec->ToCodecParameters()));
     }
   }
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc
index 3685544..c9de2e5 100644
--- a/media/engine/webrtc_video_engine_unittest.cc
+++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -36,6 +36,7 @@
 #include "api/field_trials.h"
 #include "api/make_ref_counted.h"
 #include "api/media_types.h"
+#include "api/payload_type.h"
 #include "api/priority.h"
 #include "api/rtc_error.h"
 #include "api/rtp_headers.h"
@@ -5626,7 +5627,7 @@
   VideoReceiverParameters parameters;
   parameters.codecs.push_back(GetEngineCodec("VP8"));
   parameters.codecs.push_back(GetEngineCodec("VP8"));
-  parameters.codecs[1].id += 1;
+  parameters.codecs[1].id = PayloadType(parameters.codecs[1].id + 1);
   EXPECT_TRUE(receive_channel_->SetReceiverParameters(parameters));
 }
 
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index c8bda98..a3c8c98 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -50,6 +50,7 @@
 #include "api/frame_transformer_interface.h"
 #include "api/make_ref_counted.h"
 #include "api/media_types.h"
+#include "api/payload_type.h"
 #include "api/priority.h"
 #include "api/rtc_error.h"
 #include "api/rtp_headers.h"
@@ -1431,7 +1432,7 @@
     const std::vector<Codec>& codecs,
     std::optional<Codec> preferred_codec) {
   RTC_DCHECK_RUN_ON(worker_thread_);
-  dtmf_payload_type_ = std::nullopt;
+  dtmf_payload_type_ = PayloadType::NotSet();
   dtmf_payload_freq_ = -1;
 
   // Validate supplied codecs list.
@@ -1453,7 +1454,8 @@
   for (const Codec& codec : codecs) {
     if (IsCodec(codec, kDtmfCodecName)) {
       dtmf_codecs.push_back(codec);
-      if (!dtmf_payload_type_ || codec.clockrate < dtmf_payload_freq_) {
+      if ((dtmf_payload_type_ == PayloadType::NotSet()) ||
+          codec.clockrate < dtmf_payload_freq_) {
         dtmf_payload_type_ = codec.id;
         dtmf_payload_freq_ = codec.clockrate;
       }
@@ -1513,7 +1515,7 @@
           RTC_LOG(LS_WARNING)
               << "CN frequency " << cn_codec.clockrate << " not supported.";
         } else {
-          send_codec_spec->cng_payload_type = cn_codec.id;
+          send_codec_spec->cng_payload_type = cn_codec.id.value();
         }
         break;
       }
@@ -1537,7 +1539,7 @@
     const Codec& red_codec = codecs[i];
     if (IsCodec(red_codec, kRedCodecName) &&
         CheckRedParameters(red_codec, *send_codec_spec)) {
-      send_codec_spec->red_payload_type = red_codec.id;
+      send_codec_spec->red_payload_type = red_codec.id.value();
       break;
     }
   }
@@ -1698,7 +1700,7 @@
 
 bool WebRtcVoiceSendChannel::CanInsertDtmf() {
   RTC_DCHECK_RUN_ON(worker_thread_);
-  return dtmf_payload_type_.has_value() && send_;
+  return (dtmf_payload_type_ != PayloadType::NotSet()) && send_;
 }
 
 void WebRtcVoiceSendChannel::SetFrameEncryptor(
@@ -1884,7 +1886,7 @@
     });
     if (codec != send_codecs_.end()) {
       voice_media_info->send_codecs.insert(
-          std::make_pair(codec->id, codec->ToCodecParameters()));
+          std::make_pair(codec->id.value(), codec->ToCodecParameters()));
     }
   }
 }
@@ -2772,7 +2774,7 @@
     });
     if (codec != recv_codecs_.end()) {
       voice_media_info->receive_codecs.insert(
-          std::make_pair(codec->id, codec->ToCodecParameters()));
+          std::make_pair(codec->id.value(), codec->ToCodecParameters()));
     }
   }
 }
diff --git a/media/engine/webrtc_voice_engine.h b/media/engine/webrtc_voice_engine.h
index 97907fd..c38a47f 100644
--- a/media/engine/webrtc_voice_engine.h
+++ b/media/engine/webrtc_voice_engine.h
@@ -39,6 +39,7 @@
 #include "api/field_trials_view.h"
 #include "api/frame_transformer_interface.h"
 #include "api/media_types.h"
+#include "api/payload_type.h"
 #include "api/rtc_error.h"
 #include "api/rtp_headers.h"
 #include "api/rtp_parameters.h"
@@ -280,7 +281,7 @@
 
   int max_send_bitrate_bps_ RTC_GUARDED_BY(worker_thread_) = 0;
   AudioOptions options_ RTC_GUARDED_BY(worker_thread_);
-  std::optional<int> dtmf_payload_type_ RTC_GUARDED_BY(worker_thread_);
+  PayloadType dtmf_payload_type_ RTC_GUARDED_BY(worker_thread_);
   int dtmf_payload_freq_ RTC_GUARDED_BY(worker_thread_) = -1;
   bool enable_non_sender_rtt_ RTC_GUARDED_BY(worker_thread_) = false;
   bool send_ RTC_GUARDED_BY(worker_thread_) = false;
diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc
index 8195403..526a6cb 100644
--- a/media/engine/webrtc_voice_engine_unittest.cc
+++ b/media/engine/webrtc_voice_engine_unittest.cc
@@ -38,6 +38,7 @@
 #include "api/field_trials.h"
 #include "api/make_ref_counted.h"
 #include "api/media_types.h"
+#include "api/payload_type.h"
 #include "api/priority.h"
 #include "api/ref_count.h"
 #include "api/rtc_error.h"
@@ -1110,7 +1111,7 @@
   parameters.codecs.push_back(kOpusCodec);
   EXPECT_TRUE(receive_channel_->SetReceiverParameters(parameters));
 
-  ++parameters.codecs[0].id;
+  parameters.codecs[0].id = PayloadType(parameters.codecs[0].id + 1);
   EXPECT_TRUE(receive_channel_->SetReceiverParameters(parameters));
 }
 
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index 1f19fdc..a903e35 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -105,6 +105,7 @@
     "//third_party/abseil-cpp/absl/algorithm:container",
     "//third_party/abseil-cpp/absl/cleanup",
     "//third_party/abseil-cpp/absl/functional:any_invocable",
+    "//third_party/abseil-cpp/absl/strings:str_format",
     "//third_party/abseil-cpp/absl/strings:string_view",
   ]
 }
@@ -1228,6 +1229,7 @@
     ":jsep_transport_collection",
     ":session_description",
     "../api:jsep",
+    "../api:payload_type",
     "../api:peer_connection_interface",
     "../api:rtc_error",
     "../call:payload_type",
@@ -1235,6 +1237,7 @@
     "../media:codec",
     "../rtc_base:checks",
     "../rtc_base:event_tracer",
+    "../rtc_base:logging",
     "../rtc_base:threading",
     "//third_party/abseil-cpp/absl/strings:string_view",
   ]
@@ -1390,6 +1393,7 @@
   deps = [
     ":session_description",
     ":simulcast_description",
+    "../api:payload_type",
     "../api:rtc_error",
     "../api:rtp_parameters",
     "../media:codec",
@@ -2268,6 +2272,7 @@
       "../api:make_ref_counted",
       "../api:media_stream_interface",
       "../api:mock_datagram_connection_observer",
+      "../api:payload_type",
       "../api:peer_connection_interface",
       "../api:priority",
       "../api:rtc_error",
@@ -2625,6 +2630,7 @@
       "../api:mock_encoder_selector",
       "../api:mock_packet_socket_factory",
       "../api:mock_video_track",
+      "../api:payload_type",
       "../api:peer_connection_interface",
       "../api:priority",
       "../api:ref_count",
diff --git a/pc/channel.cc b/pc/channel.cc
index 1774778..610f507 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -21,6 +21,7 @@
 #include "absl/algorithm/container.h"
 #include "absl/cleanup/cleanup.h"
 #include "absl/functional/any_invocable.h"
+#include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
 #include "api/crypto/crypto_options.h"
 #include "api/jsep.h"
@@ -156,9 +157,9 @@
       // Note: this writes into old_params
       codec.packetization = std::nullopt;
     } else if (!has_matching_packetization) {
-      std::string error_desc = StringFormat(
+      std::string error_desc = absl::StrFormat(
           "Failed to set local answer due to incompatible codec "
-          "packetization for pt='%d' specified.",
+          "packetization for pt='%v' specified.",
           codec.id);
       return RTCError(RTCErrorType::INTERNAL_ERROR, error_desc);
     }
diff --git a/pc/codec_vendor.cc b/pc/codec_vendor.cc
index 2adc3a6..cb9f65c 100644
--- a/pc/codec_vendor.cc
+++ b/pc/codec_vendor.cc
@@ -563,7 +563,7 @@
   RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
   int codec_payload_type = Codec::kIdNotSet;
   for (Codec& codec : codecs) {
-    if (codec.id == Codec::kIdNotSet) {
+    if (codec.id == PayloadType::NotSet()) {
       // Add payload types to codecs, if needed
       // This should only happen if WebRTC-PayloadTypesInTransport field trial
       // is enabled.
@@ -577,7 +577,7 @@
     // record first Opus codec id
     if (absl::EqualsIgnoreCase(codec.name, kOpusCodecName) &&
         codec_payload_type == Codec::kIdNotSet) {
-      codec_payload_type = codec.id;
+      codec_payload_type = codec.id.value();
     }
   }
   if (codec_payload_type != Codec::kIdNotSet) {
diff --git a/pc/codec_vendor_unittest.cc b/pc/codec_vendor_unittest.cc
index d8f88f6..76b4cbe 100644
--- a/pc/codec_vendor_unittest.cc
+++ b/pc/codec_vendor_unittest.cc
@@ -18,6 +18,7 @@
 #include "api/environment/environment_factory.h"
 #include "api/field_trials.h"
 #include "api/media_types.h"
+#include "api/payload_type.h"
 #include "api/rtc_error.h"
 #include "api/rtp_transceiver_direction.h"
 #include "api/test/rtc_error_matchers.h"
@@ -194,7 +195,7 @@
   MediaDescriptionOptions options(MediaType::VIDEO, "mid",
                                   RtpTransceiverDirection::kSendOnly, false);
   options.codec_preferences = {
-      ToRtpCodecCapability(CreateVideoCodec(-1, "vp9")),
+      ToRtpCodecCapability(CreateVideoCodec(PayloadType::NotSet(), "vp9")),
   };
   FakePayloadTypeSuggester pt_suggester;
 
diff --git a/pc/rtp_parameters_conversion.cc b/pc/rtp_parameters_conversion.cc
index 63d50d2..ca19b21 100644
--- a/pc/rtp_parameters_conversion.cc
+++ b/pc/rtp_parameters_conversion.cc
@@ -81,7 +81,7 @@
   codec.kind = cricket_codec.type == Codec::Type::kAudio ? MediaType::AUDIO
                                                          : MediaType::VIDEO;
   codec.clock_rate.emplace(cricket_codec.clockrate);
-  codec.preferred_payload_type.emplace(cricket_codec.id);
+  codec.preferred_payload_type.emplace(cricket_codec.id.value());
   for (const FeedbackParam& cricket_feedback :
        cricket_codec.feedback_params.params()) {
     std::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
diff --git a/pc/sdp_payload_type_suggester.cc b/pc/sdp_payload_type_suggester.cc
index acaca73..2386864 100644
--- a/pc/sdp_payload_type_suggester.cc
+++ b/pc/sdp_payload_type_suggester.cc
@@ -16,12 +16,14 @@
 
 #include "absl/strings/string_view.h"
 #include "api/jsep.h"
+#include "api/payload_type.h"
 #include "api/rtc_error.h"
 #include "call/payload_type.h"
 #include "call/payload_type_picker.h"
 #include "media/base/codec.h"
 #include "pc/session_description.h"
 #include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
 #include "rtc_base/thread.h"
 #include "rtc_base/trace_event.h"
 
@@ -45,6 +47,8 @@
   RTCErrorOr<PayloadType> remote_result =
       remote_recorder.LookupPayloadType(codec);
   if (remote_result.ok()) {
+    RTC_LOG(LS_INFO) << "SuggestPayloadType: Found remote mapping for " << codec
+                     << " to PT " << remote_result.value();
     RTCErrorOr<Codec> local_codec =
         local_recorder.LookupCodec(remote_result.value());
     if (!local_codec.ok()) {
@@ -55,6 +59,9 @@
     }
     // If we get here, PT is already in use, possibly for something else.
     // Fall through to SuggestMapping.
+  } else {
+    RTC_LOG(LS_INFO) << "SuggestPayloadType: FAILED to find remote mapping for "
+                     << codec;
   }
   return payload_type_picker_.SuggestMapping(codec, &local_recorder);
 }
diff --git a/pc/sdp_payload_type_suggester_unittest.cc b/pc/sdp_payload_type_suggester_unittest.cc
index d543d1b..1610822 100644
--- a/pc/sdp_payload_type_suggester_unittest.cc
+++ b/pc/sdp_payload_type_suggester_unittest.cc
@@ -16,9 +16,9 @@
 
 #include "absl/strings/string_view.h"
 #include "api/jsep.h"
+#include "api/payload_type.h"
 #include "api/peer_connection_interface.h"
 #include "api/rtc_error.h"
-#include "call/payload_type.h"
 #include "media/base/codec.h"
 #include "media/base/media_constants.h"
 #include "pc/session_description.h"
@@ -48,7 +48,8 @@
 };
 
 TEST_F(SdpPayloadTypeSuggesterTest, SuggestPayloadTypeBasic) {
-  Codec pcmu_codec = CreateAudioCodec(-1, kPcmuCodecName, 8000, 1);
+  Codec pcmu_codec =
+      CreateAudioCodec(PayloadType::NotSet(), kPcmuCodecName, 8000, 1);
   RTCErrorOr<PayloadType> pcmu_pt =
       suggester_.SuggestPayloadType("mid", pcmu_codec);
   ASSERT_TRUE(pcmu_pt.ok());
@@ -63,7 +64,8 @@
   offer->contents()[0].media_description()->set_codecs({remote_lyra_codec});
   EXPECT_TRUE(
       suggester_.Update(offer.get(), /* local= */ false, SdpType::kOffer).ok());
-  Codec local_lyra_codec = CreateAudioCodec(-1, "lyra", 8000, 1);
+  Codec local_lyra_codec =
+      CreateAudioCodec(PayloadType::NotSet(), "lyra", 8000, 1);
   RTCErrorOr<PayloadType> lyra_pt =
       suggester_.SuggestPayloadType(kAudioMid1, local_lyra_codec);
   ASSERT_TRUE(lyra_pt.ok());
@@ -81,12 +83,14 @@
   EXPECT_TRUE(
       suggester_.Update(offer.get(), /* local= */ false, SdpType::kOffer).ok());
   // Check that we get the Opus codec back with the remote PT
-  Codec local_opus_codec = CreateAudioCodec(-1, "opus", 48000, 2);
+  Codec local_opus_codec =
+      CreateAudioCodec(PayloadType::NotSet(), "opus", 48000, 2);
   RTCErrorOr<PayloadType> local_opus_pt =
       suggester_.SuggestPayloadType(kAudioMid1, local_opus_codec);
   EXPECT_EQ(local_opus_pt.value(), remote_opus_pt);
   // Check that we don't get 110 allocated for DTMF, since it's in use for opus
-  Codec local_other_codec = CreateAudioCodec(-1, kDtmfCodecName, 48000, 1);
+  Codec local_other_codec =
+      CreateAudioCodec(PayloadType::NotSet(), kDtmfCodecName, 48000, 1);
   RTCErrorOr<PayloadType> other_pt =
       suggester_.SuggestPayloadType(kAudioMid1, local_other_codec);
   ASSERT_TRUE(other_pt.ok());
diff --git a/pc/simulcast_sdp_serializer.cc b/pc/simulcast_sdp_serializer.cc
index 9f35399..68aea17 100644
--- a/pc/simulcast_sdp_serializer.cc
+++ b/pc/simulcast_sdp_serializer.cc
@@ -19,6 +19,7 @@
 
 #include "absl/algorithm/container.h"
 #include "absl/strings/string_view.h"
+#include "api/payload_type.h"
 #include "api/rtc_error.h"
 #include "api/rtp_parameters.h"
 #include "media/base/codec.h"
@@ -302,7 +303,7 @@
     if (it == media_desc.codecs().end()) {
       break;
     }
-    if (it->id == Codec::kIdNotSet) {
+    if (it->id == PayloadType::NotSet()) {
       RTC_DCHECK_NOTREACHED();
       break;
     }
diff --git a/rtc_base/strong_alias.h b/rtc_base/strong_alias.h
index 548253d..8071a7e 100644
--- a/rtc_base/strong_alias.h
+++ b/rtc_base/strong_alias.h
@@ -68,6 +68,13 @@
 
  protected:
   UnderlyingType value_;
+
+ private:
+  // Helper function for using abseil hash types.
+  template <typename H>
+  friend H AbslHashValue(H h, const StrongAlias& st) {
+    return H::combine(std::move(h), st.value_);
+  }
 };
 
 }  // namespace webrtc