Payload Type Redesign: Progress and fixes

This CL contains progress on the payload type allocation redesign,
including:
- Updates to documentation in payload_type_redesign.md describing
  the current status and strategy.
- Fix in pc/codec_vendor.cc to use the actual transceiver
  direction in GetNegotiatedCodecsForOffer when the field trial is
  enabled, avoiding empty codec lists in receive-only offers.
- Corrected erroneous uses of pick_from_top_of_range flag
- Added a unit test in pc/codec_vendor_redesign_unittest.cc to
  verify this behavior.
- Updated a test in pc/media_session_unittest.cc to use a more
  flexible matcher.
- Formatting fixes in several files.

TAG=agy
CONV=23847d6d-386a-43da-9d94-d19ffc9cb9ec

Bug: webrtc:360058654
Change-Id: Idc5ac8b957a776e4d8da5ca97ee90b631f8b9db4
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/474363
Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47778}
diff --git a/api/payload_type.h b/api/payload_type.h
index fe401cb..84a2ddc 100644
--- a/api/payload_type.h
+++ b/api/payload_type.h
@@ -39,7 +39,7 @@
   // 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) {
+    if (pt < 0 || pt > kUpperDynamicRangeMax.value()) {
       return std::nullopt;
     }
     return PayloadType(pt);
@@ -47,14 +47,21 @@
   // 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); }
+
+  static const PayloadType kLowerDynamicRangeMin;
+  static const PayloadType kLowerDynamicRangeMax;
+  static const PayloadType kUpperDynamicRangeMin;
+  static const PayloadType kUpperDynamicRangeMax;
+
   bool Valid(bool rtcp_mux = false) const {
     // 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 && (value() > 63 && value() < 96)) {
+    if (rtcp_mux &&
+        (*this > kLowerDynamicRangeMax && *this < kUpperDynamicRangeMin)) {
       return false;
     }
-    return value() >= 0 && value() <= 127;
+    return *this >= 0 && *this <= kUpperDynamicRangeMax;
   }
   // Older interface to validity check.
   static bool IsValid(PayloadType id, bool rtcp_mux) {
@@ -62,6 +69,11 @@
   }
   bool IsSet() const { return value() >= 0; }
 
+  bool IsDynamic() const {
+    return (*this >= kLowerDynamicRangeMin && *this <= kLowerDynamicRangeMax) ||
+           (*this >= kUpperDynamicRangeMin && *this <= kUpperDynamicRangeMax);
+  }
+
  private:
   class Internal {};
   // Allow -1 for "NotSet"
@@ -72,6 +84,15 @@
   }
 };
 
+inline constexpr PayloadType PayloadType::kLowerDynamicRangeMin =
+    PayloadType(35);
+inline constexpr PayloadType PayloadType::kLowerDynamicRangeMax =
+    PayloadType(63);
+inline constexpr PayloadType PayloadType::kUpperDynamicRangeMin =
+    PayloadType(96);
+inline constexpr PayloadType PayloadType::kUpperDynamicRangeMax =
+    PayloadType(127);
+
 }  // namespace webrtc
 
 #endif  // API_PAYLOAD_TYPE_H_
