[PT Redesign] Implement late payload type allocation for video.

This CL implements the late payload type allocation strategy for video
codecs, matching the existing implementation for audio.

Key changes:
- Introduced CodecConfiguration and ResiliencyInfo to track codec
  metadata without pre-assigned payload types.
- Implemented MergeCodecsFromConfigurations in CodecVendor to expand
  media codecs into their resiliency counterparts (RTX, RED, FEC) and
  link them only after primary PTs are suggested.
- Extracted MergeCodecsByDirection to unify direction-based merging
  logic across offer and answer paths.
- Relaxed RTX matching in CodecComparators to support negotiation before
  PTs are fully linked.
- Updated all call sites in pc/ to handle refactored CodecVendor
  accessors.
- Added integration tests in
  pc/codec_vendor_redesign_unittest.cc.

This work allows payload types to be assigned permanently during
SetLocalDescription/SetRemoteDescription, improving stability across
renegotiations.

Bug: webrtc:360058654
Change-Id: Ifae3f3daca1b18a3ada2131a57046ce4ff3dcd36
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/472060
Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47709}
diff --git a/g3doc/todo/payload_type_redesign.md b/g3doc/todo/payload_type_redesign.md
index fd11fedf..3f3ea16 100644
--- a/g3doc/todo/payload_type_redesign.md
+++ b/g3doc/todo/payload_type_redesign.md
@@ -1,5 +1,7 @@
 # Payload type allocation redesign
 
+The bug associated with this work is webrtc:360058654
+
 ## Background: What a payload type is
 
 The payload type is a property of a codec that is established between a sender
diff --git a/media/base/codec_comparators.cc b/media/base/codec_comparators.cc
index f088fc4..859cf43 100644
--- a/media/base/codec_comparators.cc
+++ b/media/base/codec_comparators.cc
@@ -11,7 +11,6 @@
 
 #include <algorithm>
 #include <cstddef>
-#include <cstdint>
 #include <optional>
 #include <string>
 #include <vector>
@@ -32,7 +31,6 @@
 #include "media/base/codec.h"
 #include "media/base/media_constants.h"
 #include "rtc_base/checks.h"
-#include "rtc_base/logging.h"
 #include "rtc_base/string_encode.h"
 
 namespace webrtc {
@@ -200,21 +198,18 @@
   if (resiliency_type == Codec::ResiliencyType::kRtx) {
     int apt_value_1_int = 0;
     int apt_value_2_int = 0;
-    if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
-                                 &apt_value_1_int) ||
-        !potential_match.GetParam(kCodecParamAssociatedPayloadType,
-                                  &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)) {
+    bool has_apt_1 = codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
+                                             &apt_value_1_int);
+    bool has_apt_2 = potential_match.GetParam(kCodecParamAssociatedPayloadType,
+                                              &apt_value_2_int);
+    if (!has_apt_1 && !has_apt_2) {
       return true;
     }
