| /* |
| * Copyright 2004 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "pc/codec_vendor.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "api/environment/environment.h" |
| #include "api/field_trials.h" |
| #include "api/media_types.h" |
| #include "api/payload_type.h" |
| #include "api/rtc_error.h" |
| #include "api/rtp_parameters.h" |
| #include "api/rtp_transceiver_direction.h" |
| #include "api/test/rtc_error_matchers.h" |
| #include "api/video_codecs/sdp_video_format.h" |
| #include "call/fake_payload_type_suggester.h" |
| #include "media/base/codec.h" |
| #include "media/base/codec_list.h" |
| #include "media/base/fake_media_engine.h" |
| #include "media/base/media_constants.h" |
| #include "media/base/test_utils.h" |
| #include "pc/media_options.h" |
| #include "pc/rtp_parameters_conversion.h" |
| #include "pc/session_description.h" |
| #include "pc/test/full_codec_matrix.h" |
| #include "rtc_base/checks.h" |
| #include "test/create_test_environment.h" |
| #include "test/create_test_field_trials.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| |
| namespace webrtc { |
| namespace { |
| |
| using ::testing::Contains; |
| using ::testing::ElementsAre; |
| using ::testing::Eq; |
| using ::testing::Field; |
| using ::testing::Ne; |
| using ::testing::Not; |
| using ::testing::Pair; |
| using ::testing::StrCaseEq; |
| |
| Codec CreateRedAudioCodec(absl::string_view encoding_id) { |
| Codec red = CreateAudioCodec(63, "red", 48000, 2); |
| red.SetParam(kCodecParamNotInNameValueFormat, |
| std::string(encoding_id) + '/' + std::string(encoding_id)); |
| return red; |
| } |
| |
| const Codec kAudioCodecs1[] = {CreateAudioCodec(111, "opus", 48000, 2), |
| CreateRedAudioCodec("111"), |
| CreateAudioCodec(102, "G722", 16000, 1), |
| CreateAudioCodec(0, "PCMU", 8000, 1), |
| CreateAudioCodec(8, "PCMA", 8000, 1), |
| CreateAudioCodec(107, "CN", 48000, 1)}; |
| |
| const Codec kAudioCodecs2[] = { |
| CreateAudioCodec(126, "foo", 16000, 1), |
| CreateAudioCodec(0, "PCMU", 8000, 1), |
| CreateAudioCodec(127, "G722", 16000, 1), |
| }; |
| |
| const Codec kAudioCodecsAnswer[] = { |
| CreateAudioCodec(102, "G722", 16000, 1), |
| CreateAudioCodec(0, "PCMU", 8000, 1), |
| }; |
| |
| // Occupies the dynamic payload type space except for the `free_payload_types` |
| // lowest payload types. A peer that supports many codecs has no payload types |
| // to spare for codecs that it does not put in the offer or answer, so tests |
| // that leave room for the expected codecs only fail if a codec that should be |
| // excluded is assigned a payload type. |
| void OccupyDynamicPayloadTypes(FakePayloadTypeSuggester& pt_suggester, |
| MediaType media_type, |
| int free_payload_types) { |
| int filler = 0; |
| int left_free = 0; |
| for (auto [first, last] : {std::pair{35, 63}, std::pair{96, 127}}) { |
| for (int pt = first; pt <= last; ++pt) { |
| if (left_free < free_payload_types) { |
| ++left_free; |
| continue; |
| } |
| std::string name = absl::StrCat("filler", filler++); |
| pt_suggester.AddLocalMapping("filler", PayloadType(pt), |
| media_type == MediaType::AUDIO |
| ? CreateAudioCodec(pt, name, 16000, 1) |
| : CreateVideoCodec(pt, name)); |
| } |
| } |
| } |
| |
| // Occupies the payload types in [`first`, `last`] with codecs that the media |
| // engine does not know about, as an endpoint whose peer has munged codecs of |
| // its own into the session description would have done. |
| void OccupyPayloadTypeRange(FakePayloadTypeSuggester& pt_suggester, |
| MediaType media_type, |
| int first, |
| int last) { |
| for (int pt = first; pt <= last; ++pt) { |
| std::string name = absl::StrCat("filler", pt); |
| pt_suggester.AddLocalMapping("filler", PayloadType(pt), |
| media_type == MediaType::AUDIO |
| ? CreateAudioCodec(pt, name, 16000, 1) |
| : CreateVideoCodec(pt, name)); |
| } |
| } |
| |
| TEST(CodecVendorTest, TestSetAudioCodecs) { |
| FieldTrials trials = |
| CreateTestFieldTrials("WebRTC-PayloadTypesInTransport/Disabled/"); |
| std::vector<Codec> send_codecs = MAKE_VECTOR(kAudioCodecs1); |
| std::vector<Codec> recv_codecs = MAKE_VECTOR(kAudioCodecs2); |
| |
| // The merged list of codecs should contain any send codecs that are also |
| // nominally in the receive codecs list. Payload types should be picked from |
| // the send codecs and a number-of-channels of 0 and 1 should be equivalent |
| // (set to 1). This equals what happens when the send codecs are used in an |
| // offer and the receive codecs are used in the following answer. |
| const std::vector<Codec> sendrecv_codecs = MAKE_VECTOR(kAudioCodecsAnswer); |
| RTC_CHECK_EQ(send_codecs[2].name, "G722") |
| << "Please don't change shared test data!"; |
| RTC_CHECK_EQ(recv_codecs[2].name, "G722") |
| << "Please don't change shared test data!"; |
| // Alter iLBC send codec to have zero channels, to test that that is handled |
| // properly. |
| send_codecs[2].channels = 0; |
| |
| // Alter PCMU receive codec to be lowercase, to test that case conversions |
| // are handled properly. |
| recv_codecs[1].name = "pcmu"; |
| |
| // Test proper merge |
| FakeMediaEngine media_engine; |
| media_engine.SetAudioSendCodecs(send_codecs); |
| media_engine.SetAudioRecvCodecs(recv_codecs); |
| { |
| CodecVendor codec_vendor(&media_engine, false, trials); |
| EXPECT_EQ(send_codecs, codec_vendor.audio_send_codecs().codecs()); |
| EXPECT_EQ(recv_codecs, codec_vendor.audio_recv_codecs().codecs()); |
| EXPECT_EQ(sendrecv_codecs, codec_vendor.audio_sendrecv_codecs().codecs()); |
| } |
| |
| // Test empty send codecs list |
| CodecList no_codecs; |
| media_engine.SetAudioSendCodecs(no_codecs.codecs()); |
| media_engine.SetAudioRecvCodecs(recv_codecs); |
| { |
| CodecVendor codec_vendor(&media_engine, false, trials); |
| EXPECT_EQ(no_codecs.codecs(), codec_vendor.audio_send_codecs().codecs()); |
| EXPECT_EQ(recv_codecs, codec_vendor.audio_recv_codecs().codecs()); |
| EXPECT_EQ(no_codecs.codecs(), |
| codec_vendor.audio_sendrecv_codecs().codecs()); |
| } |
| |
| // Test empty recv codecs list |
| media_engine.SetAudioSendCodecs(send_codecs); |
| media_engine.SetAudioRecvCodecs(no_codecs.codecs()); |
| { |
| CodecVendor codec_vendor(&media_engine, false, trials); |
| EXPECT_EQ(send_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()); |
| } |
| |
| // Test all empty codec lists |
| media_engine.SetAudioSendCodecs(no_codecs.codecs()); |
| media_engine.SetAudioRecvCodecs(no_codecs.codecs()); |
| { |
| CodecVendor codec_vendor(&media_engine, false, trials); |
| 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()); |
| } |
| } |
| |
| TEST(CodecVendorTest, VideoRtxIsIncludedWhenAskedFor) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Disabled/"}); |
| FakeMediaEngine media_engine; |
| std::vector<Codec> video_codecs({ |
| CreateVideoCodec(97, "vp8"), |
| CreateVideoRtxCodec(98, 97), |
| }); |
| media_engine.SetVideoSendCodecs(video_codecs); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ true, |
| env.field_trials()); |
| FakePayloadTypeSuggester pt_suggester; |
| RTCErrorOr<std::vector<Codec>> offered_codecs = |
| codec_vendor.GetNegotiatedCodecsForOffer( |
| MediaDescriptionOptions(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendOnly, false), |
| MediaSessionOptions(), nullptr, pt_suggester); |
| EXPECT_THAT(offered_codecs.value(), |
| Contains(Field("name", &Codec::name, "rtx"))); |
| } |
| |
| TEST(CodecVendorTest, VideoRtxIsExcludedWhenNotAskedFor) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Disabled/"}); |
| FakeMediaEngine media_engine; |
| std::vector<Codec> video_codecs({ |
| CreateVideoCodec(97, "vp8"), |
| CreateVideoRtxCodec(98, 97), |
| }); |
| media_engine.SetVideoSendCodecs(video_codecs); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env.field_trials()); |
| FakePayloadTypeSuggester pt_suggester; |
| RTCErrorOr<std::vector<Codec>> offered_codecs = |
| codec_vendor.GetNegotiatedCodecsForOffer( |
| MediaDescriptionOptions(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendOnly, false), |
| MediaSessionOptions(), nullptr, pt_suggester); |
| EXPECT_THAT(offered_codecs.value(), |
| Not(Contains(Field("name", &Codec::name, "rtx")))); |
| } |
| |
| TEST(CodecVendorTest, PreferencesAffectCodecChoice) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Disabled/"}); |
| FakeMediaEngine media_engine; |
| std::vector<Codec> video_codecs({ |
| CreateVideoCodec(97, "vp8"), |
| CreateVideoRtxCodec(98, 97), |
| CreateVideoCodec(99, "vp9"), |
| CreateVideoRtxCodec(100, 99), |
| }); |
| media_engine.SetVideoSendCodecs(video_codecs); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env.field_trials()); |
| MediaDescriptionOptions options(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendOnly, false); |
| options.codec_preferences = { |
| ToRtpCodecCapability(CreateVideoCodec(PayloadType::NotSet(), "vp9")), |
| }; |
| FakePayloadTypeSuggester pt_suggester; |
| |
| RTCErrorOr<std::vector<Codec>> offered_codecs = |
| codec_vendor.GetNegotiatedCodecsForOffer(options, MediaSessionOptions(), |
| nullptr, pt_suggester); |
| ASSERT_TRUE(offered_codecs.ok()); |
| EXPECT_THAT(offered_codecs.value(), |
| Contains(Field("name", &Codec::name, "vp9"))); |
| EXPECT_THAT(offered_codecs.value(), |
| Not(Contains(Field("name", &Codec::name, "vp8")))); |
| EXPECT_THAT(offered_codecs.value().size(), Eq(1)); |
| } |
| |
| // Regression test for a crash in re-offers: the codecs of the previous local |
| // description used to be added to the offer with the payload types they had |
| // then, while the remaining codecs were added with the payload types of the |
| // newly merged codec list. If a payload type had been reassigned to another |
| // codec in between, the two disagreed and the offer ended up with one payload |
| // type used by two codecs. |
| TEST(CodecVendorTest, ReOfferUsesPayloadTypesOfMergedCodecList) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Disabled/"}); |
| FakeMediaEngine media_engine; |
| std::vector<Codec> video_codecs({ |
| CreateVideoCodec(97, "vp8"), |
| CreateVideoCodec(99, "vp9"), |
| }); |
| media_engine.SetVideoSendCodecs(video_codecs); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env.field_trials()); |
| MediaDescriptionOptions options(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendOnly, false); |
| FakePayloadTypeSuggester pt_suggester; |
| // Record mappings where vp8 has moved to payload type 120, and the payload |
| // type it used to have (97) now belongs to vp9. |
| ASSERT_TRUE( |
| pt_suggester.AddLocalMapping("mid", 120, CreateVideoCodec(120, "vp8")) |
| .ok()); |
| ASSERT_TRUE( |
| pt_suggester.AddLocalMapping("mid", 97, CreateVideoCodec(97, "vp9")) |
| .ok()); |
| |
| // The previous local description still has vp8 on payload type 97. |
| auto video_description = std::make_unique<VideoContentDescription>(); |
| video_description->set_codecs({CreateVideoCodec(97, "vp8")}); |
| ContentInfo current_content(MediaProtocolType::kRtp, "mid", |
| std::move(video_description)); |
| |
| RTCErrorOr<std::vector<Codec>> offered_codecs = |
| codec_vendor.GetNegotiatedCodecsForOffer(options, MediaSessionOptions(), |
| ¤t_content, pt_suggester); |
| ASSERT_TRUE(offered_codecs.ok()); |
| // Both codecs are offered, each with the payload type of the merged codec |
| // list, so no payload type is used twice. |
| EXPECT_THAT(offered_codecs.value(), |
| ElementsAre(AllOf(Field("name", &Codec::name, "vp8"), |
| Field("id", &Codec::id, 120)), |
| AllOf(Field("name", &Codec::name, "vp9"), |
| Field("id", &Codec::id, 97)))); |
| } |
| |
| TEST(CodecVendorTest, GetNegotiatedCodecsForAnswerSimple) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Disabled/"}); |
| FakeMediaEngine media_engine; |
| std::vector<Codec> video_codecs({ |
| CreateVideoCodec(97, "vp8"), |
| CreateVideoRtxCodec(98, 97), |
| CreateVideoCodec(99, "vp9"), |
| CreateVideoRtxCodec(100, 99), |
| }); |
| media_engine.SetVideoSendCodecs(video_codecs); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ true, |
| env.field_trials()); |
| MediaDescriptionOptions options(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendOnly, false); |
| FakePayloadTypeSuggester pt_suggester; |
| ContentInfo* current_content = nullptr; |
| RTCErrorOr<std::vector<Codec>> answered_codecs = |
| codec_vendor.GetNegotiatedCodecsForAnswer( |
| options, MediaSessionOptions(), RtpTransceiverDirection::kSendOnly, |
| RtpTransceiverDirection::kSendOnly, current_content, video_codecs, |
| pt_suggester); |
| EXPECT_THAT(answered_codecs, IsRtcOkAndHolds(video_codecs)); |
| } |
| |
| TEST(CodecVendorTest, GetNegotiatedCodecsForAnswerWithCollision) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Disabled/"}); |
| FakeMediaEngine media_engine; |
| std::vector<Codec> video_codecs({ |
| CreateVideoCodec(97, "vp8"), |
| CreateVideoCodec(99, "vp9"), |
| CreateVideoCodec(101, "av1"), |
| }); |
| std::vector<Codec> remote_codecs({ |
| CreateVideoCodec(97, "av1"), |
| CreateVideoCodec(99, "vp9"), |
| }); |
| media_engine.SetVideoSendCodecs(video_codecs); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env.field_trials()); |
| MediaDescriptionOptions options(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendOnly, false); |
| FakePayloadTypeSuggester pt_suggester; |
| ContentInfo* current_content = nullptr; |
| RTCErrorOr<std::vector<Codec>> answered_codecs = |
| codec_vendor.GetNegotiatedCodecsForAnswer( |
| options, MediaSessionOptions(), RtpTransceiverDirection::kSendOnly, |
| RtpTransceiverDirection::kSendOnly, current_content, remote_codecs, |
| pt_suggester); |
| EXPECT_THAT(answered_codecs, IsRtcOkAndHolds(remote_codecs)); |
| } |
| |
| TEST(CodecVendorMergeTest, BasicTestSetup) { |
| if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) { |
| GTEST_SKIP(); |
| } |
| CodecList reference_codecs; |
| const std::string mid = "mid"; |
| CodecList merged_codecs; |
| FakePayloadTypeSuggester pt_suggester; |
| RTCError error = |
| MergeCodecsForTesting(reference_codecs, mid, merged_codecs, pt_suggester); |
| EXPECT_TRUE(error.ok()); |
| } |
| |
| TEST(CodecVendorMergeTest, IdenticalListsMergeWithNoChange) { |
| if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) { |
| GTEST_SKIP(); |
| } |
| CodecList reference_codecs; |
| const std::string mid = "mid"; |
| CodecList merged_codecs; |
| FakePayloadTypeSuggester pt_suggester; |
| Codec some_codec = CreateVideoCodec(97, "foo"); |
| RTCErrorOr<PayloadType> pt_or_error = |
| pt_suggester.SuggestPayloadType(mid, some_codec, false); |
| ASSERT_THAT(pt_or_error.value(), Eq(97)); |
| reference_codecs.push_back(some_codec); |
| merged_codecs.push_back(some_codec); |
| RTCError error = |
| MergeCodecsForTesting(reference_codecs, mid, merged_codecs, pt_suggester); |
| EXPECT_TRUE(error.ok()); |
| EXPECT_THAT(merged_codecs.size(), Eq(1)); |
| EXPECT_THAT(merged_codecs[0].id, Eq(97)); |
| } |
| |
| TEST(CodecVendorMergeTest, MergeRenumbersAdditionalCodecs) { |
| if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) { |
| GTEST_SKIP(); |
| } |
| CodecList reference_codecs; |
| const std::string mid = "mid"; |
| CodecList merged_codecs; |
| FakePayloadTypeSuggester pt_suggester; |
| Codec some_codec = CreateVideoCodec(97, "foo"); |
| RTCErrorOr<PayloadType> pt_or_error = |
| pt_suggester.SuggestPayloadType(mid, some_codec, false); |
| ASSERT_THAT(pt_or_error.value(), Eq(97)); |
| merged_codecs.push_back(some_codec); |
| // Use the same PT for a reference codec. This should be renumbered. |
| Codec some_other_codec = CreateVideoCodec(97, "bar"); |
| reference_codecs.push_back(some_other_codec); |
| RTCError error = |
| MergeCodecsForTesting(reference_codecs, mid, merged_codecs, pt_suggester); |
| EXPECT_TRUE(error.ok()); |
| EXPECT_THAT(merged_codecs.size(), Eq(2)); |
| // Both foo and bar should be present |
| EXPECT_THAT(merged_codecs.codecs(), |
| UnorderedElementsAre(Field("name", &Codec::name, "foo"), |
| Field("name", &Codec::name, "bar"))); |
| // Foo should retain 97 |
| EXPECT_THAT(merged_codecs.codecs(), |
| Contains(AllOf(Field("name", &Codec::name, "foo"), |
| Field("id", &Codec::id, 97)))); |
| // Bar should not have 97 |
| EXPECT_THAT(merged_codecs.codecs(), |
| Contains(AllOf(Field("name", &Codec::name, "bar"), |
| Field("id", &Codec::id, Ne(97))))); |
| } |
| |
| TEST(CodecVendorMergeTest, MergeRenumbersRedCodecArgument) { |
| if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) { |
| GTEST_SKIP(); |
| } |
| CodecList reference_codecs; |
| const std::string mid = "mid"; |
| CodecList merged_codecs; |
| FakePayloadTypeSuggester pt_suggester; |
| Codec some_codec = CreateAudioCodec(100, "foo", 8000, 1); |
| merged_codecs.push_back(some_codec); |
| // Push into "reference" with a different ID |
| some_codec.id = 102; |
| reference_codecs.push_back(some_codec); |
| Codec red_codec = CreateAudioCodec(101, "red", 8000, 1); |
| ASSERT_EQ(red_codec.GetResiliencyType(), Codec::ResiliencyType::kRed); |
| red_codec.SetParam(kCodecParamNotInNameValueFormat, "102/102"); |
| reference_codecs.push_back(red_codec); |
| // Merging should add the RED codec with parameter 100/100 |
| RTCError error = |
| MergeCodecsForTesting(reference_codecs, mid, merged_codecs, pt_suggester); |
| EXPECT_TRUE(error.ok()); |
| EXPECT_THAT(merged_codecs.size(), Eq(2)); |
| EXPECT_THAT( |
| merged_codecs.codecs(), |
| Contains(AllOf(Field("name", &Codec::name, "red"), |
| Field("params", &Codec::params, |
| ElementsAre(Pair(kCodecParamNotInNameValueFormat, |
| "100/100")))))); |
| } |
| |
| TEST(CodecVendorMergeTest, MergeRenumbersRedCodecArgumentAndMerges) { |
| if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) { |
| GTEST_SKIP(); |
| } |
| CodecList reference_codecs; |
| const std::string mid = "mid"; |
| CodecList merged_codecs; |
| FakePayloadTypeSuggester pt_suggester; |
| Codec some_codec = CreateAudioCodec(100, "foo", 8000, 1); |
| merged_codecs.push_back(some_codec); |
| // Push into "reference" with a different ID |
| some_codec.id = 102; |
| reference_codecs.push_back(some_codec); |
| Codec red_codec = CreateAudioCodec(101, "red", 8000, 1); |
| ASSERT_EQ(red_codec.GetResiliencyType(), Codec::ResiliencyType::kRed); |
| red_codec.SetParam(kCodecParamNotInNameValueFormat, "102/102"); |
| reference_codecs.push_back(red_codec); |
| // Push the same red codec into `merged_codecs` with the 100 id |
| red_codec.SetParam(kCodecParamNotInNameValueFormat, "100/100"); |
| merged_codecs.push_back(red_codec); |
| // Merging should note the duplication and not add another codec. |
| RTCError error = |
| MergeCodecsForTesting(reference_codecs, mid, merged_codecs, pt_suggester); |
| EXPECT_TRUE(error.ok()); |
| EXPECT_THAT(merged_codecs.size(), Eq(2)); |
| EXPECT_THAT( |
| merged_codecs.codecs(), |
| Contains(AllOf(Field("name", &Codec::name, "red"), |
| Field("params", &Codec::params, |
| ElementsAre(Pair(kCodecParamNotInNameValueFormat, |
| "100/100")))))); |
| } |
| |
| TEST(CodecVendorMergeTest, MergeWithBrokenReferenceRedErrors) { |
| if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) { |
| GTEST_SKIP(); |
| } |
| CodecList reference_codecs; |
| const std::string mid = "mid"; |
| CodecList merged_codecs; |
| FakePayloadTypeSuggester pt_suggester; |
| Codec some_codec = CreateAudioCodec(100, "foo", 8000, 1); |
| Codec red_codec = CreateAudioCodec(101, "red", 8000, 1); |
| // Adds a RED codec that refers to codec 102, which does not exist. |
| red_codec.SetParam(kCodecParamNotInNameValueFormat, "100/102"); |
| reference_codecs.push_back(some_codec); |
| reference_codecs.push_back(red_codec); |
| // The bogus RED codec should result in an error return. |
| RTCError error = |
| MergeCodecsForTesting(reference_codecs, mid, merged_codecs, pt_suggester); |
| EXPECT_FALSE(error.ok()); |
| EXPECT_THAT(error.type(), Eq(RTCErrorType::INTERNAL_ERROR)); |
| } |
| |
| TEST(CodecVendorMergeTest, MergeWithCollisionPicksFromTop) { |
| if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) { |
| GTEST_SKIP(); |
| } |
| CodecList reference_codecs; |
| const std::string mid = "mid"; |
| CodecList merged_codecs; |
| FakePayloadTypeSuggester pt_suggester; |
| // Existing codec with PT 97 |
| Codec some_codec = CreateVideoCodec(97, "foo"); |
| merged_codecs.push_back(some_codec); |
| RTC_CHECK(pt_suggester.AddLocalMapping(mid, 97, some_codec).ok()); |
| |
| // New codec in reference that also wants PT 97 |
| Codec some_other_codec = CreateVideoCodec(97, "bar"); |
| reference_codecs.push_back(some_other_codec); |
| |
| // When merging with pick_from_top_of_range = true, it should pick 127 |
| RTCError error = |
| MergeCodecsForTesting(reference_codecs, mid, merged_codecs, pt_suggester, |
| /*pick_from_top_of_range=*/true); |
| EXPECT_TRUE(error.ok()); |
| EXPECT_THAT(merged_codecs.size(), Eq(2)); |
| EXPECT_THAT(merged_codecs.codecs(), |
| Contains(AllOf(Field("name", &Codec::name, "bar"), |
| Field("id", &Codec::id, 127)))); |
| } |
| |
| #if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) |
| // A payload type conflict while merging means that codecs from different |
| // payload type mappings have been combined, which is an internal error rather |
| // than something an application can cause. It is fatal in debug builds; in |
| // release builds the conflicting codec is left out of the list. |
| TEST(CodecVendorMergeDeathTest, MergeAssertsOnPayloadTypeConflict) { |
| if (CreateTestFieldTrials().IsEnabled("WebRTC-PayloadTypesInTransport")) { |
| GTEST_SKIP(); |
| } |
| CodecList reference_codecs; |
| const std::string mid = "mid"; |
| CodecList merged_codecs; |
| FakePayloadTypeSuggester pt_suggester; |
| // Existing codec with PT 97. |
| Codec some_codec = CreateVideoCodec(97, "foo"); |
| merged_codecs.push_back(some_codec); |
| RTC_CHECK(pt_suggester.AddLocalMapping(mid, 97, some_codec).ok()); |
| |
| // Force the suggester to hand out the payload type of "foo" for "bar", so |
| // that the merge ends up with two codecs claiming the same payload type. |
| pt_suggester.SetSuggestion(mid, "bar", PayloadType(97)); |
| reference_codecs.push_back(CreateVideoCodec(97, "bar")); |
| |
| #if RTC_DCHECK_IS_ON |
| EXPECT_DEATH( |
| MergeCodecsForTesting(reference_codecs, mid, merged_codecs, pt_suggester) |
| .ok(), |
| "codec_vendor.cc"); |
| #else |
| RTCError error = |
| MergeCodecsForTesting(reference_codecs, mid, merged_codecs, pt_suggester); |
| EXPECT_TRUE(error.ok()); |
| // "bar" was not added to the list. |
| EXPECT_THAT(merged_codecs.size(), Eq(1)); |
| #endif |
| } |
| #endif |
| |
| TEST(CodecVendorTest, ModifyVideoCodecsReplacesCodec) { |
| FieldTrials trials = |
| CreateTestFieldTrials("WebRTC-PayloadTypesInTransport/Disabled/"); |
| FakeMediaEngine media_engine; |
| std::vector<Codec> video_codecs({ |
| CreateVideoCodec(97, "vp8"), |
| CreateVideoCodec(99, "vp9"), |
| }); |
| media_engine.SetVideoSendCodecs(video_codecs); |
| media_engine.SetVideoRecvCodecs(video_codecs); |
| |
| CodecVendor codec_vendor(&media_engine, false, trials); |
| |
| Codec original_codec = video_codecs[0]; |
| Codec modified_codec = original_codec; |
| modified_codec.name = "modified_name"; |
| |
| Codec second_codec = video_codecs[1]; |
| |
| std::vector<std::pair<Codec, Codec>> changes; |
| changes.push_back({original_codec, modified_codec}); |
| |
| codec_vendor.ModifyVideoCodecs(changes); |
| |
| const CodecList& new_send_codecs = codec_vendor.video_send_codecs(); |
| EXPECT_THAT(new_send_codecs.codecs(), Contains(modified_codec)); |
| EXPECT_THAT(new_send_codecs.codecs(), Not(Contains(original_codec))); |
| |
| // Check that the second codec is NOT changed. |
| EXPECT_THAT(new_send_codecs.codecs(), Contains(second_codec)); |
| } |
| |
| // Once every dynamic payload type is taken, offer creation must report the |
| // exhaustion to the caller. Silently omitting the codecs that could not be |
| // assigned a payload type produces an offer that mixes two payload type |
| // mappings, which is how a BUNDLE group ends up using one payload type for two |
| // different codecs. |
| TEST(CodecVendorTest, OfferFailsWhenPayloadTypeSpaceIsExhausted) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Disabled/"}); |
| FakeMediaEngine media_engine; |
| media_engine.SetVideoSendCodecs({CreateVideoCodec(97, "vp8")}); |
| media_engine.SetVideoRecvCodecs({CreateVideoCodec(97, "vp8")}); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env.field_trials()); |
| |
| // Occupy the whole dynamic payload type space with codecs that the media |
| // engine does not offer, so that no payload type is left for "vp8". |
| FakePayloadTypeSuggester pt_suggester; |
| OccupyDynamicPayloadTypes(pt_suggester, MediaType::VIDEO, |
| /*free_payload_types=*/0); |
| |
| RTCErrorOr<std::vector<Codec>> offered_codecs = |
| codec_vendor.GetNegotiatedCodecsForOffer( |
| MediaDescriptionOptions(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendOnly, false), |
| MediaSessionOptions(), nullptr, pt_suggester); |
| EXPECT_THAT(offered_codecs.error(), |
| IsRtcErrorWithType(RTCErrorType::RESOURCE_EXHAUSTED)); |
| } |
| |
| // Codecs that the direction of the media section rules out never reach the |
| // offer, so they must not be assigned a payload type either. A peer that |
| // supports many codecs has no payload types to spare for codecs it does not |
| // offer. |
| TEST(CodecVendorTest, OfferDoesNotAssignPayloadTypesToUnofferedCodecs) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Disabled/"}); |
| FakeMediaEngine media_engine; |
| media_engine.SetVideoSendCodecs({CreateVideoCodec(96, "vp8")}); |
| // A send only media section cannot offer the receive-only codecs. |
| media_engine.SetVideoRecvCodecs({CreateVideoCodec(96, "vp8"), |
| CreateVideoCodec(97, "vp9"), |
| CreateVideoCodec(98, "av1")}); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env.field_trials()); |
| |
| // Occupy the dynamic payload type space except for a single payload type, |
| // which leaves room for the one codec that the offer can contain. |
| FakePayloadTypeSuggester pt_suggester; |
| OccupyDynamicPayloadTypes(pt_suggester, MediaType::VIDEO, |
| /*free_payload_types=*/1); |
| |
| RTCErrorOr<std::vector<Codec>> offered_codecs = |
| codec_vendor.GetNegotiatedCodecsForOffer( |
| MediaDescriptionOptions(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendOnly, false), |
| MediaSessionOptions(), nullptr, pt_suggester); |
| ASSERT_TRUE(offered_codecs.ok()) << offered_codecs.error().message(); |
| EXPECT_THAT(offered_codecs.value(), |
| ElementsAre(Field(&Codec::name, Eq("vp8")))); |
| } |
| |
| // Runs a test both with the legacy payload type assignment and with the |
| // redesigned one that the WebRTC-PayloadTypesInTransport field trial enables. |
| class CodecVendorPayloadTypeTest : public ::testing::TestWithParam<bool> { |
| protected: |
| bool payload_types_in_transport() const { return GetParam(); } |
| |
| const Environment env_ = CreateTestEnvironment( |
| {.field_trials = absl::string_view( |
| GetParam() ? "WebRTC-PayloadTypesInTransport/Enabled/" |
| : "WebRTC-PayloadTypesInTransport/Disabled/")}); |
| }; |
| |
| // Codecs excluded by transceiver codec preferences must not be assigned a |
| // payload type when creating an offer. |
| TEST_P(CodecVendorPayloadTypeTest, |
| OfferDoesNotAssignPayloadTypesToExcludedCodecs) { |
| FakeMediaEngine media_engine; |
| // "av1" comes last, so it only gets a payload type if the codecs that the |
| // codec preferences exclude do not take it first. |
| media_engine.SetVideoCodecs({CreateVideoCodec(96, "vp8"), |
| CreateVideoCodec(97, "vp9"), |
| CreateVideoCodec(98, "av1")}); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env_.field_trials()); |
| |
| FakePayloadTypeSuggester pt_suggester; |
| OccupyDynamicPayloadTypes(pt_suggester, MediaType::VIDEO, |
| /*free_payload_types=*/1); |
| |
| MediaDescriptionOptions options(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendRecv, false); |
| RtpCodecCapability pref; |
| pref.name = "av1"; |
| pref.kind = MediaType::VIDEO; |
| pref.clock_rate = 90000; |
| options.codec_preferences = {pref}; |
| |
| RTCErrorOr<std::vector<Codec>> offered_codecs = |
| codec_vendor.GetNegotiatedCodecsForOffer(options, MediaSessionOptions(), |
| nullptr, pt_suggester); |
| ASSERT_TRUE(offered_codecs.ok()) << offered_codecs.error().message(); |
| EXPECT_THAT(offered_codecs.value(), |
| ElementsAre(Field(&Codec::name, Eq("av1")))); |
| } |
| |
| // RED codecs whose primary codecs are excluded by transceiver codec |
| // preferences must not be assigned a payload type when creating an offer. |
| TEST(CodecVendorTest, OfferDoesNotAssignPayloadTypesToDanglingRedCodecs) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Disabled/"}); |
| FakeMediaEngine media_engine; |
| media_engine.SetAudioCodecs({CreateAudioCodec(111, "opus", 48000, 2), |
| CreateRedAudioCodec("111"), |
| CreateAudioCodec(102, "G722", 16000, 1)}); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env.field_trials()); |
| |
| FakePayloadTypeSuggester pt_suggester; |
| OccupyDynamicPayloadTypes(pt_suggester, MediaType::AUDIO, |
| /*free_payload_types=*/1); |
| |
| MediaDescriptionOptions options(MediaType::AUDIO, "mid", |
| RtpTransceiverDirection::kSendRecv, false); |
| RtpCodecCapability red_pref; |
| red_pref.name = "red"; |
| red_pref.kind = MediaType::AUDIO; |
| red_pref.clock_rate = 48000; |
| red_pref.num_channels = 2; |
| RtpCodecCapability g722_pref; |
| g722_pref.name = "G722"; |
| g722_pref.kind = MediaType::AUDIO; |
| g722_pref.clock_rate = 16000; |
| g722_pref.num_channels = 1; |
| options.codec_preferences = {red_pref, g722_pref}; |
| |
| RTCErrorOr<std::vector<Codec>> offered_codecs = |
| codec_vendor.GetNegotiatedCodecsForOffer(options, MediaSessionOptions(), |
| nullptr, pt_suggester); |
| ASSERT_TRUE(offered_codecs.ok()) << offered_codecs.error().message(); |
| EXPECT_THAT(offered_codecs.value(), |
| ElementsAre(Field(&Codec::name, Eq("G722")))); |
| } |
| |
| // Codecs excluded by `codecs_to_include` must not be assigned a payload type |
| // when creating an offer. |
| TEST(CodecVendorTest, OfferRespectsCodecsToInclude) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Disabled/"}); |
| FakeMediaEngine media_engine; |
| media_engine.SetVideoCodecs({CreateVideoCodec(96, "vp8"), |
| CreateVideoCodec(97, "vp9"), |
| CreateVideoCodec(98, "av1")}); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env.field_trials()); |
| |
| FakePayloadTypeSuggester pt_suggester; |
| OccupyDynamicPayloadTypes(pt_suggester, MediaType::VIDEO, |
| /*free_payload_types=*/1); |
| |
| MediaDescriptionOptions options(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendRecv, false); |
| options.codecs_to_include = {CreateVideoCodec(96, "vp8")}; |
| |
| RTCErrorOr<std::vector<Codec>> offered_codecs = |
| codec_vendor.GetNegotiatedCodecsForOffer(options, MediaSessionOptions(), |
| nullptr, pt_suggester); |
| ASSERT_TRUE(offered_codecs.ok()) << offered_codecs.error().message(); |
| EXPECT_THAT(offered_codecs.value(), |
| ElementsAre(Field(&Codec::name, Eq("vp8")))); |
| } |
| |
| // When WebRTC-PayloadTypesInTransport is enabled, unoffered codecs must not be |
| // assigned a payload type when creating an offer. |
| TEST(CodecVendorTest, OfferRespectsDirectionWithPayloadTypesInTransport) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Enabled/"}); |
| FakeMediaEngine media_engine; |
| media_engine.SetVideoSendCodecs({CreateVideoCodec(96, "vp8")}); |
| media_engine.SetVideoRecvCodecs({CreateVideoCodec(96, "vp8"), |
| CreateVideoCodec(97, "vp9"), |
| CreateVideoCodec(98, "av1")}); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env.field_trials()); |
| |
| FakePayloadTypeSuggester pt_suggester; |
| OccupyDynamicPayloadTypes(pt_suggester, MediaType::VIDEO, |
| /*free_payload_types=*/1); |
| |
| RTCErrorOr<std::vector<Codec>> offered_codecs = |
| codec_vendor.GetNegotiatedCodecsForOffer( |
| MediaDescriptionOptions(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendOnly, false), |
| MediaSessionOptions(), nullptr, pt_suggester); |
| ASSERT_TRUE(offered_codecs.ok()) << offered_codecs.error().message(); |
| EXPECT_THAT(offered_codecs.value(), |
| ElementsAre(Field(&Codec::name, Eq("vp8")))); |
| } |
| |
| // Comfort noise codecs must not be assigned a payload type when the |
| // application does not want them in the offer. |
| TEST_P(CodecVendorPayloadTypeTest, |
| OfferDoesNotAssignPayloadTypesToComfortNoise) { |
| FakeMediaEngine media_engine; |
| // "CN" comes first, so it takes a payload type that is needed by the codecs |
| // that follow it unless it is excluded from the offer. |
| media_engine.SetAudioCodecs({CreateAudioCodec(107, "CN", 48000, 1), |
| CreateAudioCodec(111, "opus", 48000, 2)}); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env_.field_trials()); |
| |
| // Leave room for exactly the codecs that the offer is expected to contain: |
| // "opus", plus the "red" and "telephone-event" codecs that accompany it in |
| // the redesigned payload type assignment. |
| FakePayloadTypeSuggester pt_suggester; |
| OccupyDynamicPayloadTypes( |
| pt_suggester, MediaType::AUDIO, |
| /*free_payload_types=*/payload_types_in_transport() ? 3 : 1); |
| |
| MediaSessionOptions session_options; |
| session_options.vad_enabled = false; |
| RTCErrorOr<std::vector<Codec>> offered_codecs = |
| codec_vendor.GetNegotiatedCodecsForOffer( |
| MediaDescriptionOptions(MediaType::AUDIO, "mid", |
| RtpTransceiverDirection::kSendRecv, false), |
| session_options, nullptr, pt_suggester); |
| ASSERT_TRUE(offered_codecs.ok()) << offered_codecs.error().message(); |
| EXPECT_THAT(offered_codecs.value(), |
| Not(Contains(Field(&Codec::name, StrCaseEq("CN"))))); |
| EXPECT_THAT(offered_codecs.value(), |
| Contains(Field(&Codec::name, Eq("opus")))); |
| } |
| |
| // Codecs excluded by transceiver codec preferences must not be assigned a |
| // payload type when creating an answer. |
| TEST_P(CodecVendorPayloadTypeTest, |
| AnswerDoesNotAssignPayloadTypesToExcludedCodecs) { |
| FakeMediaEngine media_engine; |
| // "av1" comes last, so it only gets a payload type if the codecs that the |
| // codec preferences exclude do not take it first. |
| media_engine.SetVideoCodecs({CreateVideoCodec(96, "vp8"), |
| CreateVideoCodec(97, "vp9"), |
| CreateVideoCodec(98, "av1")}); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env_.field_trials()); |
| |
| FakePayloadTypeSuggester pt_suggester; |
| OccupyDynamicPayloadTypes(pt_suggester, MediaType::VIDEO, |
| /*free_payload_types=*/1); |
| |
| MediaDescriptionOptions options(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendRecv, false); |
| RtpCodecCapability pref; |
| pref.name = "av1"; |
| pref.kind = MediaType::VIDEO; |
| pref.clock_rate = 90000; |
| options.codec_preferences = {pref}; |
| |
| std::vector<Codec> codecs_from_offer = {CreateVideoCodec(96, "vp8"), |
| CreateVideoCodec(98, "av1")}; |
| RTCErrorOr<std::vector<Codec>> answered_codecs = |
| codec_vendor.GetNegotiatedCodecsForAnswer( |
| options, MediaSessionOptions(), RtpTransceiverDirection::kSendRecv, |
| RtpTransceiverDirection::kSendRecv, /*current_content=*/nullptr, |
| codecs_from_offer, pt_suggester); |
| ASSERT_TRUE(answered_codecs.ok()) << answered_codecs.error().message(); |
| EXPECT_THAT(answered_codecs.value(), |
| ElementsAre(Field(&Codec::name, Eq("av1")))); |
| } |
| |
| // Comfort noise codecs must not be assigned a payload type when the |
| // application does not want them in the answer. |
| TEST(CodecVendorTest, AnswerDoesNotAssignPayloadTypesToComfortNoise) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Disabled/"}); |
| FakeMediaEngine media_engine; |
| // "CN" comes first, so it takes the last free payload type unless it is |
| // excluded from the answer. |
| media_engine.SetAudioCodecs({CreateAudioCodec(107, "CN", 48000, 1), |
| CreateAudioCodec(111, "opus", 48000, 2)}); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env.field_trials()); |
| |
| FakePayloadTypeSuggester pt_suggester; |
| OccupyDynamicPayloadTypes(pt_suggester, MediaType::AUDIO, |
| /*free_payload_types=*/1); |
| |
| MediaSessionOptions session_options; |
| session_options.vad_enabled = false; |
| std::vector<Codec> codecs_from_offer = { |
| CreateAudioCodec(111, "opus", 48000, 2)}; |
| RTCErrorOr<std::vector<Codec>> answered_codecs = |
| codec_vendor.GetNegotiatedCodecsForAnswer( |
| MediaDescriptionOptions(MediaType::AUDIO, "mid", |
| RtpTransceiverDirection::kSendRecv, false), |
| session_options, RtpTransceiverDirection::kSendRecv, |
| RtpTransceiverDirection::kSendRecv, /*current_content=*/nullptr, |
| codecs_from_offer, pt_suggester); |
| ASSERT_TRUE(answered_codecs.ok()) << answered_codecs.error().message(); |
| EXPECT_THAT(answered_codecs.value(), |
| ElementsAre(Field(&Codec::name, Eq("opus")))); |
| } |
| |
| // The forward error correction mechanisms that the transceiver codec |
| // preferences do not include must not be assigned a payload type. |
| TEST(CodecVendorWithPayloadTypesInTransportTest, |
| OfferDoesNotAssignPayloadTypesToExcludedFecCodecs) { |
| Environment env = CreateTestEnvironment( |
| {.field_trials = "WebRTC-PayloadTypesInTransport/Enabled/"}); |
| FakeMediaEngine media_engine; |
| // The FEC codecs make the codec configuration of "vp8" carry ULPFEC and |
| // FlexFEC, neither of which the codec preferences below ask for. |
| media_engine.SetVideoCodecs({CreateVideoCodec(96, "vp8"), |
| CreateVideoCodec(97, kUlpfecCodecName), |
| CreateVideoCodec(98, kFlexfecCodecName)}); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ false, |
| env.field_trials()); |
| |
| // Leave room for "vp8" only, so that the offer can only be created if the |
| // FEC codecs are left without a payload type. |
| FakePayloadTypeSuggester pt_suggester; |
| OccupyDynamicPayloadTypes(pt_suggester, MediaType::VIDEO, |
| /*free_payload_types=*/1); |
| |
| MediaDescriptionOptions options(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendRecv, false); |
| RtpCodecCapability pref; |
| pref.name = "vp8"; |
| pref.kind = MediaType::VIDEO; |
| pref.clock_rate = 90000; |
| options.codec_preferences = {pref}; |
| |
| RTCErrorOr<std::vector<Codec>> offered_codecs = |
| codec_vendor.GetNegotiatedCodecsForOffer(options, MediaSessionOptions(), |
| nullptr, pt_suggester); |
| ASSERT_TRUE(offered_codecs.ok()) << offered_codecs.error().message(); |
| EXPECT_THAT(offered_codecs.value(), |
| ElementsAre(Field(&Codec::name, Eq("vp8")))); |
| } |
| |
| // Builds the transceiver codec preferences of an application that wants VP8 |
| // and AV1 only, with retransmission for both. |
| std::vector<RtpCodecCapability> Vp8AndAv1Preferences() { |
| const std::vector<SdpVideoFormat> formats = test::HardwareVideoFormats(); |
| RtpCodecCapability rtx; |
| rtx.name = kRtxCodecName; |
| rtx.kind = MediaType::VIDEO; |
| rtx.clock_rate = kVideoCodecClockrate; |
| return { |
| ToRtpCodecCapability( |
| CreateVideoCodec(PayloadType::NotSet(), formats.front())), |
| ToRtpCodecCapability( |
| CreateVideoCodec(PayloadType::NotSet(), formats.back())), |
| rtx, |
| }; |
| } |
| |
| // The offer of a peer that only has the two codecs that the preferences above |
| // ask for. |
| std::vector<Codec> Vp8AndAv1Offer() { |
| const std::vector<SdpVideoFormat> formats = test::HardwareVideoFormats(); |
| return {CreateVideoCodec(120, formats.front()), CreateVideoRtxCodec(121, 120), |
| CreateVideoCodec(122, formats.back()), CreateVideoRtxCodec(123, 122)}; |
| } |
| |
| // Regression test for https://issues.webrtc.org/564178627, where an endpoint |
| // with hardware codecs ran out of dynamic payload types: it supports 14 video |
| // formats, each with a retransmission codec of its own, the lower range that |
| // AV1 prefers has been used up, and the application has munged codecs of its |
| // own into the upper range. The payload types that are left are only enough |
| // for the codecs that the answer can contain, so AV1, which comes last, only |
| // survives if the codecs that the transceiver's codec preferences exclude do |
| // not take them first. |
| TEST_P(CodecVendorPayloadTypeTest, |
| AnswerWithHardwareCodecMatrixUnderPayloadTypePressure) { |
| FakeMediaEngine media_engine; |
| media_engine.SetVideoCodecs(test::HardwareVideoCodecs()); |
| CodecVendor codec_vendor(&media_engine, /* rtx_enabled= */ true, |
| env_.field_trials()); |
| |
| // Eight payload types are left, which is enough for the four codecs of the |
| // answer but nowhere near enough for the 28 codecs of the matrix. |
| FakePayloadTypeSuggester pt_suggester; |
| OccupyPayloadTypeRange(pt_suggester, MediaType::VIDEO, 35, 63); |
| OccupyPayloadTypeRange(pt_suggester, MediaType::VIDEO, 96, 119); |
| |
| MediaDescriptionOptions options(MediaType::VIDEO, "mid", |
| RtpTransceiverDirection::kSendRecv, false); |
| options.codec_preferences = Vp8AndAv1Preferences(); |
| |
| RTCErrorOr<std::vector<Codec>> answered_codecs = |
| codec_vendor.GetNegotiatedCodecsForAnswer( |
| options, MediaSessionOptions(), RtpTransceiverDirection::kSendRecv, |
| RtpTransceiverDirection::kSendRecv, /*current_content=*/nullptr, |
| Vp8AndAv1Offer(), pt_suggester); |
| ASSERT_TRUE(answered_codecs.ok()) << answered_codecs.error().message(); |
| EXPECT_THAT(answered_codecs.value(), |
| Contains(Field(&Codec::name, StrCaseEq("VP8")))); |
| EXPECT_THAT(answered_codecs.value(), |
| Contains(Field(&Codec::name, StrCaseEq("AV1")))); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(PayloadTypesInTransport, |
| CodecVendorPayloadTypeTest, |
| ::testing::Bool(), |
| [](const ::testing::TestParamInfo<bool>& info) |
| -> std::string { |
| return info.param ? "Enabled" : "Disabled"; |
| }); |
| |
| } // namespace |
| } // namespace webrtc |