red: fix fmtp payload type collision handling
along the lines of RTX handling but with limited support for missing fmtp lines because of video/red. BUG=webrtc:13178 Change-Id: Ia866c0e857da6da2ef1e4b81b51f90f534c7bb83 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/231948 Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org> Commit-Queue: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#35107}
This commit is contained in:

committed by
WebRTC LUCI CQ

parent
4a1c2c4754
commit
7145a1421b
@ -39,6 +39,9 @@ const char kCodecParamRtxTime[] = "rtx-time";
|
||||
const char kCodecParamAssociatedPayloadType[] = "apt";
|
||||
|
||||
const char kCodecParamAssociatedCodecName[] = "acn";
|
||||
// Parameters that do not follow the key-value convention
|
||||
// are treated as having the empty string as key.
|
||||
const char kCodecParamNotInNameValueFormat[] = "";
|
||||
|
||||
const char kOpusCodecName[] = "opus";
|
||||
const char kIsacCodecName[] = "ISAC";
|
||||
|
@ -42,6 +42,7 @@ extern const char kCodecParamRtxTime[];
|
||||
extern const char kCodecParamAssociatedPayloadType[];
|
||||
|
||||
extern const char kCodecParamAssociatedCodecName[];
|
||||
extern const char kCodecParamNotInNameValueFormat[];
|
||||
|
||||
extern const char kOpusCodecName[];
|
||||
extern const char kIsacCodecName[];
|
||||
|
@ -653,6 +653,11 @@ static bool ContainsRtxCodec(const std::vector<C>& codecs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class C>
|
||||
static bool IsRedCodec(const C& codec) {
|
||||
return absl::EqualsIgnoreCase(codec.name, kRedCodecName);
|
||||
}
|
||||
|
||||
template <class C>
|
||||
static bool IsRtxCodec(const C& codec) {
|
||||
return absl::EqualsIgnoreCase(codec.name, kRtxCodecName);
|
||||
@ -800,6 +805,11 @@ static void NegotiateCodecs(const std::vector<C>& local_codecs,
|
||||
if (rtx_time_it != theirs.params.end()) {
|
||||
negotiated.SetParam(kCodecParamRtxTime, rtx_time_it->second);
|
||||
}
|
||||
} else if (IsRedCodec(negotiated)) {
|
||||
const auto red_it = theirs.params.find(kCodecParamNotInNameValueFormat);
|
||||
if (red_it != theirs.params.end()) {
|
||||
negotiated.SetParam(kCodecParamNotInNameValueFormat, red_it->second);
|
||||
}
|
||||
}
|
||||
if (absl::EqualsIgnoreCase(ours.name, kH264CodecName)) {
|
||||
webrtc::H264GenerateProfileLevelIdForAnswer(ours.params, theirs.params,
|
||||
@ -829,15 +839,16 @@ static void NegotiateCodecs(const std::vector<C>& local_codecs,
|
||||
}
|
||||
|
||||
// Finds a codec in `codecs2` that matches `codec_to_match`, which is
|
||||
// a member of `codecs1`. If `codec_to_match` is an RTX codec, both
|
||||
// a member of `codecs1`. If `codec_to_match` is an RED or RTX codec, both
|
||||
// the codecs themselves and their associated codecs must match.
|
||||
template <class C>
|
||||
static bool FindMatchingCodec(const std::vector<C>& codecs1,
|
||||
const std::vector<C>& codecs2,
|
||||
const C& codec_to_match,
|
||||
C* found_codec) {
|
||||
// `codec_to_match` should be a member of `codecs1`, in order to look up RTX
|
||||
// codecs' associated codecs correctly. If not, that's a programming error.
|
||||
// `codec_to_match` should be a member of `codecs1`, in order to look up
|
||||
// RED/RTX codecs' associated codecs correctly. If not, that's a programming
|
||||
// error.
|
||||
RTC_DCHECK(absl::c_any_of(codecs1, [&codec_to_match](const C& codec) {
|
||||
return &codec == &codec_to_match;
|
||||
}));
|
||||
@ -857,6 +868,54 @@ static bool FindMatchingCodec(const std::vector<C>& codecs1,
|
||||
apt_value_2)) {
|
||||
continue;
|
||||
}
|
||||
} else if (IsRedCodec(codec_to_match)) {
|
||||
auto red_parameters_1 =
|
||||
codec_to_match.params.find(kCodecParamNotInNameValueFormat);
|
||||
auto red_parameters_2 =
|
||||
potential_match.params.find(kCodecParamNotInNameValueFormat);
|
||||
bool has_parameters_1 = red_parameters_1 != codec_to_match.params.end();
|
||||
bool has_parameters_2 =
|
||||
red_parameters_2 != potential_match.params.end();
|
||||
if (has_parameters_1 && has_parameters_2) {
|
||||
// Mixed reference codecs (i.e. 111/112) are not supported.
|
||||
// Different levels of redundancy between offer and answer are
|
||||
// since RED is considered to be declarative.
|
||||
std::vector<std::string> redundant_payloads_1;
|
||||
std::vector<std::string> redundant_payloads_2;
|
||||
rtc::split(red_parameters_1->second, '/', &redundant_payloads_1);
|
||||
rtc::split(red_parameters_2->second, '/', &redundant_payloads_2);
|
||||
if (redundant_payloads_1.size() > 0 &&
|
||||
redundant_payloads_2.size() > 0) {
|
||||
bool consistent = true;
|
||||
for (size_t i = 1; i < redundant_payloads_1.size(); i++) {
|
||||
if (redundant_payloads_1[i] != redundant_payloads_1[0]) {
|
||||
consistent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (size_t i = 1; i < redundant_payloads_2.size(); i++) {
|
||||
if (redundant_payloads_2[i] != redundant_payloads_2[0]) {
|
||||
consistent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!consistent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int red_value_1;
|
||||
int red_value_2;
|
||||
if (rtc::FromString(redundant_payloads_1[0], &red_value_1) &&
|
||||
rtc::FromString(redundant_payloads_2[0], &red_value_2)) {
|
||||
if (!ReferencedCodecsMatch(codecs1, red_value_1, codecs2,
|
||||
red_value_2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (has_parameters_1 != has_parameters_2) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (found_codec) {
|
||||
*found_codec = potential_match;
|
||||
@ -869,8 +928,8 @@ static bool FindMatchingCodec(const std::vector<C>& codecs1,
|
||||
|
||||
// Find the codec in `codec_list` that `rtx_codec` is associated with.
|
||||
template <class C>
|
||||
static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
|
||||
const C& rtx_codec) {
|
||||
static const C* GetAssociatedCodecForRtx(const std::vector<C>& codec_list,
|
||||
const C& rtx_codec) {
|
||||
std::string associated_pt_str;
|
||||
if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
|
||||
&associated_pt_str)) {
|
||||
@ -887,7 +946,7 @@ static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Find the associated reference codec for the reference RTX codec.
|
||||
// Find the associated codec for the RTX codec.
|
||||
const C* associated_codec = FindCodecById(codec_list, associated_pt);
|
||||
if (!associated_codec) {
|
||||
RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
|
||||
@ -897,6 +956,43 @@ static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
|
||||
return associated_codec;
|
||||
}
|
||||
|
||||
// Find the codec in `codec_list` that `red_codec` is associated with.
|
||||
template <class C>
|
||||
static const C* GetAssociatedCodecForRed(const std::vector<C>& codec_list,
|
||||
const C& red_codec) {
|
||||
std::string fmtp;
|
||||
if (!red_codec.GetParam(kCodecParamNotInNameValueFormat, &fmtp)) {
|
||||
// Normal for video/RED.
|
||||
RTC_LOG(LS_WARNING) << "RED codec " << red_codec.name
|
||||
<< " is missing an associated payload type.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::string> redundant_payloads;
|
||||
rtc::split(fmtp, '/', &redundant_payloads);
|
||||
if (redundant_payloads.size() < 2) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string associated_pt_str = redundant_payloads[0];
|
||||
int associated_pt;
|
||||
if (!rtc::FromString(associated_pt_str, &associated_pt)) {
|
||||
RTC_LOG(LS_WARNING) << "Couldn't convert first payload type "
|
||||
<< associated_pt_str << " of RED codec "
|
||||
<< red_codec.name << " to an integer.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Find the associated codec for the RED codec.
|
||||
const C* associated_codec = FindCodecById(codec_list, associated_pt);
|
||||
if (!associated_codec) {
|
||||
RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
|
||||
<< associated_pt << " for RED codec " << red_codec.name
|
||||
<< ".";
|
||||
}
|
||||
return associated_codec;
|
||||
}
|
||||
|
||||
// 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.
|
||||
@ -904,9 +1000,11 @@ template <class C>
|
||||
static void MergeCodecs(const std::vector<C>& reference_codecs,
|
||||
std::vector<C>* offered_codecs,
|
||||
UsedPayloadTypes* used_pltypes) {
|
||||
// Add all new codecs that are not RTX codecs.
|
||||
// Add all new codecs that are not RTX/RED codecs.
|
||||
// The two-pass splitting of the loops means preferring payload types
|
||||
// of actual codecs with respect to collisions.
|
||||
for (const C& reference_codec : reference_codecs) {
|
||||
if (!IsRtxCodec(reference_codec) &&
|
||||
if (!IsRtxCodec(reference_codec) && !IsRedCodec(reference_codec) &&
|
||||
!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
|
||||
reference_codec, nullptr)) {
|
||||
C codec = reference_codec;
|
||||
@ -915,14 +1013,14 @@ static void MergeCodecs(const std::vector<C>& reference_codecs,
|
||||
}
|
||||
}
|
||||
|
||||
// Add all new RTX codecs.
|
||||
// Add all new RTX or RED codecs.
|
||||
for (const C& reference_codec : reference_codecs) {
|
||||
if (IsRtxCodec(reference_codec) &&
|
||||
!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
|
||||
reference_codec, nullptr)) {
|
||||
C rtx_codec = reference_codec;
|
||||
const C* associated_codec =
|
||||
GetAssociatedCodec(reference_codecs, rtx_codec);
|
||||
GetAssociatedCodecForRtx(reference_codecs, rtx_codec);
|
||||
if (!associated_codec) {
|
||||
continue;
|
||||
}
|
||||
@ -940,6 +1038,27 @@ static void MergeCodecs(const std::vector<C>& reference_codecs,
|
||||
rtc::ToString(matching_codec.id);
|
||||
used_pltypes->FindAndSetIdUsed(&rtx_codec);
|
||||
offered_codecs->push_back(rtx_codec);
|
||||
} else if (IsRedCodec(reference_codec) &&
|
||||
!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
|
||||
reference_codec, nullptr)) {
|
||||
C red_codec = reference_codec;
|
||||
const C* associated_codec =
|
||||
GetAssociatedCodecForRed(reference_codecs, red_codec);
|
||||
if (associated_codec) {
|
||||
C matching_codec;
|
||||
if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
|
||||
*associated_codec, &matching_codec)) {
|
||||
RTC_LOG(LS_WARNING) << "Couldn't find matching "
|
||||
<< associated_codec->name << " codec.";
|
||||
continue;
|
||||
}
|
||||
|
||||
red_codec.params[kCodecParamNotInNameValueFormat] =
|
||||
rtc::ToString(matching_codec.id) + "/" +
|
||||
rtc::ToString(matching_codec.id);
|
||||
}
|
||||
used_pltypes->FindAndSetIdUsed(&red_codec);
|
||||
offered_codecs->push_back(red_codec);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -956,6 +1075,7 @@ static Codecs MatchCodecPreference(
|
||||
Codecs filtered_codecs;
|
||||
std::set<std::string> kept_codecs_ids;
|
||||
bool want_rtx = false;
|
||||
bool want_red = false;
|
||||
|
||||
for (const auto& codec_preference : codec_preferences) {
|
||||
auto found_codec = absl::c_find_if(
|
||||
@ -980,10 +1100,12 @@ static Codecs MatchCodecPreference(
|
||||
}
|
||||
} else if (IsRtxCodec(codec_preference)) {
|
||||
want_rtx = true;
|
||||
} else if (IsRedCodec(codec_preference)) {
|
||||
want_red = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (want_rtx) {
|
||||
if (want_rtx || want_red) {
|
||||
for (const auto& codec : codecs) {
|
||||
if (IsRtxCodec(codec)) {
|
||||
const auto apt =
|
||||
@ -992,6 +1114,17 @@ static Codecs MatchCodecPreference(
|
||||
kept_codecs_ids.count(apt->second) > 0) {
|
||||
filtered_codecs.push_back(codec);
|
||||
}
|
||||
} else if (IsRedCodec(codec)) {
|
||||
const auto fmtp =
|
||||
codec.params.find(cricket::kCodecParamNotInNameValueFormat);
|
||||
if (fmtp != codec.params.end()) {
|
||||
std::vector<std::string> redundant_payloads;
|
||||
rtc::split(fmtp->second, '/', &redundant_payloads);
|
||||
if (redundant_payloads.size() > 0 &&
|
||||
kept_codecs_ids.count(redundant_payloads[0]) > 0) {
|
||||
filtered_codecs.push_back(codec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1303,6 +1303,258 @@ TEST_P(PeerConnectionMediaTest,
|
||||
audio_options.combined_audio_video_bwe);
|
||||
}
|
||||
|
||||
// Test that if a RED codec refers to another codec in its fmtp line, but that
|
||||
// codec's payload type was reassigned for some reason (either the remote
|
||||
// endpoint selected a different payload type or there was a conflict), the RED
|
||||
// fmtp line is modified to refer to the correct payload type.
|
||||
TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeReassigned) {
|
||||
std::vector<cricket::AudioCodec> caller_fake_codecs;
|
||||
caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
|
||||
auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
|
||||
caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
|
||||
auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
|
||||
|
||||
std::vector<cricket::AudioCodec> callee_fake_codecs;
|
||||
callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
|
||||
callee_fake_codecs.push_back(
|
||||
cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
|
||||
callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
|
||||
"120/120");
|
||||
auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
|
||||
callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
|
||||
auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
|
||||
|
||||
// Offer from the caller establishes 100 as the "foo" payload type.
|
||||
auto offer = caller->CreateOfferAndSetAsLocal();
|
||||
callee->SetRemoteDescription(std::move(offer));
|
||||
auto answer = callee->CreateAnswerAndSetAsLocal();
|
||||
auto answer_description =
|
||||
cricket::GetFirstAudioContentDescription(answer->description());
|
||||
ASSERT_EQ(1u, answer_description->codecs().size());
|
||||
|
||||
// Offer from the callee should respect the established payload type, and
|
||||
// attempt to add RED, which should refer to the correct payload type.
|
||||
offer = callee->CreateOfferAndSetAsLocal();
|
||||
auto* offer_description =
|
||||
cricket::GetFirstAudioContentDescription(offer->description());
|
||||
ASSERT_EQ(2u, offer_description->codecs().size());
|
||||
for (const auto& codec : offer_description->codecs()) {
|
||||
if (codec.name == "foo") {
|
||||
ASSERT_EQ(100, codec.id);
|
||||
} else if (codec.name == cricket::kRedCodecName) {
|
||||
std::string fmtp;
|
||||
ASSERT_TRUE(codec.GetParam("", &fmtp));
|
||||
EXPECT_EQ("100/100", fmtp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that RED without fmtp does match RED without fmtp.
|
||||
TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeNoFmtpMatchNoFmtp) {
|
||||
std::vector<cricket::AudioCodec> caller_fake_codecs;
|
||||
caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
|
||||
caller_fake_codecs.push_back(
|
||||
cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
|
||||
auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
|
||||
caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
|
||||
auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
|
||||
|
||||
std::vector<cricket::AudioCodec> callee_fake_codecs;
|
||||
callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
|
||||
callee_fake_codecs.push_back(
|
||||
cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
|
||||
auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
|
||||
callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
|
||||
auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
|
||||
|
||||
// Offer from the caller establishes 100 as the "foo" payload type.
|
||||
// Red (without fmtp) is negotiated.
|
||||
auto offer = caller->CreateOfferAndSetAsLocal();
|
||||
callee->SetRemoteDescription(std::move(offer));
|
||||
auto answer = callee->CreateAnswerAndSetAsLocal();
|
||||
auto answer_description =
|
||||
cricket::GetFirstAudioContentDescription(answer->description());
|
||||
ASSERT_EQ(2u, answer_description->codecs().size());
|
||||
|
||||
// Offer from the callee should respect the established payload type, and
|
||||
// attempt to add RED.
|
||||
offer = callee->CreateOfferAndSetAsLocal();
|
||||
auto* offer_description =
|
||||
cricket::GetFirstAudioContentDescription(offer->description());
|
||||
ASSERT_EQ(2u, offer_description->codecs().size());
|
||||
for (const auto& codec : offer_description->codecs()) {
|
||||
if (codec.name == "foo") {
|
||||
ASSERT_EQ(100, codec.id);
|
||||
} else if (codec.name == cricket::kRedCodecName) {
|
||||
ASSERT_EQ(101, codec.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that RED without fmtp does not match RED with fmtp.
|
||||
TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeNoFmtpNoMatchFmtp) {
|
||||
std::vector<cricket::AudioCodec> caller_fake_codecs;
|
||||
caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
|
||||
caller_fake_codecs.push_back(
|
||||
cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
|
||||
auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
|
||||
caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
|
||||
auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
|
||||
|
||||
std::vector<cricket::AudioCodec> callee_fake_codecs;
|
||||
callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
|
||||
callee_fake_codecs.push_back(
|
||||
cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
|
||||
callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
|
||||
"120/120");
|
||||
auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
|
||||
callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
|
||||
auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
|
||||
|
||||
// Offer from the caller establishes 100 as the "foo" payload type.
|
||||
// It should not negotiate RED.
|
||||
auto offer = caller->CreateOfferAndSetAsLocal();
|
||||
callee->SetRemoteDescription(std::move(offer));
|
||||
auto answer = callee->CreateAnswerAndSetAsLocal();
|
||||
auto answer_description =
|
||||
cricket::GetFirstAudioContentDescription(answer->description());
|
||||
ASSERT_EQ(1u, answer_description->codecs().size());
|
||||
|
||||
// Offer from the callee should respect the established payload type, and
|
||||
// attempt to add RED, which should refer to the correct payload type.
|
||||
offer = callee->CreateOfferAndSetAsLocal();
|
||||
auto* offer_description =
|
||||
cricket::GetFirstAudioContentDescription(offer->description());
|
||||
ASSERT_EQ(2u, offer_description->codecs().size());
|
||||
for (const auto& codec : offer_description->codecs()) {
|
||||
if (codec.name == "foo") {
|
||||
ASSERT_EQ(100, codec.id);
|
||||
} else if (codec.name == cricket::kRedCodecName) {
|
||||
std::string fmtp;
|
||||
ASSERT_TRUE(
|
||||
codec.GetParam(cricket::kCodecParamNotInNameValueFormat, &fmtp));
|
||||
EXPECT_EQ("100/100", fmtp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that RED with fmtp must match base codecs.
|
||||
TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeMustMatchBaseCodecs) {
|
||||
std::vector<cricket::AudioCodec> caller_fake_codecs;
|
||||
caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
|
||||
caller_fake_codecs.push_back(
|
||||
cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
|
||||
caller_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
|
||||
"100/100");
|
||||
auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
|
||||
caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
|
||||
auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
|
||||
|
||||
std::vector<cricket::AudioCodec> callee_fake_codecs;
|
||||
callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
|
||||
callee_fake_codecs.push_back(
|
||||
cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
|
||||
callee_fake_codecs.push_back(cricket::AudioCodec(122, "bar", 0, 0, 1));
|
||||
callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
|
||||
"122/122");
|
||||
auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
|
||||
callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
|
||||
auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
|
||||
|
||||
// Offer from the caller establishes 100 as the "foo" payload type.
|
||||
// It should not negotiate RED since RED is associated with foo, not bar.
|
||||
auto offer = caller->CreateOfferAndSetAsLocal();
|
||||
callee->SetRemoteDescription(std::move(offer));
|
||||
auto answer = callee->CreateAnswerAndSetAsLocal();
|
||||
auto answer_description =
|
||||
cricket::GetFirstAudioContentDescription(answer->description());
|
||||
ASSERT_EQ(1u, answer_description->codecs().size());
|
||||
}
|
||||
|
||||
// Test behaviour when the RED fmtp attempts to specify different codecs
|
||||
// which is not supported.
|
||||
TEST_P(PeerConnectionMediaTest, RedFmtpPayloadMixed) {
|
||||
std::vector<cricket::AudioCodec> caller_fake_codecs;
|
||||
caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
|
||||
caller_fake_codecs.push_back(cricket::AudioCodec(102, "bar", 0, 0, 1));
|
||||
caller_fake_codecs.push_back(
|
||||
cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
|
||||
caller_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
|
||||
"100/102");
|
||||
auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
|
||||
caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
|
||||
auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
|
||||
|
||||
std::vector<cricket::AudioCodec> callee_fake_codecs;
|
||||
callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
|
||||
callee_fake_codecs.push_back(
|
||||
cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
|
||||
callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
|
||||
"120/120");
|
||||
auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
|
||||
callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
|
||||
auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
|
||||
|
||||
// Offer from the caller establishes 100 as the "foo" payload type.
|
||||
auto offer = caller->CreateOfferAndSetAsLocal();
|
||||
callee->SetRemoteDescription(std::move(offer));
|
||||
auto answer = callee->CreateAnswerAndSetAsLocal();
|
||||
auto answer_description =
|
||||
cricket::GetFirstAudioContentDescription(answer->description());
|
||||
// RED is not negotiated.
|
||||
ASSERT_EQ(1u, answer_description->codecs().size());
|
||||
}
|
||||
|
||||
// Test behaviour when the RED fmtp attempts to negotiate different levels of
|
||||
// redundancy.
|
||||
TEST_P(PeerConnectionMediaTest, RedFmtpPayloadDifferentRedundancy) {
|
||||
std::vector<cricket::AudioCodec> caller_fake_codecs;
|
||||
caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
|
||||
caller_fake_codecs.push_back(
|
||||
cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
|
||||
caller_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
|
||||
"100/100");
|
||||
auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
|
||||
caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
|
||||
auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
|
||||
|
||||
std::vector<cricket::AudioCodec> callee_fake_codecs;
|
||||
callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
|
||||
callee_fake_codecs.push_back(
|
||||
cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
|
||||
callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
|
||||
"120/120/120");
|
||||
auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
|
||||
callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
|
||||
auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
|
||||
|
||||
// Offer from the caller establishes 100 as the "foo" payload type.
|
||||
auto offer = caller->CreateOfferAndSetAsLocal();
|
||||
callee->SetRemoteDescription(std::move(offer));
|
||||
auto answer = callee->CreateAnswerAndSetAsLocal();
|
||||
auto answer_description =
|
||||
cricket::GetFirstAudioContentDescription(answer->description());
|
||||
// RED is negotiated.
|
||||
ASSERT_EQ(2u, answer_description->codecs().size());
|
||||
|
||||
// Offer from the callee should respect the established payload type, and
|
||||
// attempt to add RED, which should refer to the correct payload type.
|
||||
offer = callee->CreateOfferAndSetAsLocal();
|
||||
auto* offer_description =
|
||||
cricket::GetFirstAudioContentDescription(offer->description());
|
||||
ASSERT_EQ(2u, offer_description->codecs().size());
|
||||
for (const auto& codec : offer_description->codecs()) {
|
||||
if (codec.name == "foo") {
|
||||
ASSERT_EQ(100, codec.id);
|
||||
} else if (codec.name == cricket::kRedCodecName) {
|
||||
std::string fmtp;
|
||||
ASSERT_TRUE(
|
||||
codec.GetParam(cricket::kCodecParamNotInNameValueFormat, &fmtp));
|
||||
EXPECT_EQ("100/100", fmtp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
bool CompareCodecs(const std::vector<webrtc::RtpCodecCapability>& capabilities,
|
||||
const std::vector<C>& codecs) {
|
||||
|
Reference in New Issue
Block a user