-    return false;
+    if (!has_apt_1 || !has_apt_2) {
+      return false;
+    }
+    return reference_comparator(PayloadType(apt_value_1_int),
+                                PayloadType(apt_value_2_int));
   }
   if (resiliency_type == Codec::ResiliencyType::kRed) {
     auto red_parameters_1 =
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index e17db1e..8054c46 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -429,6 +429,7 @@
     "../rtc_base:macromagic",
     "../rtc_base:stringutils",
     "../rtc_base:threading",
+    "../rtc_base/containers:flat_map",
     "../rtc_base/system:no_unique_address",
     "//third_party/abseil-cpp/absl/algorithm:container",
     "//third_party/abseil-cpp/absl/base:nullability",
diff --git a/pc/codec_vendor.cc b/pc/codec_vendor.cc
index 16a791b..96b3ebe 100644
--- a/pc/codec_vendor.cc
+++ b/pc/codec_vendor.cc
@@ -10,16 +10,13 @@
 #include "pc/codec_vendor.h"
 
 #include <cstddef>
-#include <map>
 #include <optional>
 #include <string>
-#include <unordered_map>
 #include <utility>
 #include <vector>
 
 #include "absl/algorithm/container.h"
 #include "absl/strings/match.h"
-#include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 #include "api/field_trials_view.h"
 #include "api/media_types.h"
@@ -35,11 +32,13 @@
 #include "media/base/media_constants.h"
 #include "media/base/media_engine.h"
 #include "media/base/sdp_video_format_utils.h"
+#include "pc/codec_configuration.h"
 #include "pc/media_options.h"
 #include "pc/rtp_media_utils.h"
 #include "pc/session_description.h"
 #include "pc/typed_codec_vendor.h"
 #include "rtc_base/checks.h"
+#include "rtc_base/containers/flat_map.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/string_encode.h"
 #include "rtc_base/strings/string_builder.h"
@@ -52,6 +51,14 @@
 namespace webrtc {
 namespace {
 
+std::optional<PayloadType> PayloadTypeFromString(absl::string_view s) {
+  int pt;
+  if (FromString(s, &pt)) {
+    return PayloadType::Create(pt);
+  }
+  return std::nullopt;
+}
+
 bool IsRtxCodec(const RtpCodecCapability& capability) {
   return absl::EqualsIgnoreCase(capability.name, kRtxCodecName);
 }
@@ -89,29 +96,29 @@
 const Codec* GetAssociatedCodecForRtx(const CodecList& codec_list,
                                       const Codec& rtx_codec) {
   RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
-  std::string associated_pt_str;
+  int associated_pt_int;
   if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
-                          &associated_pt_str)) {
+                          &associated_pt_int)) {
     RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.id
                         << " is missing an associated payload type.";
     return nullptr;
   }
 
-  int associated_pt;
-  if (!FromString(associated_pt_str, &associated_pt)) {
-    RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
-                        << " of RTX codec " << rtx_codec.id
-                        << " to an integer.";
+  std::optional<PayloadType> associated_pt =
+      PayloadType::Create(associated_pt_int);
+  if (!associated_pt) {
+    RTC_LOG(LS_WARNING) << "Invalid payload type " << associated_pt_int
+                        << " for RTX codec " << rtx_codec.id << ".";
     return nullptr;
   }
 
   // Find the associated codec for the RTX codec.
   const Codec* associated_codec =
-      FindCodecById(codec_list.codecs(), associated_pt);
+      FindCodecById(codec_list.codecs(), *associated_pt);
   if (!associated_codec) {
     RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
-                        << associated_pt << " for RTX codec " << rtx_codec.id
-                        << ".";
+                        << associated_pt_int << " for RTX codec "
+                        << rtx_codec.id << ".";
   }
   return associated_codec;
 }
@@ -142,21 +149,22 @@
   }
   for (size_t index = 0; index < redundant_payloads.size(); ++index) {
     absl::string_view associated_pt_str = redundant_payloads[index];
-    int associated_pt;
-    if (!FromString(associated_pt_str, &associated_pt)) {
+    std::optional<PayloadType> associated_pt =
+        PayloadTypeFromString(associated_pt_str);
+    if (!associated_pt) {
       RTC_LOG(LS_WARNING) << "Couldn't convert payload type "
                           << associated_pt_str << " of RED codec " << red_codec
-                          << " to an integer.";
+                          << " to a valid payload type.";
       return RTCError(RTCErrorType::INTERNAL_ERROR,
-                      "RED codec with non-integer argument");
+                      "RED codec with invalid payload type argument");
     }
 
     // Find the associated codec for the RED codec.
     const Codec* associated_codec =
-        FindCodecById(codec_list.codecs(), associated_pt);
+        FindCodecById(codec_list.codecs(), *associated_pt);
     if (!associated_codec) {
       RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
-                          << associated_pt << " for RED codec " << red_codec
+                          << associated_pt_str << " for RED codec " << red_codec
                           << ".";
       return RTCError(RTCErrorType::INTERNAL_ERROR,
                       "RED codec pointing to nonexistent PT");
@@ -166,14 +174,203 @@
   return codecs;
 }
 
+RTCError MergeRtxCodec(const CodecConfiguration& config,
+                       const Codec& primary_codec,
+                       absl::string_view mid,
+                       CodecList& offered_codecs,
+                       PayloadTypeSuggester& pt_suggester,
+                       bool pick_from_top_of_range) {
+  if (!config.resiliency.rtx) {
+    return RTCError::OK();
+  }
+  auto rtx_it = absl::c_find_if(offered_codecs, [&](const Codec& c) {
+    if (c.name != kRtxCodecName)
+      return false;
+    int apt;
+    return c.GetParam(kCodecParamAssociatedPayloadType, &apt) &&
+           apt == primary_codec.id.value();
+  });
+  if (rtx_it == offered_codecs.end()) {
+    Codec rtx = (config.codec.type == Codec::Type::kAudio)
+                    ? CreateAudioCodec({kRtxCodecName, config.codec.clockrate,
+                                        config.codec.channels})
+                    : CreateVideoCodec(PayloadType::NotSet(), kRtxCodecName);
+    rtx.SetParam(kCodecParamAssociatedPayloadType, primary_codec.id.value());
+    auto result =
+        pt_suggester.SuggestPayloadType(mid, rtx, pick_from_top_of_range);
+    if (!result.ok()) {
+      return result.MoveError();
+    }
+    rtx.id = result.value();
+    offered_codecs.push_back(rtx);
+  }
+  return RTCError::OK();
+}
+
+RTCError MergeRedCodec(const CodecConfiguration& config,
+                       absl::string_view mid,
+                       CodecList& offered_codecs,
+                       PayloadTypeSuggester& pt_suggester,
+                       bool pick_from_top_of_range) {
+  if (!config.resiliency.red) {
+    return RTCError::OK();
+  }
+  auto red_it = absl::c_find_if(offered_codecs, [&](const Codec& c) {
+    return c.name == kRedCodecName && c.type == config.codec.type;
+  });
+  if (red_it == offered_codecs.end()) {
+    Codec red = (config.codec.type == Codec::Type::kAudio)
+                    ? CreateAudioCodec({kRedCodecName, 48000, 2})
+                    : CreateVideoCodec(kRedCodecName);
+    auto result =
+        pt_suggester.SuggestPayloadType(mid, red, pick_from_top_of_range);
+    if (!result.ok()) {
+      return result.MoveError();
+    }
+    red.id = result.value();
+    offered_codecs.push_back(red);
+
+    if (config.codec.type == Codec::Type::kVideo) {
+      // Video RED also gets an RTX codec.
+      Codec red_rtx = CreateVideoCodec(PayloadType::NotSet(), kRtxCodecName);
+      red_rtx.SetParam(kCodecParamAssociatedPayloadType, red.id.value());
+      auto rtx_res =
+          pt_suggester.SuggestPayloadType(mid, red_rtx, pick_from_top_of_range);
+      if (rtx_res.ok()) {
+        red_rtx.id = rtx_res.value();
+        offered_codecs.push_back(red_rtx);
+      } else {
+        // If error is RESOURCE_EXHAUSTED, we ran out of PT numbers.
+        // In that case, we can ignore the codec altogether.
+        RTC_LOG(LS_WARNING)
+            << "Error when assigning RTX codec to RED codec:" << rtx_res;
+        // In debug mode, check that it's the expected error code.
+        RTC_DCHECK(rtx_res.error().type() == RTCErrorType::RESOURCE_EXHAUSTED);
+      }
+    }
+  }
+  return RTCError::OK();
+}
+
+RTCError MergeUlpfecCodec(const CodecConfiguration& config,
+                          absl::string_view mid,
+                          CodecList& offered_codecs,
+                          PayloadTypeSuggester& pt_suggester,
+                          bool pick_from_top_of_range) {
+  if (!config.resiliency.ulpfec || config.codec.type != Codec::Type::kVideo) {
+    return RTCError::OK();
+  }
+  auto fec_it = absl::c_find_if(offered_codecs, [&](const Codec& c) {
+    return c.name == kUlpfecCodecName;
+  });
+  if (fec_it == offered_codecs.end()) {
+    Codec fec = CreateVideoCodec(kUlpfecCodecName);
+    auto result =
+        pt_suggester.SuggestPayloadType(mid, fec, pick_from_top_of_range);
+    if (!result.ok()) {
+      return result.MoveError();
+    }
+    fec.id = result.value();
+    offered_codecs.push_back(fec);
+  }
+  return RTCError::OK();
+}
+
+RTCError MergeFlexfecCodec(const CodecConfiguration& config,
+                           absl::string_view mid,
+                           CodecList& offered_codecs,
+                           PayloadTypeSuggester& pt_suggester,
+                           const FieldTrialsView& trials,
+                           bool pick_from_top_of_range) {
+  if (!config.resiliency.flexfec || config.codec.type != Codec::Type::kVideo ||
+      !trials.IsEnabled("WebRTC-FlexFEC-03-Advertised")) {
+    return RTCError::OK();
+  }
+  auto fec_it = absl::c_find_if(offered_codecs, [&](const Codec& c) {
+    return c.name == kFlexfecCodecName;
+  });
+  if (fec_it == offered_codecs.end()) {
+    Codec fec = CreateVideoCodec(kFlexfecCodecName);
+    auto result =
+        pt_suggester.SuggestPayloadType(mid, fec, pick_from_top_of_range);
+    if (!result.ok()) {
+      return result.MoveError();
+    }
+    fec.id = result.value();
+    offered_codecs.push_back(fec);
+  }
+  return RTCError::OK();
+}
+
+// Adds all codecs from `configurations` to `offered_codecs` that don't
+// already exist in `offered_codecs` and ensure the payload types don't
+// collide.
+RTCError MergeCodecsFromConfigurations(
+    const std::vector<CodecConfiguration>& configurations,
+    absl::string_view mid,
+    CodecList& offered_codecs,
+    PayloadTypeSuggester& pt_suggester,
+    const FieldTrialsView& trials,
+    bool pick_from_top_of_range = false) {
+  RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
+  for (const auto& config : configurations) {
+    // 1. Find or add primary codec
+    auto primary_it = absl::c_find_if(offered_codecs, [&](const Codec& c) {
+      return MatchesWithCodecRules(config.codec, c);
+    });
+    Codec primary_codec;
+    if (primary_it == offered_codecs.end()) {
+      primary_codec = config.codec;
+      auto result = pt_suggester.SuggestPayloadType(mid, primary_codec,
+                                                    pick_from_top_of_range);
+      if (!result.ok()) {
+        return result.MoveError();
+      }
+      primary_codec.id = result.value();
+      offered_codecs.PushIfNotPresent(primary_codec);
+    } else {
+      primary_codec = *primary_it;
+    }
+
+    // 2. Handle RTX
+    RTCError error = MergeRtxCodec(config, primary_codec, mid, offered_codecs,
+                                   pt_suggester, pick_from_top_of_range);
+    if (!error.ok()) {
+      return error;
+    }
+
+    // 3. Handle RED
+    error = MergeRedCodec(config, mid, offered_codecs, pt_suggester,
+                          pick_from_top_of_range);
+    if (!error.ok()) {
+      return error;
+    }
+
+    // 4. Handle ULPFEC
+    error = MergeUlpfecCodec(config, mid, offered_codecs, pt_suggester,
+                             pick_from_top_of_range);
+    if (!error.ok()) {
+      return error;
+    }
+
+    // 5. Handle FlexFEC
+    error = MergeFlexfecCodec(config, mid, offered_codecs, pt_suggester, trials,
+                              pick_from_top_of_range);
+    if (!error.ok()) {
+      return error;
+    }
+  }
+  return RTCError::OK();
+}
+
 // Adds all codecs from `reference_codecs` to `offered_codecs` that don't
 // already exist in `offered_codecs` and ensure the payload types don't
 // collide.
-RTCError MergeCodecs(const CodecList& reference_codecs,
-                     absl::string_view mid,
-                     CodecList& offered_codecs,
-                     PayloadTypeSuggester& pt_suggester,
-                     bool pick_from_top_of_range = false) {
+RTCError MergeCodecsLegacy(const CodecList& reference_codecs,
+                           absl::string_view mid,
+                           CodecList& offered_codecs,
+                           PayloadTypeSuggester& pt_suggester,
+                           bool pick_from_top_of_range = false) {
   RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
   // Add all new codecs that are not RTX/RED codecs.
   // The two-pass splitting of the loops means preferring payload types
@@ -213,8 +410,8 @@
         continue;
       }
 
-      rtx_codec.params[kCodecParamAssociatedPayloadType] =
-          absl::StrCat(matching_codec->id);
+      rtx_codec.SetParam(kCodecParamAssociatedPayloadType,
+                         matching_codec->id.value());
       RTCErrorOr<PayloadType> suggestion = pt_suggester.SuggestPayloadType(
           mid, rtx_codec, pick_from_top_of_range);
       if (!suggestion.ok()) {
@@ -287,7 +484,6 @@
   }
 
   offered_codecs.CheckConsistency();
-
   return RTCError::OK();
 }
 
@@ -335,14 +531,15 @@
           filtered_codecs.push_back(*found_codec_with_correct_pt);
           red_was_added = is_red_codec ? true : red_was_added;
         }
