Bug 1967189 - Track duplicate payload types for all recv tracks in a peer connection. r=bwc,dbaker

Two issues are fixed here:
- The lists of unique and duplicate payload types were never reset, so would
  accumulate entries across renegotiations
- The std::map could only store distinct payload types, and only a single track
  per payload type. Therefore, at most one track could ever know about any given
  duplicate payload type.

This patch also adds some unittests for JsepTrack::SetUniqueReceivePayloadTypes.
They're very verbose, for now, to enable landing this patch quickly.

Differential Revision: https://phabricator.services.mozilla.com/D250095
This commit is contained in:
Andreas Pehrson
2025-05-20 21:26:51 +00:00
committed by pehrsons@gmail.com
parent 4f622a88c7
commit 1e511bf3af
3 changed files with 437 additions and 44 deletions

View File

@@ -733,11 +733,13 @@ nsresult JsepTrack::Negotiate(const SdpMediaSection& answer,
/* static */
void JsepTrack::SetUniqueReceivePayloadTypes(std::vector<JsepTrack*>& tracks,
bool localOffer) {
// Maps to track details if no other track contains the payload type,
// otherwise maps to nullptr.
std::map<uint16_t, std::tuple<JsepTrack*, bool>> payloadTypeToDetailsMap;
// Maps payload types to all tracks that have negotiated them.
std::multimap<uint16_t, JsepTrack*> payloadTypeToTracks;
for (JsepTrack* track : tracks) {
track->mUniqueReceivePayloadTypes.clear();
track->mDuplicateReceivePayloadTypes.clear();
if (track->GetMediaType() == SdpMediaSection::kApplication) {
continue;
}
@@ -755,24 +757,23 @@ void JsepTrack::SetUniqueReceivePayloadTypes(std::vector<JsepTrack*>& tracks,
}
for (uint16_t pt : payloadTypesForTrack) {
payloadTypeToDetailsMap[pt] =
std::make_tuple(track, !payloadTypeToDetailsMap.count(pt));
payloadTypeToTracks.insert({pt, track});
}
}
for (auto ptAndDetails : payloadTypeToDetailsMap) {
uint16_t uniquePt = ptAndDetails.first;
MOZ_ASSERT(uniquePt <= UINT8_MAX);
auto* trackDetails = std::get<JsepTrack*>(ptAndDetails.second);
if (trackDetails) {
if (std::get<bool>(ptAndDetails.second)) {
trackDetails->mUniqueReceivePayloadTypes.push_back(
static_cast<uint8_t>(uniquePt));
} else {
trackDetails->mDuplicateReceivePayloadTypes.push_back(
static_cast<uint8_t>(uniquePt));
}
for (auto it = payloadTypeToTracks.begin(), end = payloadTypeToTracks.end();
it != end;) {
const auto& [key, firstTrackForPt] = *it;
const auto pt = AssertedCast<uint8_t>(key);
const size_t count = payloadTypeToTracks.count(key);
if (count == 1) {
firstTrackForPt->mUniqueReceivePayloadTypes.push_back(pt);
++it;
continue;
}
for (auto next = payloadTypeToTracks.upper_bound(key); it != next; ++it) {
const auto& [_pt, track] = *it;
track->mDuplicateReceivePayloadTypes.push_back(pt);
}
}
}

View File

@@ -14,6 +14,7 @@
#include "mozilla/RefPtr.h"
#define GTEST_HAS_RTTI 0
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "CodecConfig.h"
@@ -24,6 +25,8 @@
#include "jsep/JsepSession.h"
#include "jsep/JsepSessionImpl.h"
using testing::UnorderedElementsAre;
namespace mozilla {
MOZ_RUNINIT static std::string kAEqualsCandidate("a=candidate:");
const static size_t kNumCandidatesPerComponent = 3;
@@ -4972,55 +4975,65 @@ TEST_F(JsepSessionTest, TestUniqueReceivePayloadTypes) {
ASSERT_FALSE(IsNull(offerTransceivers[0].mRecvTrack));
ASSERT_TRUE(offerTransceivers[0].mRecvTrack.GetNegotiatedDetails());
ASSERT_EQ(
0U,
offerTransceivers[0].mRecvTrack.GetUniqueReceivePayloadTypes().size());
ASSERT_THAT(offerTransceivers[0].mRecvTrack.GetUniqueReceivePayloadTypes(),
UnorderedElementsAre());
ASSERT_THAT(offerTransceivers[0].mRecvTrack.GetDuplicateReceivePayloadTypes(),
UnorderedElementsAre(0, 8, 9, 101, 109));
ASSERT_FALSE(IsNull(offerTransceivers[1].mRecvTrack));
ASSERT_TRUE(offerTransceivers[1].mRecvTrack.GetNegotiatedDetails());
ASSERT_EQ(
0U,
offerTransceivers[1].mRecvTrack.GetUniqueReceivePayloadTypes().size());
ASSERT_THAT(offerTransceivers[1].mRecvTrack.GetUniqueReceivePayloadTypes(),
UnorderedElementsAre());
ASSERT_THAT(offerTransceivers[1].mRecvTrack.GetDuplicateReceivePayloadTypes(),
UnorderedElementsAre(0, 8, 9, 101, 109));
// First video transceiver is the only one receiving, so gets unique pts.
ASSERT_FALSE(IsNull(offerTransceivers[2].mRecvTrack));
ASSERT_TRUE(offerTransceivers[2].mRecvTrack.GetNegotiatedDetails());
ASSERT_NE(
0U,
offerTransceivers[2].mRecvTrack.GetUniqueReceivePayloadTypes().size());
ASSERT_THAT(offerTransceivers[2].mRecvTrack.GetUniqueReceivePayloadTypes(),
UnorderedElementsAre(97, 99, 103, 105, 120, 121, 122, 123, 126));
ASSERT_THAT(offerTransceivers[2].mRecvTrack.GetDuplicateReceivePayloadTypes(),
UnorderedElementsAre());
// First video transceiver is not receiving, so does not get unique pts.
ASSERT_TRUE(IsNull(offerTransceivers[3].mRecvTrack));
ASSERT_TRUE(offerTransceivers[3].mRecvTrack.GetNegotiatedDetails());
ASSERT_EQ(
0U,
offerTransceivers[3].mRecvTrack.GetUniqueReceivePayloadTypes().size());
ASSERT_THAT(offerTransceivers[3].mRecvTrack.GetUniqueReceivePayloadTypes(),
UnorderedElementsAre());
ASSERT_THAT(offerTransceivers[3].mRecvTrack.GetDuplicateReceivePayloadTypes(),
UnorderedElementsAre(97, 99, 103, 105, 120, 121, 122, 123, 126));
ASSERT_FALSE(IsNull(answerTransceivers[0].mRecvTrack));
ASSERT_TRUE(answerTransceivers[0].mRecvTrack.GetNegotiatedDetails());
ASSERT_EQ(
0U,
answerTransceivers[0].mRecvTrack.GetUniqueReceivePayloadTypes().size());
ASSERT_THAT(answerTransceivers[0].mRecvTrack.GetUniqueReceivePayloadTypes(),
UnorderedElementsAre());
ASSERT_THAT(
answerTransceivers[0].mRecvTrack.GetDuplicateReceivePayloadTypes(),
UnorderedElementsAre(0, 8, 9, 101, 109));
ASSERT_FALSE(IsNull(answerTransceivers[1].mRecvTrack));
ASSERT_TRUE(answerTransceivers[1].mRecvTrack.GetNegotiatedDetails());
ASSERT_EQ(
0U,
answerTransceivers[1].mRecvTrack.GetUniqueReceivePayloadTypes().size());
ASSERT_THAT(answerTransceivers[1].mRecvTrack.GetUniqueReceivePayloadTypes(),
UnorderedElementsAre());
ASSERT_THAT(
answerTransceivers[1].mRecvTrack.GetDuplicateReceivePayloadTypes(),
UnorderedElementsAre(0, 8, 9, 101, 109));
// Answerer is receiving two video streams with the same payload types.
// Neither recv track should have unique pts.
ASSERT_FALSE(IsNull(answerTransceivers[2].mRecvTrack));
ASSERT_TRUE(answerTransceivers[2].mRecvTrack.GetNegotiatedDetails());
ASSERT_EQ(
0U,
answerTransceivers[2].mRecvTrack.GetUniqueReceivePayloadTypes().size());
ASSERT_THAT(answerTransceivers[2].mRecvTrack.GetUniqueReceivePayloadTypes(),
UnorderedElementsAre());
ASSERT_THAT(
answerTransceivers[2].mRecvTrack.GetDuplicateReceivePayloadTypes(),
UnorderedElementsAre(97, 99, 103, 105, 120, 121, 122, 123, 126));
ASSERT_FALSE(IsNull(answerTransceivers[3].mRecvTrack));
ASSERT_TRUE(answerTransceivers[3].mRecvTrack.GetNegotiatedDetails());
ASSERT_EQ(
0U,
answerTransceivers[3].mRecvTrack.GetUniqueReceivePayloadTypes().size());
ASSERT_THAT(answerTransceivers[3].mRecvTrack.GetUniqueReceivePayloadTypes(),
UnorderedElementsAre());
ASSERT_THAT(
answerTransceivers[3].mRecvTrack.GetDuplicateReceivePayloadTypes(),
UnorderedElementsAre(97, 99, 103, 105, 120, 121, 122, 123, 126));
}
TEST_F(JsepSessionTest, UnknownFingerprintAlgorithm) {

View File

@@ -8,6 +8,7 @@
#include "ssl.h"
#define GTEST_HAS_RTTI 0
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "MockJsepCodecPreferences.h"
@@ -17,6 +18,8 @@
#include "sdp/SipccSdpParser.h"
#include "sdp/SdpHelper.h"
using testing::UnorderedElementsAre;
namespace mozilla {
class JsepTrackTestBase : public ::testing::Test {
@@ -1790,4 +1793,380 @@ TEST_F(JsepTrackTest, NonDefaultVideoSdpFmtpLine) {
EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
}
TEST(JsepTrackRecvPayloadTypesTest, SingleTrackPTsAreUnique)
{
constexpr auto audio = SdpMediaSection::MediaType::kAudio;
std::vector<UniquePtr<JsepCodecDescription>> codecs;
codecs.emplace_back(
MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1, true));
SipccSdp offer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
SdpMediaSection& offer1Msection1 = offer1.AddMediaSection(
audio, SdpDirectionAttribute::kRecvonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SipccSdp answer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
SdpMediaSection& answer1Msection1 = answer1.AddMediaSection(
audio, SdpDirectionAttribute::kSendonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
for (const auto& codec : codecs) {
codec->mDirection = sdp::kSend;
offer1Msection1.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
codec->mChannels);
auto clone = WrapUnique(codec->Clone());
clone->mDirection = sdp::kRecv;
clone->AddToMediaSection(answer1Msection1);
}
JsepTrack t1{audio, sdp::Direction::kRecv};
t1.PopulateCodecs(codecs, false);
t1.RecvTrackSetLocal(offer1Msection1);
t1.RecvTrackSetRemote(answer1, answer1Msection1);
ASSERT_EQ(t1.Negotiate(answer1Msection1, answer1Msection1, offer1Msection1),
NS_OK);
std::vector tracks{&t1};
JsepTrack::SetUniqueReceivePayloadTypes(tracks);
EXPECT_THAT(t1.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(1));
EXPECT_THAT(t1.GetDuplicateReceivePayloadTypes(), UnorderedElementsAre());
}
TEST(JsepTrackRecvPayloadTypesTest, DoubleTrackPTsAreUnique)
{
constexpr auto audio = SdpMediaSection::MediaType::kAudio;
std::vector<UniquePtr<JsepCodecDescription>> codecs1;
codecs1.emplace_back(
MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1, true));
std::vector<UniquePtr<JsepCodecDescription>> codecs2;
codecs2.emplace_back(
MakeUnique<JsepAudioCodecDescription>("2", "codec1", 48000, 1, true));
SipccSdp offer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
SdpMediaSection& offer1Msection1 = offer1.AddMediaSection(
audio, SdpDirectionAttribute::kRecvonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SdpMediaSection& offer1Msection2 = offer1.AddMediaSection(
audio, SdpDirectionAttribute::kRecvonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SipccSdp answer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
SdpMediaSection& answer1Msection1 = answer1.AddMediaSection(
audio, SdpDirectionAttribute::kSendonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SdpMediaSection& answer1Msection2 = answer1.AddMediaSection(
audio, SdpDirectionAttribute::kSendonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
for (const auto& codec : codecs1) {
codec->mDirection = sdp::kSend;
offer1Msection1.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
codec->mChannels);
auto clone = WrapUnique(codec->Clone());
clone->mDirection = sdp::kRecv;
clone->AddToMediaSection(answer1Msection1);
}
for (const auto& codec : codecs2) {
codec->mDirection = sdp::kSend;
offer1Msection2.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
codec->mChannels);
auto clone = WrapUnique(codec->Clone());
clone->mDirection = sdp::kRecv;
clone->AddToMediaSection(answer1Msection2);
}
JsepTrack t1{audio, sdp::Direction::kRecv};
t1.PopulateCodecs(codecs1, false);
t1.RecvTrackSetLocal(offer1Msection1);
t1.RecvTrackSetRemote(answer1, answer1Msection1);
ASSERT_EQ(t1.Negotiate(answer1Msection1, answer1Msection1, offer1Msection1),
NS_OK);
JsepTrack t2{audio, sdp::Direction::kRecv};
t2.PopulateCodecs(codecs2, false);
t2.RecvTrackSetLocal(offer1Msection2);
t2.RecvTrackSetRemote(answer1, answer1Msection2);
ASSERT_EQ(t2.Negotiate(answer1Msection2, answer1Msection2, offer1Msection2),
NS_OK);
std::vector tracks{&t1, &t2};
JsepTrack::SetUniqueReceivePayloadTypes(tracks);
EXPECT_THAT(t1.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(1));
EXPECT_THAT(t1.GetDuplicateReceivePayloadTypes(), UnorderedElementsAre());
EXPECT_THAT(t2.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(2));
EXPECT_THAT(t2.GetDuplicateReceivePayloadTypes(), UnorderedElementsAre());
}
TEST(JsepTrackRecvPayloadTypesTest, DoubleTrackPTsAreDuplicates)
{
constexpr auto audio = SdpMediaSection::MediaType::kAudio;
std::vector<UniquePtr<JsepCodecDescription>> codecs1;
codecs1.emplace_back(
MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1, true));
std::vector<UniquePtr<JsepCodecDescription>> codecs2;
codecs2.emplace_back(
MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1, true));
SipccSdp offer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
SdpMediaSection& offer1Msection1 = offer1.AddMediaSection(
audio, SdpDirectionAttribute::kRecvonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SdpMediaSection& offer1Msection2 = offer1.AddMediaSection(
audio, SdpDirectionAttribute::kRecvonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SipccSdp answer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
SdpMediaSection& answer1Msection1 = answer1.AddMediaSection(
audio, SdpDirectionAttribute::kSendonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SdpMediaSection& answer1Msection2 = answer1.AddMediaSection(
audio, SdpDirectionAttribute::kSendonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
for (const auto& codec : codecs1) {
codec->mDirection = sdp::kSend;
offer1Msection1.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
codec->mChannels);
auto clone = WrapUnique(codec->Clone());
clone->mDirection = sdp::kRecv;
clone->AddToMediaSection(answer1Msection1);
}
for (const auto& codec : codecs2) {
codec->mDirection = sdp::kSend;
offer1Msection2.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
codec->mChannels);
auto clone = WrapUnique(codec->Clone());
clone->mDirection = sdp::kRecv;
clone->AddToMediaSection(answer1Msection2);
}
JsepTrack t1{audio, sdp::Direction::kRecv};
t1.PopulateCodecs(codecs1, false);
t1.RecvTrackSetLocal(offer1Msection1);
t1.RecvTrackSetRemote(answer1, answer1Msection1);
ASSERT_EQ(t1.Negotiate(answer1Msection1, answer1Msection1, offer1Msection1),
NS_OK);
JsepTrack t2{audio, sdp::Direction::kRecv};
t2.PopulateCodecs(codecs2, false);
t2.RecvTrackSetLocal(offer1Msection2);
t2.RecvTrackSetRemote(answer1, answer1Msection2);
ASSERT_EQ(t2.Negotiate(answer1Msection2, answer1Msection2, offer1Msection2),
NS_OK);
std::vector tracks{&t1, &t2};
JsepTrack::SetUniqueReceivePayloadTypes(tracks);
EXPECT_THAT(t1.GetUniqueReceivePayloadTypes(), UnorderedElementsAre());
EXPECT_THAT(t1.GetDuplicateReceivePayloadTypes(), UnorderedElementsAre(1));
EXPECT_THAT(t2.GetUniqueReceivePayloadTypes(), UnorderedElementsAre());
EXPECT_THAT(t2.GetDuplicateReceivePayloadTypes(), UnorderedElementsAre(1));
}
TEST(JsepTrackRecvPayloadTypesTest, DoubleTrackPTsOverlap)
{
constexpr auto audio = SdpMediaSection::MediaType::kAudio;
std::vector<UniquePtr<JsepCodecDescription>> codecs1;
codecs1.emplace_back(
MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1, true));
codecs1.emplace_back(
MakeUnique<JsepAudioCodecDescription>("2", "codec2", 48000, 1, true));
std::vector<UniquePtr<JsepCodecDescription>> codecs2;
codecs2.emplace_back(
MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1, true));
codecs2.emplace_back(
MakeUnique<JsepAudioCodecDescription>("3", "codec2", 48000, 1, true));
SipccSdp offer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
SdpMediaSection& offer1Msection1 = offer1.AddMediaSection(
audio, SdpDirectionAttribute::kRecvonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SdpMediaSection& offer1Msection2 = offer1.AddMediaSection(
audio, SdpDirectionAttribute::kRecvonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SipccSdp answer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
SdpMediaSection& answer1Msection1 = answer1.AddMediaSection(
audio, SdpDirectionAttribute::kSendonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SdpMediaSection& answer1Msection2 = answer1.AddMediaSection(
audio, SdpDirectionAttribute::kSendonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
for (const auto& codec : codecs1) {
codec->mDirection = sdp::kSend;
offer1Msection1.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
codec->mChannels);
auto clone = WrapUnique(codec->Clone());
clone->mDirection = sdp::kRecv;
clone->AddToMediaSection(answer1Msection1);
}
for (const auto& codec : codecs2) {
codec->mDirection = sdp::kSend;
offer1Msection2.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
codec->mChannels);
auto clone = WrapUnique(codec->Clone());
clone->mDirection = sdp::kRecv;
clone->AddToMediaSection(answer1Msection2);
}
JsepTrack t1{audio, sdp::Direction::kRecv};
t1.PopulateCodecs(codecs1, false);
t1.RecvTrackSetLocal(offer1Msection1);
t1.RecvTrackSetRemote(answer1, answer1Msection1);
ASSERT_EQ(t1.Negotiate(answer1Msection1, answer1Msection1, offer1Msection1),
NS_OK);
JsepTrack t2{audio, sdp::Direction::kRecv};
t2.PopulateCodecs(codecs2, false);
t2.RecvTrackSetLocal(offer1Msection2);
t2.RecvTrackSetRemote(answer1, answer1Msection2);
ASSERT_EQ(t2.Negotiate(answer1Msection2, answer1Msection2, offer1Msection2),
NS_OK);
std::vector tracks{&t1, &t2};
JsepTrack::SetUniqueReceivePayloadTypes(tracks);
EXPECT_THAT(t1.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(2));
EXPECT_THAT(t1.GetDuplicateReceivePayloadTypes(), UnorderedElementsAre(1));
EXPECT_THAT(t2.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(3));
EXPECT_THAT(t2.GetDuplicateReceivePayloadTypes(), UnorderedElementsAre(1));
}
TEST(JsepTrackRecvPayloadTypesTest, DoubleTrackPTsDuplicateAfterRenegotiation)
{
constexpr auto audio = SdpMediaSection::MediaType::kAudio;
std::vector<UniquePtr<JsepCodecDescription>> codecs1;
codecs1.emplace_back(
MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1, true));
codecs1.emplace_back(
MakeUnique<JsepAudioCodecDescription>("2", "codec2", 48000, 1, true));
std::vector<UniquePtr<JsepCodecDescription>> codecs2;
codecs2.emplace_back(
MakeUnique<JsepAudioCodecDescription>("3", "codec1", 48000, 1, true));
codecs2.emplace_back(
MakeUnique<JsepAudioCodecDescription>("4", "codec2", 48000, 1, true));
// First negotiation.
SipccSdp offer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
SdpMediaSection& offer1Msection1 = offer1.AddMediaSection(
audio, SdpDirectionAttribute::kRecvonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SdpMediaSection& offer1Msection2 = offer1.AddMediaSection(
audio, SdpDirectionAttribute::kRecvonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SipccSdp answer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
SdpMediaSection& answer1Msection1 = answer1.AddMediaSection(
audio, SdpDirectionAttribute::kSendonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SdpMediaSection& answer1Msection2 = answer1.AddMediaSection(
audio, SdpDirectionAttribute::kSendonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
for (const auto& codec : codecs1) {
codec->mDirection = sdp::kSend;
offer1Msection1.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
codec->mChannels);
auto clone = WrapUnique(codec->Clone());
clone->mDirection = sdp::kRecv;
clone->AddToMediaSection(answer1Msection1);
}
for (const auto& codec : codecs2) {
codec->mDirection = sdp::kSend;
offer1Msection2.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
codec->mChannels);
auto clone = WrapUnique(codec->Clone());
clone->mDirection = sdp::kRecv;
clone->AddToMediaSection(answer1Msection2);
}
// t1 and t2 use distinct payload types in the first negotiation.
JsepTrack t1{audio, sdp::Direction::kRecv};
t1.PopulateCodecs(codecs1, false);
t1.RecvTrackSetLocal(offer1Msection1);
t1.RecvTrackSetRemote(answer1, answer1Msection1);
ASSERT_EQ(t1.Negotiate(answer1Msection1, answer1Msection1, offer1Msection1),
NS_OK);
JsepTrack t2{audio, sdp::Direction::kRecv};
t2.PopulateCodecs(codecs2, false);
t2.RecvTrackSetLocal(offer1Msection2);
t2.RecvTrackSetRemote(answer1, answer1Msection2);
ASSERT_EQ(t2.Negotiate(answer1Msection2, answer1Msection2, offer1Msection2),
NS_OK);
std::vector tracks{&t1, &t2};
JsepTrack::SetUniqueReceivePayloadTypes(tracks);
EXPECT_THAT(t1.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(1, 2));
EXPECT_THAT(t1.GetDuplicateReceivePayloadTypes(), UnorderedElementsAre());
EXPECT_THAT(t2.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(3, 4));
EXPECT_THAT(t2.GetDuplicateReceivePayloadTypes(), UnorderedElementsAre());
// Second negotiation.
SipccSdp offer2(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
SdpMediaSection& offer2Msection1 = offer2.AddMediaSection(
audio, SdpDirectionAttribute::kRecvonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SdpMediaSection& offer2Msection2 = offer2.AddMediaSection(
audio, SdpDirectionAttribute::kRecvonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SipccSdp answer2(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
SdpMediaSection& answer2Msection1 = answer2.AddMediaSection(
audio, SdpDirectionAttribute::kSendonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
SdpMediaSection& answer2Msection2 = answer2.AddMediaSection(
audio, SdpDirectionAttribute::kSendonly, 0,
SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
for (const auto& codec : codecs1) {
codec->mDirection = sdp::kSend;
offer2Msection1.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
codec->mChannels);
auto clone = WrapUnique(codec->Clone());
clone->mDirection = sdp::kRecv;
clone->AddToMediaSection(answer2Msection1);
}
for (const auto& codec : codecs2) {
codec->mDirection = sdp::kSend;
offer2Msection2.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
codec->mChannels);
auto clone = WrapUnique(codec->Clone());
clone->mDirection = sdp::kRecv;
clone->AddToMediaSection(answer2Msection2);
}
t1.PopulateCodecs(codecs1, false);
t1.RecvTrackSetLocal(offer2Msection1);
t1.RecvTrackSetRemote(answer2, answer2Msection1);
ASSERT_EQ(t1.Negotiate(answer2Msection1, answer2Msection1, offer2Msection1),
NS_OK);
// Change t2 to use the same payload types as t1. Both tracks should now mark
// all their payload types as duplicates.
t2.PopulateCodecs(codecs1, false);
t2.RecvTrackSetLocal(offer2Msection2);
t2.RecvTrackSetRemote(answer2, answer2Msection2);
ASSERT_EQ(t2.Negotiate(answer2Msection2, answer2Msection2, offer2Msection2),
NS_OK);
std::vector newTracks{&t1, &t2};
JsepTrack::SetUniqueReceivePayloadTypes(newTracks);
EXPECT_THAT(t1.GetUniqueReceivePayloadTypes(), UnorderedElementsAre());
EXPECT_THAT(t1.GetDuplicateReceivePayloadTypes(), UnorderedElementsAre(1, 2));
EXPECT_THAT(t2.GetUniqueReceivePayloadTypes(), UnorderedElementsAre());
EXPECT_THAT(t2.GetDuplicateReceivePayloadTypes(), UnorderedElementsAre(1, 2));
}
} // namespace mozilla