diff --git a/call/payload_type_picker.cc b/call/payload_type_picker.cc
index 4930a68..ed62a00 100644
--- a/call/payload_type_picker.cc
+++ b/call/payload_type_picker.cc
@@ -214,11 +214,11 @@
     Codec codec,
     const PayloadTypeRecorder* excluder,
     bool pick_from_top_of_range) {
-  // Test compatibility: If the codec contains a PT, and it is free, use it.
-  // This saves having to rewrite tests that set the codec ID themselves.
-  // Codecs with unassigned IDs should have -1 as their id.
-  if (codec.id >= 0 && codec.id <= kLastDynamicPayloadTypeUpperRange &&
-      seen_payload_types_.count(codec.id.value()) == 0) {
+  // Test compatibility: If the codec contains a PT, and it is free and valid,
+  // use it. This saves having to rewrite tests that set the codec ID
+  // themselves. Unassigned IDs will have id.IsSet() = false.
+  if (codec.id.IsSet() && codec.id.IsDynamic() &&
+      !seen_payload_types_.contains(codec.id)) {
     AddMapping(PayloadType(codec.id), codec);
     return PayloadType(codec.id);
   }
@@ -284,7 +284,7 @@
       !MatchesWithReferenceAttributes(codec, existing_codec_it->second)) {
     // Redefinition attempted.
     if (disallow_redefinition_level_ > 0) {
-      if (accepted_definitions_.count(payload_type) > 0) {
+      if (accepted_definitions_.contains(payload_type)) {
         // We have already defined this PT in this scope.
         RTC_LOG(LS_WARNING)
             << "Rejected attempt to redefine mapping for PT " << payload_type
@@ -428,7 +428,7 @@
 
   // Test compatibility: If preferred_id is provided and free, use it.
   if (preferred_id >= 1 && preferred_id <= 255 &&
-      seen_ids_.count(preferred_id) == 0) {
+      !seen_ids_.contains(preferred_id)) {
     if (preferred_id <= 14) {
       AddMapping(preferred_id, uri, encrypt);
       return preferred_id;
@@ -446,7 +446,7 @@
   // One-byte range: 1-14.
   // We prefer to allocate from the top of the range (14 down to 1).
   for (int id = 14; id >= 1; --id) {
-    if (seen_ids_.count(id) == 0) {
+    if (!seen_ids_.contains(id)) {
       AddMapping(id, uri, encrypt);
       return id;
     }
@@ -456,7 +456,7 @@
     // TODO: issues.webrtc.org/334925828 - add unit tests for this case.
     // Two-byte range: 16-255. (Avoid 15, which is special in RFC 8285)
     for (int id = 16; id <= 255; ++id) {
-      if (seen_ids_.count(id) == 0) {
+      if (!seen_ids_.contains(id)) {
         AddMapping(id, uri, encrypt);
         return id;
       }
diff --git a/g3doc/todo/payload_type_redesign.md b/g3doc/todo/payload_type_redesign.md
index 0042e41..0810674 100644
--- a/g3doc/todo/payload_type_redesign.md
+++ b/g3doc/todo/payload_type_redesign.md
@@ -118,94 +118,68 @@
 
 ## Current implementation status
 
-The new strategy is implemented for audio codecs and is being enabled for video
-codecs. Several issues that caused test failures when enabling the
-`WebRTC-PayloadTypesInTransport` field trial have been identified and fixed:
+The redesign is now largely implemented for both audio and video codecs when the
+`WebRTC-PayloadTypesInTransport` field trial is enabled. Key milestones reached:
 
-- **Audio/Video RED Collision:** RED codecs of different media types were
-  incorrectly matching, leading to payload type conflicts.
-  `MatchesWithCodecRules` now enforces media type equality.
-- **MID Recycling:** When a MID is recycled, it must preserve its media type
-  (e.g., Audio stays Audio). `CodecVendor` now correctly identifies and returns
-  an `INTERNAL_ERROR` if a MID is reused for a different media type, preventing
-  invalid codec merging.
-- **RED Matching Logic:** Relaxed the matching rules for RED to allow
-  negotiation to proceed even when parameters (linking RED to primary codecs)
-  are not yet populated, as this linking now happens late in the `CodecVendor`.
-- **RTX PT Convention:** RTX payload types now follow the conventional
-  `Primary_PT + 1` rule where possible.
+- **Bifurcated Negotiation Logic:** `CodecVendor` now has separate paths for
+  legacy and redesigned PT allocation. The redesigned path uses
+  `CodecConfiguration` and `MergeCodecsFromConfigurations` for all media types.
+- **Unified Resiliency Expansion:** Late expansion of RTX, RED, ULPFEC, and
+  FlexFEC is handled uniformly in `pc/codec_vendor.cc`.
+- **Audio/Video RED Collision:** Fixed by enforcing media type equality in
+  matching rules.
+- **MID Recycling:** Correctly handled with media type validation, preventing
+  invalid codec merging when MIDs are reused.
+- **Stable PT Assignment:** Verified to maintain payload type stability across
+  renegotiations and codec preference changes.
+- **Conventional RTX Assignment:** RTX PTs now default to `Primary_PT + 1` to
+  maintain backwards compatibility with legacy expectations.
 
-The new strategy is now mostly implemented for video codecs, including support
-for RTX, RED, ULPFEC, and FlexFEC late assignment.
+The implementation is verified by a dedicated suite of integration tests in
+`pc/codec_vendor_redesign_unittest.cc`.
 
 ## Unified Implementation Strategy for Audio and Video
 
-The goal is to transition audio and video codec handling to a unified
-late-assignment model using a new internal representation to handle resiliency
-mechanisms. This also involves refactoring the existing partial late assignment
-implementation for audio.
+The transition to a unified late-assignment model is nearly complete, using
+internal `CodecConfiguration` objects to represent codecs before they are
+assigned payload types.
 
 ### 1. CodecConfiguration and ResiliencyInfo
 
-To support late assignment without modifying the global `webrtc::Codec` class, a
-new internal representation `CodecConfiguration` will be introduced in the `pc/`
-directory.
+Introduced in `pc/codec_configuration.h`:
 
-- **`ResiliencyInfo`**: Encapsulates the redundancy requirements for a codec
-  (e.g., RTX, RED, ULPFEC, FlexFEC). It supports combined requirements (RED +
-  ULPFEC) and identifies whether a mechanism is shared across the media section.
-- **`CodecConfiguration`**: Stores codec attributes (excluding payload type) and
-  the associated `ResiliencyInfo`. This is the primary representation used
-  during capability gathering and the initial stages of negotiation.
+- **`ResiliencyInfo`**: Encapsulates the redundancy requirements (RTX, RED,
+  ULPFEC, FlexFEC).
+- **`CodecConfiguration`**: Stores codec attributes and their associated
+  `ResiliencyInfo`. This allows the engine to express capabilities without
+  pre-assigning payload types.
 
-### 2. Unified Codec Collection with Bifurcated Paths
+### 2. Unified Codec Collection
 
-- `TypedCodecVendor` will be updated to store either a legacy `CodecList` or a
-  collection of `CodecConfiguration` objects for audio and video, depending on
-  the `WebRTC-PayloadTypesInTransport` field trial.
-- When the trial is active:
-  - **Audio**: `CollectAudioCodecs` will be refactored to return
-    `CodecConfiguration` objects. Media codecs like Opus will be tagged with a
-    shared RED requirement.
-  - **Video**: `CollectVideoCodecs` and `VideoCodecsFromFactory` will populate
-    `CodecConfiguration` objects, tagging media codecs with their required
-    resiliency (e.g., VP8 gets RTX; all video codecs get shared RED and
-    FlexFEC).
-- When the trial is inactive, legacy methods will be used to ensure zero
-  behavior change.
+`TypedCodecVendor` handles the bifurcated collection path:
 
-### 3. Late Expansion and Unified Parameter Linking
+- **Redesigned Path**: Collects `CodecConfiguration` objects from the media
+  engine factories. It also performs a "legacy expansion" to populate the
+  internal `codecs()` list for compatibility with existing code that expects
+  pre-assigned PTs.
+- **Legacy Path**: Continues to use the engine's `LegacySendCodecs` /
+  `LegacyRecvCodecs` methods.
 
-`CodecVendor` will bifurcate its negotiation logic:
+### 3. Late Expansion and Parameter Linking
 
-- **Legacy Path**: Continues to use the existing `MergeCodecs` logic with
-  pre-assigned payload types.
-- **Late Assignment Path**: Uses a new `MergeCodecsFromConfigurations` function
-  for all media types that:
-  1. Assigns a payload type to the primary media codec via
-     `SdpPayloadTypeSuggester`.
-  2. Expands the `ResiliencyInfo` into one or more redundancy `Codec` objects.
-  3. Links these redundancy codecs to the primary codec's payload type (e.g.,
-     setting the `apt` parameter for RTX, or updating RED's FMTP with the
-     primary PT).
-  4. Assigns payload types to the redundancy codecs, following conventional
-     rules where possible (e.g., `RTX_PT = Primary_PT + 1`).
+`CodecVendor::MergeCodecsFromConfigurations` performs the following for all
+media types:
 
-This unified strategy removes the need for media-specific hacks (like the
-current manual RED linking for audio) and ensures that all redundancy codecs are
-correctly linked only after the primary payload types are known, while strictly
-preserving legacy behavior when the field trial is disabled.
+1.  Assigns a payload type to the primary media codec via
+    `SdpPayloadTypeSuggester`.
+2.  Expands the `ResiliencyInfo` into redundancy `Codec` objects (RTX, RED,
+    FEC).
+3.  Links redundancy codecs to the primary PT (e.g., setting the `apt` parameter
+    for RTX).
 
-### 4. Verification and Testing
-
-- **Integration Tests:** Enable the `WebRTC-PayloadTypesInTransport` trial in
-  `peerconnection_unittests` and `rtc_unittests` to identify any video-specific
-  regressions.
-- **Stable PT Tests:** Add coverage to ensure that payload types remain stable
-  across renegotiations, even when the order of codecs in the transceiver
-  preferences changes.
-- **MID Recycling:** Verify that MID recycling (within the same media type)
-  works correctly without PT collisions or crashes.
+**Current Status:** RTX linking is fully unified. RED linking for audio still
+partially relies on a legacy `LinkRed` helper, which will be refactored into the
+unified expansion logic in a future step.
 
 ## Testing Strategy
 
@@ -213,41 +187,21 @@
 `WebRTC-PayloadTypesInTransport` field trial is being developed, a "Redesign
 Feedback Loop" strategy is used:
 
-1. **Identify failing tests** Run the tests for this CL with the flag
-   "force-fieldtrials='WebRTC-PayloadTypesInTransport/Enable'". When using this
-   with `gtest-parallel`, two dashes must be inserted before the extra argument.
-2. **Reproduction and Isolation**: When a failure is identified in step 1, the
-   specific test case is cloned or ported into a specialized integration test
-   file (`pc/codec_vendor_redesign_unittest.cc`) on the implementation branch.
-   This allows for focused debugging and ensures the failure is reproducible in
-   a clean environment with the trial explicitly enabled.
-3. **Surgical Fixes**: Fixes are developed and verified using the isolated
-   tests.
-4. **Full Re-verification**: Once the tests are stable, run all tests without
-   the field trial flag to ensure there are no regressions, and then either ask
-   to commit this set of changes or loop back to step 1.
-
-To ensure that no unit tests are missed, a "canary branch" approach is used.
-
-1. **Canary Branch (`pt-enable`)**: Maintain a branch where the field trial is
-   forced enabled by default. This branch is used to run the full WebRTC test
-   suite (especially `rtc_pc_unittests` and `peerconnection_unittests`) to
-   identify all edge cases and legacy behaviors that the redesign logic doesn't
-   yet handle.
+1.  **Identify failing tests:** Run full suites (`rtc_unittests`,
+    `peerconnection_unittests`) with the trial enabled.
+2.  **Reproduction and Isolation:** Failing cases are ported to
+    `pc/codec_vendor_redesign_unittest.cc` for focused debugging.
+3.  **Surgical Fixes:** Fixes are verified against the isolated tests and then
+    re-verified against the full suite.
+4.  **Full Re-verification:** Once the tests are stable, run all tests without
+    the field trial flag to ensure there are no regressions, and then either ask
+    to commit this set of changes or loop back to step 1.
 
 ## Backwards Compatibility for Unit Testing
 
-Many legacy unit tests (e.g., in `MediaSessionDescriptionFactoryTest`) have
-hardcoded expectations for payload type assignments. The redesigned PT
-allocation logic, which performs late assignment and respects established
-transport mappings, may assign different PTs than the old fixed-list strategy
-used by the engines.
+Test helpers like `CodecLookupHelperForTesting` are used in legacy unit tests
+to "pre-seed" the `FakePayloadTypeSuggester` with hardcoded PT expectations.
+This allows tests that depend on specific PT values to pass while the
+underlying allocation logic transitions to a more generic, transport-aware
+strategy.
 
-To maintain test stability without embedding legacy expectations in the
-production `CodecVendor` or `TypedCodecVendor`, a test-only "pre-seeding"
-mechanism is used. Test helpers like `CodecLookupHelperForTesting` are updated
-to harvest the hardcoded PTs from the test-configured codec lists and register
-them as local mappings in the `FakePayloadTypeSuggester` for the default audio
-and video MIDs. This ensures that when the `CodecVendor` requests a PT for a
-codec, the suggester returns the value the test expects, while the core
-allocation logic remains clean and generic.
diff --git a/media/base/codec_comparators.cc b/media/base/codec_comparators.cc
index 859cf43..c89ea5a 100644
--- a/media/base/codec_comparators.cc
+++ b/media/base/codec_comparators.cc
@@ -271,26 +271,22 @@
   // Match the codec id/name based on the typical static/dynamic name rules.
   // Matching is case-insensitive.
 
-  // We support the ranges [96, 127] and more recently [35, 65].
+  // We support the ranges [96, 127] and more recently [35, 63].
   // https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-1
   // Within those ranges we match by codec name, outside by codec id.
   // We also match by name if either ID is unassigned.
   // Since no codecs are assigned an id in the range [66, 95] by us, these will
   // never match.
-  const int kLowerDynamicRangeMin = 35;
-  const int kLowerDynamicRangeMax = 65;
-  const int kUpperDynamicRangeMin = 96;
-  const int kUpperDynamicRangeMax = 127;
   const bool is_id_in_dynamic_range =
-      (left_codec.id >= kLowerDynamicRangeMin &&
-       left_codec.id <= kLowerDynamicRangeMax) ||
-      (left_codec.id >= kUpperDynamicRangeMin &&
-       left_codec.id <= kUpperDynamicRangeMax);
+      (left_codec.id >= PayloadType::kLowerDynamicRangeMin &&
+       left_codec.id <= PayloadType::kLowerDynamicRangeMax) ||
+      (left_codec.id >= PayloadType::kUpperDynamicRangeMin &&
+       left_codec.id <= PayloadType::kUpperDynamicRangeMax);
   const bool is_codec_id_in_dynamic_range =
-      (right_codec.id >= kLowerDynamicRangeMin &&
-       right_codec.id <= kLowerDynamicRangeMax) ||
-      (right_codec.id >= kUpperDynamicRangeMin &&
-       right_codec.id <= kUpperDynamicRangeMax);
+      (right_codec.id >= PayloadType::kLowerDynamicRangeMin &&
+       right_codec.id <= PayloadType::kLowerDynamicRangeMax) ||
+      (right_codec.id >= PayloadType::kUpperDynamicRangeMin &&
+       right_codec.id <= PayloadType::kUpperDynamicRangeMax);
 
   if (left_codec.type != right_codec.type) {
     return false;
diff --git a/media/base/codec_comparators_unittest.cc b/media/base/codec_comparators_unittest.cc
index 0487656..575323c 100644
--- a/media/base/codec_comparators_unittest.cc
+++ b/media/base/codec_comparators_unittest.cc
@@ -414,7 +414,8 @@
   EXPECT_TRUE(c1.Matches(CreateAudioCodec(97, "a", 44100, 0)));
   EXPECT_TRUE(c1.Matches(CreateAudioCodec(35, "a", 44100, 0)));
   EXPECT_TRUE(c1.Matches(CreateAudioCodec(42, "a", 44100, 0)));
-  EXPECT_TRUE(c1.Matches(CreateAudioCodec(65, "a", 44100, 0)));
+  EXPECT_TRUE(c1.Matches(CreateAudioCodec(63, "a", 44100, 0)));
+  EXPECT_FALSE(c1.Matches(CreateAudioCodec(64, "a", 44100, 0)));
   EXPECT_FALSE(c1.Matches(CreateAudioCodec(95, "A", 44100, 0)));
   EXPECT_FALSE(c1.Matches(CreateAudioCodec(34, "A", 44100, 0)));
   EXPECT_FALSE(c1.Matches(CreateAudioCodec(96, "", 44100, 2)));
@@ -467,7 +468,8 @@
   EXPECT_TRUE(c1.Matches(CreateVideoCodec(97, "v")));
   EXPECT_TRUE(c1.Matches(CreateVideoCodec(35, "v")));
   EXPECT_TRUE(c1.Matches(CreateVideoCodec(42, "v")));
-  EXPECT_TRUE(c1.Matches(CreateVideoCodec(65, "v")));
+  EXPECT_TRUE(c1.Matches(CreateVideoCodec(63, "v")));
+  EXPECT_FALSE(c1.Matches(CreateVideoCodec(64, "v")));
   EXPECT_FALSE(c1.Matches(CreateVideoCodec(96, "")));
   EXPECT_FALSE(c1.Matches(CreateVideoCodec(95, "V")));
   EXPECT_FALSE(c1.Matches(CreateVideoCodec(34, "V")));
diff --git a/pc/codec_vendor.cc b/pc/codec_vendor.cc
index 4512b97..cbea16b 100644
--- a/pc/codec_vendor.cc
+++ b/pc/codec_vendor.cc
@@ -197,10 +197,10 @@
                     : CreateVideoCodec(PayloadType::NotSet(), kRtxCodecName);
     rtx.SetParam(kCodecParamAssociatedPayloadType, primary_codec.id.value());
     // Convention: RTX PT = primary PT + 1.
-    // Suggester will ignore this if it is already in use.
-    int preferred_id = primary_codec.id.value() + 1;
-    if (preferred_id <= 127) {
-      rtx.id = PayloadType(preferred_id);
+    // Suggester will ignore this if it is already in use or invalid.
+    PayloadType preferred_id = PayloadType(primary_codec.id.value() + 1);
+    if (preferred_id.Valid(/*rtcp_mux=*/true)) {
+      rtx.id = preferred_id;
     }
     RTCErrorOr<PayloadType> result =
         pt_suggester.SuggestPayloadType(mid, rtx, pick_from_top_of_range);
@@ -933,8 +933,8 @@
       }
     }
     MergeCodecsByDirection(media_description_options.type,
-                           RtpTransceiverDirection::kSendRecv, mid, codecs,
-                           pt_suggester, /*pick_from_top_of_range=*/true);
+                           media_description_options.direction, mid, codecs,
+                           pt_suggester, /*pick_from_top_of_range=*/false);
   } else {
     // LEGACY path: Assume codecs have PTs.
     // If current content exists and is not being recycled, use its codecs.
@@ -1062,7 +1062,7 @@
   if (payload_types_in_transport_) {
     AssignCodecIdsAndLinkRedRefactored(pt_suggester, mid,
                                        filtered_codecs.writable_codecs(),
-                                       /*pick_from_top_of_range=*/true);
+                                       /*pick_from_top_of_range=*/false);
   } else {
     RecordCodecIdsAndLinkRed(pt_suggester, mid,
                              filtered_codecs.writable_codecs());
diff --git a/pc/codec_vendor_redesign_unittest.cc b/pc/codec_vendor_redesign_unittest.cc
index 05f7141..bfa1878 100644
--- a/pc/codec_vendor_redesign_unittest.cc
+++ b/pc/codec_vendor_redesign_unittest.cc
@@ -175,6 +175,24 @@
   EXPECT_THAT(codecs, Not(Contains(Field(&Codec::name, "red"))));
 }
 
+TEST_F(CodecVendorRedesignTest, VideoOfferWithRecvOnlyAndNoEncoderFactory) {
+  media_engine_.SetVideoSendCodecs({});
+  std::vector<Codec> video_codecs({
+      CreateVideoCodec(97, "vp8"),
+  });
+  media_engine_.SetVideoRecvCodecs(video_codecs);
+  vendor_ = std::make_unique<CodecVendor>(&media_engine_,
+                                          /*rtx_enabled=*/true, trials_);
+  MediaDescriptionOptions options(MediaType::VIDEO, "video",
+                                  RtpTransceiverDirection::kRecvOnly,
+                                  /*stopped=*/false);
+  auto result = vendor_->GetNegotiatedCodecsForOffer(
+      options, MediaSessionOptions(), /*current_content=*/nullptr,
+      pt_suggester_);
+  ASSERT_TRUE(result.ok());
+  EXPECT_THAT(result.value(), Contains(Field(&Codec::name, "vp8")));
+}
+
 TEST_F(CodecVendorRedesignTest, OfferMaintainsStableIds) {
   MediaDescriptionOptions options(MediaType::AUDIO, "audio",
                                   RtpTransceiverDirection::kSendRecv,
diff --git a/pc/media_session_unittest.cc b/pc/media_session_unittest.cc
index 8e27cbd..10fcb66 100644
--- a/pc/media_session_unittest.cc
+++ b/pc/media_session_unittest.cc
@@ -5619,10 +5619,10 @@
                                               offerer_recv_codecs);
   codec_lookup_helper_answerer_.SetVideoCodecs(answerer_send_codecs,
                                                answerer_recv_codecs);
-  EXPECT_EQ(offerer_sendrecv_codecs,
-            codec_lookup_helper_offerer_.GetCodecVendor()
-                ->video_sendrecv_codecs()
-                .codecs());
+  EXPECT_THAT(codec_lookup_helper_offerer_.GetCodecVendor()
+                  ->video_sendrecv_codecs()
+                  .codecs(),
+              CodecListsMatch(offerer_sendrecv_codecs, &env_.field_trials()));
 
   MediaSessionOptions opts;
   AddMediaDescriptionOptions(MediaType::VIDEO, kVideoMid,
diff --git a/pc/test/integration_test_helpers.cc b/pc/test/integration_test_helpers.cc
index bd7ca7c..3f44237 100644
--- a/pc/test/integration_test_helpers.cc
+++ b/pc/test/integration_test_helpers.cc
@@ -34,7 +34,6 @@
 #include "api/enable_media_with_defaults.h"
 #include "api/environment/environment.h"
 #include "api/environment/environment_factory.h"
-#include "api/field_trials.h"
 #include "api/jsep.h"
 #include "api/make_ref_counted.h"
 #include "api/media_stream_interface.h"
@@ -1260,7 +1259,7 @@
   auto it = field_trials_overrides_.find(debug_name);
   if (it != field_trials_overrides_.end()) {
     field_trials = it->second;
-    dependencies.trials = std::make_unique<FieldTrials>(it->second);
+    dependencies.trials = CreateTestFieldTrialsPtr(it->second);
   }
   env.Set(CreateTestFieldTrialsPtr(field_trials));