-        std::string id = absl::StrCat(found_codec_with_correct_pt->id);
+        PayloadType id = found_codec_with_correct_pt->id;
         // Search for the matching rtx or red codec.
         if (want_red || want_rtx) {
           for (const Codec& codec : codecs) {
             if (want_rtx &&
                 codec.GetResiliencyType() == Codec::ResiliencyType::kRtx) {
-              auto apt = codec.params.find(kCodecParamAssociatedPayloadType);
-              if (apt != codec.params.end() && apt->second == id) {
+              int apt;
+              if (codec.GetParam(kCodecParamAssociatedPayloadType, &apt) &&
+                  apt == id.value()) {
                 filtered_codecs.push_back(codec);
                 break;
               }
@@ -355,8 +552,10 @@
               if (fmtp != codec.params.end()) {
                 std::vector<absl::string_view> redundant_payloads =
                     split(fmtp->second, '/');
+                int first_redundant_pt;
                 if (!redundant_payloads.empty() &&
-                    redundant_payloads[0] == id) {
+                    FromString(redundant_payloads[0], &first_redundant_pt) &&
+                    first_redundant_pt == id.value()) {
                   if (!red_was_added) {
                     filtered_codecs.push_back(codec);
                     red_was_added = true;
@@ -411,7 +610,7 @@
   // do not clear define the expected behavior for the level in the offer.
 #ifdef RTC_ENABLE_H265
   if (media_description_options.type == MediaType::VIDEO) {
-    std::unordered_map<H265Profile, H265Level> supported_h265_profiles;
+    flat_map<H265Profile, H265Level> supported_h265_profiles;
     // The assumption here is that H.265 codecs with the same profile and tier
     // are already with highest level for that profile in both
     // |supported_codecs| and |filtered_codecs|.
@@ -454,7 +653,7 @@
                          CodecList& negotiated_codecs_out,
                          bool keep_offer_order) {
   RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
-  std::map<int, int> pt_mapping_table;
+  flat_map<PayloadType, PayloadType> pt_mapping_table;
   // Since we build the negotiated codec list one entry at a time,
   // the list will have inconsistencies during building.
   std::vector<Codec> negotiated_codecs;
@@ -504,22 +703,18 @@
     if (negotiated.GetResiliencyType() == Codec::ResiliencyType::kRtx) {
       // Change the apt value according to the pt mapping table.
       // This avoids changing to apt values that don't exist any more.
-      std::string apt_str;
-      if (!negotiated.GetParam(kCodecParamAssociatedPayloadType, &apt_str)) {
+      int apt_int;
+      if (!negotiated.GetParam(kCodecParamAssociatedPayloadType, &apt_int)) {
         RTC_LOG(LS_WARNING) << "No apt value";
         continue;
       }
-      int apt_value;
-      if (!FromString(apt_str, &apt_value)) {
-        RTC_LOG(LS_WARNING) << "Unconvertable apt value";
-        continue;
-      }
-      if (pt_mapping_table.count(apt_value) != 1) {
+      PayloadType apt_value(apt_int);
+      if (!pt_mapping_table.contains(apt_value)) {
         RTC_LOG(LS_WARNING) << "Unmapped apt value " << apt_value;
         continue;
       }
       negotiated.SetParam(kCodecParamAssociatedPayloadType,
-                          pt_mapping_table.at(apt_value));
+                          pt_mapping_table.at(apt_value).value());
     }
   }
   if (keep_offer_order) {
@@ -528,7 +723,7 @@
     // specific reason, the answerer list formats in the same relative order
     // they were present in the offer.
     // This can be skipped when the transceiver has any codec preferences.
-    std::unordered_map<int, int> payload_type_preferences;
+    flat_map<PayloadType, int> payload_type_preferences;
     int preference = static_cast<int>(offered_codecs.size() + 1);
     for (const Codec& codec : offered_codecs) {
       payload_type_preferences[codec.id] = preference--;
@@ -546,50 +741,89 @@
   return RTCError::OK();
 }
 
-// Update the ID fields of the codec vector.
-// If any codec has an ID with value "kIdNotSet", use the payload type suggester
-// to assign and record a payload type for it.
 // If there is a RED codec without its fmtp parameter, give it the ID of the
 // first OPUS codec in the codec list.
-RTCError AssignCodecIdsAndLinkRed(PayloadTypeSuggester* pt_suggester,
-                                  const std::string& mid,
-                                  std::vector<Codec>& codecs,
-                                  bool pick_from_top_of_range = false) {
-  RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
-  int codec_payload_type = Codec::kIdNotSet;
-  for (Codec& codec : codecs) {
-    if (codec.id == PayloadType::NotSet()) {
-      // Add payload types to codecs, if needed
-      // This should only happen if WebRTC-PayloadTypesInTransport field trial
-      // is enabled.
-      RTC_CHECK(pt_suggester);
-      RTCErrorOr<PayloadType> result =
-          pt_suggester->SuggestPayloadType(mid, codec, pick_from_top_of_range);
-      if (!result.ok()) {
-        return result.error();
-      }
-      codec.id = result.value();
-    } else if (pt_suggester) {
-      pt_suggester->AddLocalMapping(mid, codec.id, codec);
-    }
-    // record first Opus codec id
-    if (absl::EqualsIgnoreCase(codec.name, kOpusCodecName) &&
-        codec_payload_type == Codec::kIdNotSet) {
-      codec_payload_type = codec.id.value();
+void LinkRed(std::vector<Codec>& codecs) {
+  int first_opus_pt = Codec::kIdNotSet;
+  for (const Codec& codec : codecs) {
+    if (codec.type == Codec::Type::kAudio &&
+        absl::EqualsIgnoreCase(codec.name, kOpusCodecName)) {
+      first_opus_pt = codec.id.value();
+      break;
     }
   }
-  if (codec_payload_type != Codec::kIdNotSet) {
+
+  if (first_opus_pt != Codec::kIdNotSet) {
     for (Codec& codec : codecs) {
       if (codec.type == Codec::Type::kAudio &&
           absl::EqualsIgnoreCase(codec.name, kRedCodecName)) {
         if (codec.params.empty()) {
           StringBuilder param;
-          param << codec_payload_type << "/" << codec_payload_type;
+          param << first_opus_pt << "/" << first_opus_pt;
           codec.SetParam(kCodecParamNotInNameValueFormat, param.str());
         }
       }
     }
   }
+}
+
+// Update the ID fields of the codec vector in the legacy path
+// (payload_types_in_transport_ = false).
+// If any codec has an ID with value "kIdNotSet", this is an error.
+RTCError RecordCodecIdsAndLinkRed(PayloadTypeSuggester& pt_suggester,
+                                  const std::string& mid,
+                                  std::vector<Codec>& codecs) {
+  RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
+  for (Codec& codec : codecs) {
+    RTC_DCHECK(codec.id != PayloadType::NotSet());
+    pt_suggester.AddLocalMapping(mid, codec.id, codec);
+  }
+
+  LinkRed(codecs);
+  return RTCError::OK();
+}
+
+// Update the ID fields of the codec vector in the redesigned path.
+// If any codec has an ID with value "kIdNotSet", use the payload type suggester
+// to assign and record a payload type for it.
+RTCError AssignCodecIdsAndLinkRedRefactored(
+    PayloadTypeSuggester& pt_suggester,
+    const std::string& mid,
+    std::vector<Codec>& codecs,
+    bool pick_from_top_of_range = false) {
+  RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
+  Codec* last_codec_that_can_have_rtx = nullptr;
+
+  for (Codec& codec : codecs) {
+    if (codec.id == PayloadType::NotSet()) {
+      auto result =
+          pt_suggester.SuggestPayloadType(mid, codec, pick_from_top_of_range);
+      if (!result.ok()) {
+        return result.error();
+      }
+      codec.id = result.value();
+    } else {
+      pt_suggester.AddLocalMapping(mid, codec.id, codec);
+    }
+
+    if (codec.GetResiliencyType() == Codec::ResiliencyType::kRtx) {
+      if (last_codec_that_can_have_rtx &&
+          codec.params.find(kCodecParamAssociatedPayloadType) ==
+              codec.params.end()) {
+        // When new codecs are added that want RTX, they're added as a pair
+        // of (media codec, rtx, codec). But since they don't have assigned
+        // IDs yet, the apt parameter can't be set. This logic ensures that
+        // the apt parameter points to the immediately preceding codec.
+        codec.SetParam(kCodecParamAssociatedPayloadType,
+                       last_codec_that_can_have_rtx->id.value());
+      }
+    } else {
+      // This is a codec that can potentially be associated with an RTX codec.
+      last_codec_that_can_have_rtx = &codec;
+    }
+  }
+
+  LinkRed(codecs);
   return RTCError::OK();
 }
 
@@ -601,8 +835,61 @@
                                CodecList& offered_codecs,
                                PayloadTypeSuggester& pt_suggester,
                                bool pick_from_top_of_range) {
-  return MergeCodecs(reference_codecs, mid, offered_codecs, pt_suggester,
-                     pick_from_top_of_range);
+  // This function is only available for testing the legacy path.
+  return MergeCodecsLegacy(reference_codecs, mid, offered_codecs, pt_suggester,
+                           pick_from_top_of_range);
+}
+
+RTCError CodecVendor::MergeCodecsByDirection(MediaType type,
+                                             RtpTransceiverDirection direction,
+                                             absl::string_view mid,
+                                             CodecList& codecs_out,
+                                             PayloadTypeSuggester& pt_suggester,
+                                             bool pick_from_top_of_range,
+                                             bool favor_send_order) {
+  RTC_DCHECK_RUN_ON(&sequence_checker_);
+  RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
+  const std::vector<CodecConfiguration>& send_configs =
+      (type == MediaType::AUDIO) ? audio_send_codecs_.configurations()
+                                 : video_send_codecs_.configurations();
+  const std::vector<CodecConfiguration>& recv_configs =
+      (type == MediaType::AUDIO) ? audio_recv_codecs_.configurations()
+                                 : video_recv_codecs_.configurations();
+
+  switch (direction) {
+    case RtpTransceiverDirection::kSendRecv:
+    case RtpTransceiverDirection::kStopped:
+    case RtpTransceiverDirection::kInactive: {
+      if (favor_send_order) {
+        RTCError error = MergeCodecsFromConfigurations(
+            send_configs, mid, codecs_out, pt_suggester, trials_,
+            pick_from_top_of_range);
+        if (!error.ok())
+          return error;
+        return MergeCodecsFromConfigurations(recv_configs, mid, codecs_out,
+                                             pt_suggester, trials_,
+                                             pick_from_top_of_range);
+      } else {
+        RTCError error = MergeCodecsFromConfigurations(
+            recv_configs, mid, codecs_out, pt_suggester, trials_,
+            pick_from_top_of_range);
+        if (!error.ok())
+          return error;
+        return MergeCodecsFromConfigurations(send_configs, mid, codecs_out,
+                                             pt_suggester, trials_,
+                                             pick_from_top_of_range);
+      }
+    }
+    case RtpTransceiverDirection::kSendOnly:
+      return MergeCodecsFromConfigurations(send_configs, mid, codecs_out,
+                                           pt_suggester, trials_,
+                                           pick_from_top_of_range);
+    case RtpTransceiverDirection::kRecvOnly:
+      return MergeCodecsFromConfigurations(recv_configs, mid, codecs_out,
+                                           pt_suggester, trials_,
+                                           pick_from_top_of_range);
+  }
+  RTC_CHECK_NOTREACHED();
 }
 
 RTCErrorOr<std::vector<Codec>> CodecVendor::GetNegotiatedCodecsForOffer(
@@ -612,37 +899,68 @@
     PayloadTypeSuggester& pt_suggester) {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
   RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
-  CodecList codecs;
+
   std::string mid = media_description_options.mid;
-  // If current content exists and is not being recycled, use its codecs.
-  if (current_content && current_content->mid() == mid &&
-      IsMediaContentOfType(current_content, media_description_options.type)) {
-    RTCErrorOr<CodecList> checked_codec_list =
-        CodecList::Create(current_content->media_description()->codecs());
-    if (!checked_codec_list.ok()) {
-      return checked_codec_list.MoveError();
+  CodecList codecs;
+
+  if (payload_types_in_transport_) {
+    // REDESIGN path: Assume codecs from TypedCodecVendor are NotSet.
+    // If current content exists and is not being recycled, use its codecs.
+    if (current_content && current_content->mid() == mid &&
+        IsMediaContentOfType(current_content, media_description_options.type)) {
+      RTCErrorOr<CodecList> checked_codec_list =
+          CodecList::Create(current_content->media_description()->codecs());
+      if (!checked_codec_list.ok()) {
+        return checked_codec_list.MoveError();
+      }
+      codecs = checked_codec_list.MoveValue();
+      for (const Codec& codec : codecs) {
+        pt_suggester.AddLocalMapping(mid, codec.id, codec);
+      }
     }
-    // Use MergeCodecs in order to handle PT clashes.
-    MergeCodecs(checked_codec_list.value(), mid, codecs, pt_suggester,
-                /*pick_from_top_of_range=*/true);
-  }
-  // Add our codecs that are not in the current description.
-  if (media_description_options.type == MediaType::AUDIO) {
-    MergeCodecs(audio_recv_codecs(), mid, codecs, pt_suggester,
-                /*pick_from_top_of_range=*/true);
-    MergeCodecs(audio_send_codecs(), mid, codecs, pt_suggester,
-                /*pick_from_top_of_range=*/true);
+    MergeCodecsByDirection(media_description_options.type,
+                           RtpTransceiverDirection::kSendRecv, mid, codecs,
+                           pt_suggester, /*pick_from_top_of_range=*/true);
   } else {
-    MergeCodecs(video_recv_codecs(), mid, codecs, pt_suggester,
-                /*pick_from_top_of_range=*/true);
-    MergeCodecs(video_send_codecs(), mid, codecs, pt_suggester,
-                /*pick_from_top_of_range=*/true);
+    // LEGACY path: Assume codecs have PTs.
+    // If current content exists and is not being recycled, use its codecs.
+    if (current_content && current_content->mid() == mid &&
+        IsMediaContentOfType(current_content, media_description_options.type)) {
+      RTCErrorOr<CodecList> checked_codec_list =
+          CodecList::Create(current_content->media_description()->codecs());
+      if (!checked_codec_list.ok()) {
+        return checked_codec_list.MoveError();
+      }
+      // Use MergeCodecsLegacy in order to handle PT clashes.
+      MergeCodecsLegacy(checked_codec_list.value(), mid, codecs, pt_suggester,
+                        /*pick_from_top_of_range=*/true);
+    }
+    // Add our codecs that are not in the current description.
+    if (media_description_options.type == MediaType::AUDIO) {
+      MergeCodecsLegacy(audio_recv_codecs_.codecs(), mid, codecs, pt_suggester,
+                        /*pick_from_top_of_range=*/true);
+      MergeCodecsLegacy(audio_send_codecs_.codecs(), mid, codecs, pt_suggester,
+                        /*pick_from_top_of_range=*/true);
+    } else {
+      MergeCodecsLegacy(video_recv_codecs_.codecs(), mid, codecs, pt_suggester,
+                        /*pick_from_top_of_range=*/true);
+      MergeCodecsLegacy(video_send_codecs_.codecs(), mid, codecs, pt_suggester,
+                        /*pick_from_top_of_range=*/true);
+    }
   }
+
   CodecList filtered_codecs;
-  CodecList supported_codecs =
-      media_description_options.type == MediaType::AUDIO
-          ? GetAudioCodecsForOffer(media_description_options.direction)
-          : GetVideoCodecsForOffer(media_description_options.direction);
+  CodecList supported_codecs;
+  if (payload_types_in_transport_) {
+    MergeCodecsByDirection(
+        media_description_options.type, media_description_options.direction,
+        mid, supported_codecs, pt_suggester, /*pick_from_top_of_range=*/true);
+  } else {
+    supported_codecs =
+        media_description_options.type == MediaType::AUDIO
+            ? GetAudioCodecsForOffer(media_description_options.direction)
+            : GetVideoCodecsForOffer(media_description_options.direction);
+  }
 
   if (media_description_options.codecs_to_include.empty()) {
     if (!media_description_options.codec_preferences.empty()) {
@@ -655,14 +973,14 @@
       // Add the codecs from current content if it exists and is not rejected
       // nor recycled.
       if (current_content && !current_content->rejected &&
-          current_content->mid() == media_description_options.mid) {
+          current_content->mid() == mid) {
         if (!IsMediaContentOfType(current_content,
                                   media_description_options.type)) {
           // Can happen if the remote side re-uses a MID while recycling.
           return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR)
                            << "Media type for content with mid='"
                            << current_content->mid()
-                           << "' does not match previous type.");
+                           << "' does not match expected type.");
         }
         const MediaContentDescription* mcd =
             current_content->media_description();
@@ -689,30 +1007,20 @@
             // audio.
             const Codec* referenced_codec =
                 GetAssociatedCodecForRtx(supported_codecs, codec);
-            RTC_DCHECK(referenced_codec);
-
-            // Find the codec we should be referencing and point to it.
-            std::optional<Codec> changed_referenced_codec = FindMatchingCodec(
-                supported_codecs, filtered_codecs, *referenced_codec);
-            if (changed_referenced_codec) {
-              found_codec->SetParam(kCodecParamAssociatedPayloadType,
-                                    changed_referenced_codec->id);
+            if (referenced_codec) {
+              // Find the codec we should be referencing and point to it.
+              std::optional<Codec> changed_referenced_codec = FindMatchingCodec(
+                  supported_codecs, filtered_codecs, *referenced_codec);
+              if (changed_referenced_codec) {
+                found_codec->SetParam(kCodecParamAssociatedPayloadType,
+                                      changed_referenced_codec->id.value());
+              }
             }
           }
-          // Ensure pt_suggester is aware of this codec for this MID.
-          // This should just return the PT already in found_codec->id,
-          // unless there is a collision in filtered_codecs.
-          RTCErrorOr<PayloadType> suggestion = pt_suggester.SuggestPayloadType(
-              mid, *found_codec, /*pick_from_top_of_range=*/true);
-          if (!suggestion.ok()) {
-            return suggestion.MoveError();
-          }
-          found_codec->id = suggestion.value();
           filtered_codecs.PushIfNotPresent(*found_codec);
         }
       }
     }
-
     if (media_description_options.type == MediaType::AUDIO &&
         !session_options.vad_enabled) {
       // If application doesn't want CN codecs in offer.
@@ -737,10 +1045,14 @@
     }
     filtered_codecs = codecs_from_arg.MoveValue();
   }
-  AssignCodecIdsAndLinkRed(&pt_suggester, mid,
-                           filtered_codecs.writable_codecs(),
-                           /*pick_from_top_of_range=*/true);
-
+  if (payload_types_in_transport_) {
+    AssignCodecIdsAndLinkRedRefactored(pt_suggester, mid,
+                                       filtered_codecs.writable_codecs(),
+                                       /*pick_from_top_of_range=*/true);
+  } else {
+    RecordCodecIdsAndLinkRed(pt_suggester, mid,
+                             filtered_codecs.writable_codecs());
+  }
   return filtered_codecs.codecs();
 }
 
@@ -750,37 +1062,77 @@
     RtpTransceiverDirection offer_rtd,
     RtpTransceiverDirection answer_rtd,
     const ContentInfo* current_content,
-    const std::vector<Codec> codecs_from_offer,
+    std::vector<Codec> codecs_from_offer,
     PayloadTypeSuggester& pt_suggester) {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
   RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
+
   CodecList codecs;
   std::string mid = media_description_options.mid;
-  if (current_content && current_content->mid() == mid &&
-      IsMediaContentOfType(current_content, media_description_options.type)) {
-    RTCErrorOr<CodecList> checked_codec_list =
-        CodecList::Create(current_content->media_description()->codecs());
-    if (!checked_codec_list.ok()) {
-      return checked_codec_list.MoveError();
+
+  if (payload_types_in_transport_) {
+    // REDESIGN path: Assume local codecs from TypedCodecVendor are NotSet.
+    // If current content exists and is not being recycled, use its codecs.
+    if (current_content && current_content->mid() == mid &&
+        IsMediaContentOfType(current_content, media_description_options.type)) {
+      RTCErrorOr<CodecList> checked_codec_list =
+          CodecList::Create(current_content->media_description()->codecs());
+      if (!checked_codec_list.ok()) {
+        return checked_codec_list.MoveError();
+      }
+      codecs = checked_codec_list.MoveValue();
+      for (const Codec& codec : codecs) {
+        pt_suggester.AddLocalMapping(mid, codec.id, codec);
+      }
     }
-    MergeCodecs(checked_codec_list.value(), mid, codecs, pt_suggester);
-  }
-  // Add all our supported codecs
-  if (media_description_options.type == MediaType::AUDIO) {
-    MergeCodecs(audio_send_codecs(), mid, codecs, pt_suggester);
-    MergeCodecs(audio_recv_codecs(), mid, codecs, pt_suggester);
+    MergeCodecsByDirection(media_description_options.type,
+                           RtpTransceiverDirection::kSendRecv, mid, codecs,
+                           pt_suggester, /*pick_from_top_of_range=*/false,
+                           /*favor_send_order=*/true);
   } else {
-    MergeCodecs(video_send_codecs(), mid, codecs, pt_suggester);
-    MergeCodecs(video_recv_codecs(), mid, codecs, pt_suggester);
+    // LEGACY path: Assume codecs have PTs.
+    // If current content exists and is not being recycled, use its codecs.
+    if (current_content && current_content->mid() == mid &&
+        IsMediaContentOfType(current_content, media_description_options.type)) {
+      RTCErrorOr<CodecList> checked_codec_list =
+          CodecList::Create(current_content->media_description()->codecs());
+      if (!checked_codec_list.ok()) {
+        return checked_codec_list.MoveError();
+      }
+      MergeCodecsLegacy(checked_codec_list.value(), mid, codecs, pt_suggester);
+    }
+    // Add all our supported codecs
+    if (media_description_options.type == MediaType::AUDIO) {
+      MergeCodecsLegacy(audio_send_codecs_.codecs(), mid, codecs, pt_suggester);
+      MergeCodecsLegacy(audio_recv_codecs_.codecs(), mid, codecs, pt_suggester);
+    } else {
+      MergeCodecsLegacy(video_send_codecs_.codecs(), mid, codecs, pt_suggester);
+      MergeCodecsLegacy(video_recv_codecs_.codecs(), mid, codecs, pt_suggester);
+    }
   }
+
   CodecList filtered_codecs;
   CodecList negotiated_codecs;
   if (media_description_options.codecs_to_include.empty()) {
-    const CodecList& supported_codecs =
-        media_description_options.type == MediaType::AUDIO
-            ? GetAudioCodecsForAnswer(offer_rtd, answer_rtd)
-            : GetVideoCodecsForAnswer(offer_rtd, answer_rtd);
+    CodecList supported_codecs;
+    if (payload_types_in_transport_) {
+      RtpTransceiverDirection direction_to_use = answer_rtd;
+      if (answer_rtd == RtpTransceiverDirection::kSendRecv ||
+          answer_rtd == RtpTransceiverDirection::kStopped ||
+          answer_rtd == RtpTransceiverDirection::kInactive) {
+        direction_to_use = RtpTransceiverDirectionReversed(offer_rtd);
+      }
+      MergeCodecsByDirection(media_description_options.type, direction_to_use,
+                             mid, supported_codecs, pt_suggester,
+                             /*pick_from_top_of_range=*/false);
+    } else {
+      supported_codecs = media_description_options.type == MediaType::AUDIO
+                             ? GetAudioCodecsForAnswer(offer_rtd, answer_rtd)
+                             : GetVideoCodecsForAnswer(offer_rtd, answer_rtd);
+    }
     if (!media_description_options.codec_preferences.empty()) {
+      // Add the codecs from the current transceiver's codec preferences.
+      // They override any existing codecs from previous negotiations.
       filtered_codecs =
           MatchCodecPreference(media_description_options.codec_preferences,
                                codecs, supported_codecs);
@@ -788,26 +1140,32 @@
       // Add the codecs from current content if it exists and is not rejected
       // nor recycled.
       if (current_content && !current_content->rejected &&
-          current_content->mid() == media_description_options.mid) {
+          current_content->mid() == mid) {
         if (!IsMediaContentOfType(current_content,
                                   media_description_options.type)) {
           // Can happen if the remote side re-uses a MID while recycling.
           return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR)
                            << "Media type for content with mid='"
                            << current_content->mid()
-                           << "' does not match previous type.");
+                           << "' does not match expected type.");
         }
         const MediaContentDescription* mcd =
             current_content->media_description();
         for (const Codec& codec : mcd->codecs()) {
-          if (std::optional<Codec> found_codec =
-                  FindMatchingCodec(mcd->codecs(), codecs.codecs(), codec)) {
-            filtered_codecs.push_back(*found_codec);
+          if (FindMatchingCodec(mcd->codecs(), codecs.codecs(), codec)) {
+            filtered_codecs.push_back(codec);
           }
         }
       }
-      // Merge other_codecs into filtered_codecs, resolving PT conflicts.
-      MergeCodecs(supported_codecs, mid, filtered_codecs, pt_suggester);
+      if (payload_types_in_transport_) {
+        // Redesign path: Use configurations to merge supported codecs.
+        MergeCodecsByDirection(media_description_options.type, answer_rtd, mid,
+                               filtered_codecs, pt_suggester,
+                               /*pick_from_top_of_range=*/false);
+      } else {
+        // Merge other_codecs into filtered_codecs, resolving PT conflicts.
+        MergeCodecsLegacy(supported_codecs, mid, filtered_codecs, pt_suggester);
+      }
     }
 
     if (media_description_options.type == MediaType::AUDIO &&
@@ -840,8 +1198,13 @@
     }
     negotiated_codecs = codecs_from_arg.MoveValue();
   }
-  AssignCodecIdsAndLinkRed(&pt_suggester, media_description_options.mid,
-                           negotiated_codecs.writable_codecs());
+  if (payload_types_in_transport_) {
+    AssignCodecIdsAndLinkRedRefactored(pt_suggester, mid,
+                                       negotiated_codecs.writable_codecs());
+  } else {
+    RecordCodecIdsAndLinkRed(pt_suggester, mid,
+                             negotiated_codecs.writable_codecs());
+  }
   return negotiated_codecs.codecs();
 }
 
@@ -858,7 +1221,8 @@
 CodecVendor::CodecVendor(const MediaEngineInterface* media_engine,
                          bool rtx_enabled,
                          const FieldTrialsView& trials)
-    : audio_send_codecs_(InitTypedCodecVendor(media_engine,
+    : trials_(trials),
+      audio_send_codecs_(InitTypedCodecVendor(media_engine,
                                               MediaType::AUDIO,
                                               /*is_sender=*/true,
                                               rtx_enabled,
@@ -868,6 +1232,8 @@
                                               /*is_sender=*/false,
                                               rtx_enabled,
                                               trials)),
+      payload_types_in_transport_(
+          trials.IsEnabled("WebRTC-PayloadTypesInTransport")),
       video_send_codecs_(InitTypedCodecVendor(media_engine,
                                               MediaType::VIDEO,
                                               /*is_sender=*/true,
diff --git a/pc/codec_vendor.h b/pc/codec_vendor.h
index a2ad1c4..a78e0c5 100644
--- a/pc/codec_vendor.h
+++ b/pc/codec_vendor.h
@@ -17,6 +17,7 @@
 #include "absl/base/nullability.h"
 #include "absl/strings/string_view.h"
 #include "api/field_trials_view.h"
+#include "api/media_types.h"
 #include "api/rtc_error.h"
 #include "api/rtp_transceiver_direction.h"
 #include "api/sequence_checker.h"
@@ -104,14 +105,26 @@
       const RtpTransceiverDirection& offer,
       const RtpTransceiverDirection& answer) const;
 
+  RTCError MergeCodecsByDirection(MediaType type,
+                                  RtpTransceiverDirection direction,
+                                  absl::string_view mid,
+                                  CodecList& codecs_out,
+                                  PayloadTypeSuggester& pt_suggester,
+                                  bool pick_from_top_of_range,
+                                  bool favor_send_order = false);
+
   // Makes sure that modifications and reading data is done on the same thread
   // and to makessure we consistently make calls to GetNegotiatedCodecsForOffer
   // and GetNegotiatedCodecsForAnswer in the same calling context.
   RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
 
+  const FieldTrialsView& trials_;
+
   const TypedCodecVendor audio_send_codecs_;
   const TypedCodecVendor audio_recv_codecs_;
 
+  const bool payload_types_in_transport_;
+
   // TODO: bugs.webrtc.org/412904801 - Make const. In order to be able to do
   // that, `ModifyVideoCodecs` needs to be removed. In the meantime, codec
   // information must be read and modified on the same task queue.
@@ -135,6 +148,7 @@
 
 // A helper function to merge codecs numbered in one PT numberspace
 // into a list numbered in another PT numberspace. Exposed for testing.
+// This function is only available for testing the legacy path.
 RTCError MergeCodecsForTesting(const CodecList& reference_codecs,
                                absl::string_view mid,
                                CodecList& offered_codecs,
diff --git a/pc/codec_vendor_redesign_unittest.cc b/pc/codec_vendor_redesign_unittest.cc
index 6f6bba8..b58aa38 100644
--- a/pc/codec_vendor_redesign_unittest.cc
+++ b/pc/codec_vendor_redesign_unittest.cc
@@ -107,8 +107,7 @@
   EXPECT_EQ(fmtp, absl::StrCat(opus_it->id, "/", opus_it->id));
 }
 
-TEST_F(CodecVendorRedesignTest,
-       DISABLED_VideoOfferIncludesRtxAndRedAndAssignsIds) {
+TEST_F(CodecVendorRedesignTest, VideoOfferIncludesRtxAndRedAndAssignsIds) {
   MediaDescriptionOptions options(MediaType::VIDEO, "video",
                                   RtpTransceiverDirection::kSendRecv,
                                   /*stopped=*/false);
diff --git a/pc/codec_vendor_unittest.cc b/pc/codec_vendor_unittest.cc
index e4f973b..d86306d 100644
--- a/pc/codec_vendor_unittest.cc
+++ b/pc/codec_vendor_unittest.cc
@@ -32,6 +32,7 @@
 #include "pc/rtp_parameters_conversion.h"
 #include "pc/session_description.h"
 #include "rtc_base/checks.h"
+#include "test/create_test_field_trials.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
 
@@ -133,9 +134,10 @@
   media_engine.SetAudioRecvCodecs(no_codecs.codecs());
   {
     CodecVendor codec_vendor(&media_engine, false, trials);
-    EXPECT_EQ(no_codecs, codec_vendor.audio_send_codecs());
-    EXPECT_EQ(no_codecs, codec_vendor.audio_recv_codecs());
-    EXPECT_EQ(no_codecs, codec_vendor.audio_sendrecv_codecs());
+    EXPECT_EQ(no_codecs.codecs(), codec_vendor.audio_send_codecs().codecs());
+    EXPECT_EQ(no_codecs.codecs(), codec_vendor.audio_recv_codecs().codecs());
+    EXPECT_EQ(no_codecs.codecs(),
+              codec_vendor.audio_sendrecv_codecs().codecs());
   }
 }
 
@@ -266,6 +268,9 @@
 }
 
 TEST(CodecVendorMergeTest, BasicTestSetup) {
+  if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) {
+    GTEST_SKIP();
+  }
   CodecList reference_codecs;
   const std::string mid = "mid";
   CodecList merged_codecs;
@@ -276,6 +281,9 @@
 }
 
 TEST(CodecVendorMergeTest, IdenticalListsMergeWithNoChange) {
+  if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) {
+    GTEST_SKIP();
+  }
   CodecList reference_codecs;
   const std::string mid = "mid";
   CodecList merged_codecs;
@@ -293,6 +301,9 @@
 }
 
 TEST(CodecVendorMergeTest, MergeRenumbersAdditionalCodecs) {
+  if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) {
+    GTEST_SKIP();
+  }
   CodecList reference_codecs;
   const std::string mid = "mid";
   CodecList merged_codecs;
@@ -323,6 +334,9 @@
 }
 
 TEST(CodecVendorMergeTest, MergeRenumbersRedCodecArgument) {
+  if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) {
+    GTEST_SKIP();
+  }
   CodecList reference_codecs;
   const std::string mid = "mid";
   CodecList merged_codecs;
@@ -350,6 +364,9 @@
 }
 
 TEST(CodecVendorMergeTest, MergeRenumbersRedCodecArgumentAndMerges) {
+  if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) {
+    GTEST_SKIP();
+  }
   CodecList reference_codecs;
   const std::string mid = "mid";
   CodecList merged_codecs;
@@ -380,6 +397,9 @@
 }
 
 TEST(CodecVendorMergeTest, MergeWithBrokenReferenceRedErrors) {
+  if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) {
+    GTEST_SKIP();
+  }
   CodecList reference_codecs;
   const std::string mid = "mid";
   CodecList merged_codecs;
@@ -398,6 +418,9 @@
 }
 
 TEST(CodecVendorMergeTest, MergeWithCollisionPicksFromTop) {
+  if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) {
+    GTEST_SKIP();
+  }
   CodecList reference_codecs;
   const std::string mid = "mid";
   CodecList merged_codecs;
diff --git a/pc/media_session_unittest.cc b/pc/media_session_unittest.cc
index f27d4c6..664c91a 100644
--- a/pc/media_session_unittest.cc
+++ b/pc/media_session_unittest.cc
@@ -6546,8 +6546,9 @@
   AttachSenderToMediaDescriptionOptions(
       kVideoMid, MediaType::VIDEO, kVideoTrack1, {kMediaStream1}, 1, &opts);
   std::vector<RtpCodecCapability> preferences;
-  for (const Codec& codec :
-       codec_lookup_helper_offerer_.GetCodecVendor()->video_recv_codecs()) {
+  for (const Codec& codec : codec_lookup_helper_offerer_.GetCodecVendor()
+                                ->video_recv_codecs()
+                                .codecs()) {
     preferences.push_back(ToRtpCodecCapability(codec));
   }
   opts.media_description_options[0].codec_preferences = preferences;
diff --git a/pc/typed_codec_vendor.cc b/pc/typed_codec_vendor.cc
index b0599de..20b2d66 100644
--- a/pc/typed_codec_vendor.cc
+++ b/pc/typed_codec_vendor.cc
@@ -179,7 +179,8 @@
 
 Codecs CodecsFromConfigurations(
     const std::vector<CodecConfiguration>& configurations,
-    MediaType type) {
+    MediaType type,
+    bool rtx_enabled) {
   Codecs out;
   flat_set<std::string> shared_added;
   for (const auto& config : configurations) {
@@ -195,7 +196,9 @@
       if (config.resiliency.red && shared_added.insert(kRedCodecName).second) {
         out.push_back(CreateVideoCodec(kRedCodecName));
         // Video RED also gets an RTX codec.
-        out.push_back(CreateVideoCodec(PayloadType::NotSet(), kRtxCodecName));
+        if (rtx_enabled) {
+          out.push_back(CreateVideoCodec(PayloadType::NotSet(), kRtxCodecName));
+        }
       }
       if (config.resiliency.ulpfec &&
           shared_added.insert(kUlpfecCodecName).second) {
@@ -248,7 +251,7 @@
           media_engine->video(), is_sender, rtx_enabled, trials);
     }
     codecs_ = CodecList::CreateFromTrustedData(
-        CodecsFromConfigurations(configurations_, type));
+        CodecsFromConfigurations(configurations_, type, rtx_enabled));
   } else {
     codecs_ = CodecList::CreateFromTrustedData(
         GetCodecs(media_engine, type, is_sender, rtx_enabled));