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));