summaryrefslogtreecommitdiffstats
path: root/media/webrtc/signaling/gtest/jsep_track_unittest.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /media/webrtc/signaling/gtest/jsep_track_unittest.cpp
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'media/webrtc/signaling/gtest/jsep_track_unittest.cpp')
-rw-r--r--media/webrtc/signaling/gtest/jsep_track_unittest.cpp1727
1 files changed, 1727 insertions, 0 deletions
diff --git a/media/webrtc/signaling/gtest/jsep_track_unittest.cpp b/media/webrtc/signaling/gtest/jsep_track_unittest.cpp
new file mode 100644
index 0000000000..cbb351c379
--- /dev/null
+++ b/media/webrtc/signaling/gtest/jsep_track_unittest.cpp
@@ -0,0 +1,1727 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nss.h"
+#include "ssl.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+
+#include "jsep/JsepTrack.h"
+#include "sdp/SipccSdp.h"
+#include "sdp/SipccSdpParser.h"
+#include "sdp/SdpHelper.h"
+
+namespace mozilla {
+
+class JsepTrackTestBase : public ::testing::Test {
+ public:
+ static void SetUpTestCase() {
+ NSS_NoDB_Init(nullptr);
+ NSS_SetDomesticPolicy();
+ }
+};
+
+class JsepTrackTest : public JsepTrackTestBase {
+ public:
+ JsepTrackTest()
+ : mSendOff(SdpMediaSection::kAudio, sdp::kSend),
+ mRecvOff(SdpMediaSection::kAudio, sdp::kRecv),
+ mSendAns(SdpMediaSection::kAudio, sdp::kSend),
+ mRecvAns(SdpMediaSection::kAudio, sdp::kRecv) {}
+
+ void TearDown() override {
+ if (::testing::UnitTest::GetInstance()
+ ->current_test_info()
+ ->result()
+ ->Failed()) {
+ if (mOffer) {
+ std::cerr << "Offer SDP: " << std::endl;
+ mOffer->Serialize(std::cerr);
+ }
+
+ if (mAnswer) {
+ std::cerr << "Answer SDP: " << std::endl;
+ mAnswer->Serialize(std::cerr);
+ }
+ }
+ }
+
+ std::vector<UniquePtr<JsepCodecDescription>> MakeCodecs(
+ bool addFecCodecs = false, bool preferRed = false,
+ bool addDtmfCodec = false) const {
+ std::vector<UniquePtr<JsepCodecDescription>> results;
+ results.emplace_back(JsepAudioCodecDescription::CreateDefaultOpus());
+ results.emplace_back(JsepAudioCodecDescription::CreateDefaultG722());
+ if (addDtmfCodec) {
+ results.emplace_back(
+ JsepAudioCodecDescription::CreateDefaultTelephoneEvent());
+ }
+
+ if (addFecCodecs && preferRed) {
+ results.emplace_back(JsepVideoCodecDescription::CreateDefaultRed());
+ }
+
+ results.emplace_back(JsepVideoCodecDescription::CreateDefaultVP8(false));
+ results.emplace_back(JsepVideoCodecDescription::CreateDefaultH264_1(false));
+
+ if (addFecCodecs) {
+ if (!preferRed) {
+ results.emplace_back(JsepVideoCodecDescription::CreateDefaultRed());
+ }
+ results.emplace_back(JsepVideoCodecDescription::CreateDefaultUlpFec());
+ }
+
+ results.emplace_back(new JsepApplicationCodecDescription(
+ "webrtc-datachannel", 256, 5999, 499));
+
+ return results;
+ }
+
+ void Init(SdpMediaSection::MediaType type) {
+ InitCodecs();
+ InitTracks(type);
+ InitSdp(type);
+ }
+
+ void InitCodecs() {
+ mOffCodecs = MakeCodecs();
+ mAnsCodecs = MakeCodecs();
+ }
+
+ void InitTracks(SdpMediaSection::MediaType type) {
+ mSendOff = JsepTrack(type, sdp::kSend);
+ if (type != SdpMediaSection::MediaType::kApplication) {
+ mSendOff.UpdateStreamIds(std::vector<std::string>(1, "stream_id"));
+ }
+ mRecvOff = JsepTrack(type, sdp::kRecv);
+ mSendOff.PopulateCodecs(mOffCodecs);
+ mRecvOff.PopulateCodecs(mOffCodecs);
+
+ mSendAns = JsepTrack(type, sdp::kSend);
+ if (type != SdpMediaSection::MediaType::kApplication) {
+ mSendAns.UpdateStreamIds(std::vector<std::string>(1, "stream_id"));
+ }
+ mRecvAns = JsepTrack(type, sdp::kRecv);
+ mSendAns.PopulateCodecs(mAnsCodecs);
+ mRecvAns.PopulateCodecs(mAnsCodecs);
+ }
+
+ void InitSdp(SdpMediaSection::MediaType type) {
+ std::vector<std::string> msids(1, "*");
+ std::string error;
+ SdpHelper helper(&error);
+
+ mOffer.reset(new SipccSdp(SdpOrigin("", 0, 0, sdp::kIPv4, "")));
+ mOffer->AddMediaSection(type, SdpDirectionAttribute::kSendrecv, 0,
+ SdpHelper::GetProtocolForMediaType(type),
+ sdp::kIPv4, "0.0.0.0");
+ // JsepTrack doesn't set msid-semantic
+ helper.SetupMsidSemantic(msids, mOffer.get());
+
+ mAnswer.reset(new SipccSdp(SdpOrigin("", 0, 0, sdp::kIPv4, "")));
+ mAnswer->AddMediaSection(type, SdpDirectionAttribute::kSendrecv, 0,
+ SdpHelper::GetProtocolForMediaType(type),
+ sdp::kIPv4, "0.0.0.0");
+ // JsepTrack doesn't set msid-semantic
+ helper.SetupMsidSemantic(msids, mAnswer.get());
+ }
+
+ SdpMediaSection& GetOffer() { return mOffer->GetMediaSection(0); }
+
+ SdpMediaSection& GetAnswer() { return mAnswer->GetMediaSection(0); }
+
+ void CreateOffer() {
+ mSendOff.AddToOffer(mSsrcGenerator, &GetOffer());
+ mRecvOff.AddToOffer(mSsrcGenerator, &GetOffer());
+ }
+
+ void CreateAnswer() {
+ if (mRecvAns.GetMediaType() != SdpMediaSection::MediaType::kApplication) {
+ mRecvAns.RecvTrackSetRemote(*mOffer, GetOffer());
+ mSendAns.SendTrackSetRemote(mSsrcGenerator, GetOffer());
+ }
+
+ mSendAns.AddToAnswer(GetOffer(), mSsrcGenerator, &GetAnswer());
+ mRecvAns.AddToAnswer(GetOffer(), mSsrcGenerator, &GetAnswer());
+ }
+
+ void Negotiate() {
+ if (mRecvOff.GetMediaType() != SdpMediaSection::MediaType::kApplication) {
+ mRecvOff.RecvTrackSetRemote(*mAnswer, GetAnswer());
+ mSendOff.SendTrackSetRemote(mSsrcGenerator, GetAnswer());
+ }
+
+ if (GetAnswer().IsSending()) {
+ mSendAns.Negotiate(GetAnswer(), GetOffer(), GetAnswer());
+ mRecvOff.Negotiate(GetAnswer(), GetAnswer(), GetOffer());
+ }
+
+ if (GetAnswer().IsReceiving()) {
+ mRecvAns.Negotiate(GetAnswer(), GetOffer(), GetAnswer());
+ mSendOff.Negotiate(GetAnswer(), GetAnswer(), GetOffer());
+ }
+ }
+
+ void OfferAnswer() {
+ CreateOffer();
+ CreateAnswer();
+ Negotiate();
+ SanityCheck();
+ }
+
+ // TODO: Look into writing a macro that wraps an ASSERT_ and returns false
+ // if it fails (probably requires writing a bool-returning function that
+ // takes a void-returning lambda with a bool outparam, which will in turn
+ // invokes the ASSERT_)
+ static void CheckEncodingCount(size_t expected, const JsepTrack& send,
+ const JsepTrack& recv) {
+ if (expected) {
+ ASSERT_TRUE(send.GetNegotiatedDetails());
+ ASSERT_TRUE(recv.GetNegotiatedDetails());
+ }
+
+ if (!send.GetStreamIds().empty() && send.GetNegotiatedDetails()) {
+ ASSERT_EQ(expected, send.GetNegotiatedDetails()->GetEncodingCount());
+ }
+
+ if (!recv.GetStreamIds().empty() && recv.GetNegotiatedDetails()) {
+ ASSERT_EQ(expected, recv.GetNegotiatedDetails()->GetEncodingCount());
+ }
+ }
+
+ void CheckOffEncodingCount(size_t expected) const {
+ CheckEncodingCount(expected, mSendOff, mRecvAns);
+ }
+
+ void CheckAnsEncodingCount(size_t expected) const {
+ CheckEncodingCount(expected, mSendAns, mRecvOff);
+ }
+
+ UniquePtr<JsepCodecDescription> GetCodec(const JsepTrack& track,
+ SdpMediaSection::MediaType type,
+ size_t expectedSize,
+ size_t codecIndex) const {
+ if (!track.GetNegotiatedDetails() ||
+ track.GetNegotiatedDetails()->GetEncodingCount() != 1U ||
+ track.GetMediaType() != type) {
+ return nullptr;
+ }
+ const auto& codecs =
+ track.GetNegotiatedDetails()->GetEncoding(0).GetCodecs();
+ // it should not be possible for codecs to have a different type
+ // than the track, but we'll check the codec here just in case.
+ if (codecs.size() != expectedSize || codecIndex >= expectedSize ||
+ codecs[codecIndex]->Type() != type) {
+ return nullptr;
+ }
+ return UniquePtr<JsepCodecDescription>(codecs[codecIndex]->Clone());
+ }
+
+ UniquePtr<JsepVideoCodecDescription> GetVideoCodec(
+ const JsepTrack& track, size_t expectedSize = 1,
+ size_t codecIndex = 0) const {
+ auto codec =
+ GetCodec(track, SdpMediaSection::kVideo, expectedSize, codecIndex);
+ return UniquePtr<JsepVideoCodecDescription>(
+ static_cast<JsepVideoCodecDescription*>(codec.release()));
+ }
+
+ UniquePtr<JsepAudioCodecDescription> GetAudioCodec(
+ const JsepTrack& track, size_t expectedSize = 1,
+ size_t codecIndex = 0) const {
+ auto codec =
+ GetCodec(track, SdpMediaSection::kAudio, expectedSize, codecIndex);
+ return UniquePtr<JsepAudioCodecDescription>(
+ static_cast<JsepAudioCodecDescription*>(codec.release()));
+ }
+
+ void CheckOtherFbExists(const JsepVideoCodecDescription& videoCodec,
+ SdpRtcpFbAttributeList::Type type) const {
+ for (const auto& fb : videoCodec.mOtherFbTypes) {
+ if (fb.type == type) {
+ return; // found the RtcpFb type, so stop looking
+ }
+ }
+ FAIL(); // RtcpFb type not found
+ }
+
+ void SanityCheckRtcpFbs(const JsepVideoCodecDescription& a,
+ const JsepVideoCodecDescription& b) const {
+ ASSERT_EQ(a.mNackFbTypes.size(), b.mNackFbTypes.size());
+ ASSERT_EQ(a.mAckFbTypes.size(), b.mAckFbTypes.size());
+ ASSERT_EQ(a.mCcmFbTypes.size(), b.mCcmFbTypes.size());
+ ASSERT_EQ(a.mOtherFbTypes.size(), b.mOtherFbTypes.size());
+ }
+
+ void SanityCheckCodecs(const JsepCodecDescription& a,
+ const JsepCodecDescription& b) const {
+#define MSG \
+ "For codecs " << a.mName << " (" << a.mDirection << ") and " << b.mName \
+ << " (" << b.mDirection << ")"
+ ASSERT_EQ(a.Type(), b.Type()) << MSG;
+ if (a.Type() != SdpMediaSection::kApplication) {
+ ASSERT_EQ(a.mDefaultPt, b.mDefaultPt) << MSG;
+ }
+ ASSERT_EQ(a.mName, b.mName);
+ if (!mExpectDifferingFmtp) {
+ ASSERT_EQ(a.mSdpFmtpLine, b.mSdpFmtpLine) << MSG;
+ }
+ ASSERT_EQ(a.mClock, b.mClock) << MSG;
+ ASSERT_EQ(a.mChannels, b.mChannels) << MSG;
+ ASSERT_NE(a.mDirection, b.mDirection) << MSG;
+ // These constraints are for fmtp and rid, which _are_ signaled
+ ASSERT_EQ(a.mConstraints, b.mConstraints) << MSG;
+#undef MSG
+
+ if (a.Type() == SdpMediaSection::kVideo) {
+ SanityCheckRtcpFbs(static_cast<const JsepVideoCodecDescription&>(a),
+ static_cast<const JsepVideoCodecDescription&>(b));
+ }
+ }
+
+ void SanityCheckEncodings(const JsepTrackEncoding& a,
+ const JsepTrackEncoding& b) const {
+ ASSERT_EQ(a.GetCodecs().size(), b.GetCodecs().size());
+ for (size_t i = 0; i < a.GetCodecs().size(); ++i) {
+ SanityCheckCodecs(*a.GetCodecs()[i], *b.GetCodecs()[i]);
+ }
+
+ ASSERT_EQ(a.mRid, b.mRid);
+ // mConstraints will probably differ, since they are not signaled to the
+ // other side.
+ }
+
+ void SanityCheckNegotiatedDetails(const JsepTrackNegotiatedDetails& a,
+ const JsepTrackNegotiatedDetails& b) const {
+ ASSERT_EQ(a.GetEncodingCount(), b.GetEncodingCount());
+ for (size_t i = 0; i < a.GetEncodingCount(); ++i) {
+ SanityCheckEncodings(a.GetEncoding(i), b.GetEncoding(i));
+ }
+
+ ASSERT_EQ(a.GetUniquePayloadTypes().size(),
+ b.GetUniquePayloadTypes().size());
+ for (size_t i = 0; i < a.GetUniquePayloadTypes().size(); ++i) {
+ ASSERT_EQ(a.GetUniquePayloadTypes()[i], b.GetUniquePayloadTypes()[i]);
+ }
+ }
+
+ void SanityCheckTracks(const JsepTrack& a, const JsepTrack& b) const {
+ if (!a.GetNegotiatedDetails()) {
+ ASSERT_FALSE(!!b.GetNegotiatedDetails());
+ return;
+ }
+
+ ASSERT_TRUE(!!a.GetNegotiatedDetails());
+ ASSERT_TRUE(!!b.GetNegotiatedDetails());
+ ASSERT_EQ(a.GetMediaType(), b.GetMediaType());
+ ASSERT_EQ(a.GetStreamIds(), b.GetStreamIds());
+ ASSERT_EQ(a.GetCNAME(), b.GetCNAME());
+ ASSERT_NE(a.GetDirection(), b.GetDirection());
+ ASSERT_EQ(a.GetSsrcs().size(), b.GetSsrcs().size());
+ for (size_t i = 0; i < a.GetSsrcs().size(); ++i) {
+ ASSERT_EQ(a.GetSsrcs()[i], b.GetSsrcs()[i]);
+ }
+
+ SanityCheckNegotiatedDetails(*a.GetNegotiatedDetails(),
+ *b.GetNegotiatedDetails());
+ }
+
+ void SanityCheck() const {
+ SanityCheckTracks(mSendOff, mRecvAns);
+ SanityCheckTracks(mRecvOff, mSendAns);
+ }
+
+ protected:
+ JsepTrack mSendOff;
+ JsepTrack mRecvOff;
+ JsepTrack mSendAns;
+ JsepTrack mRecvAns;
+ std::vector<UniquePtr<JsepCodecDescription>> mOffCodecs;
+ std::vector<UniquePtr<JsepCodecDescription>> mAnsCodecs;
+ UniquePtr<Sdp> mOffer;
+ UniquePtr<Sdp> mAnswer;
+ SsrcGenerator mSsrcGenerator;
+ bool mExpectDifferingFmtp = false;
+};
+
+TEST_F(JsepTrackTestBase, CreateDestroy) {}
+
+TEST_F(JsepTrackTest, CreateDestroy) { Init(SdpMediaSection::kAudio); }
+
+TEST_F(JsepTrackTest, AudioNegotiation) {
+ Init(SdpMediaSection::kAudio);
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+}
+
+TEST_F(JsepTrackTest, VideoNegotiation) {
+ Init(SdpMediaSection::kVideo);
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+}
+
+class CheckForCodecType {
+ public:
+ explicit CheckForCodecType(SdpMediaSection::MediaType type, bool* result)
+ : mResult(result), mType(type) {}
+
+ void operator()(const UniquePtr<JsepCodecDescription>& codec) {
+ if (codec->Type() == mType) {
+ *mResult = true;
+ }
+ }
+
+ private:
+ bool* mResult;
+ SdpMediaSection::MediaType mType;
+};
+
+TEST_F(JsepTrackTest, CheckForMismatchedAudioCodecAndVideoTrack) {
+ std::vector<UniquePtr<JsepCodecDescription>> offerCodecs;
+
+ // make codecs including telephone-event (an audio codec)
+ offerCodecs = MakeCodecs(false, false, true);
+ JsepTrack videoTrack(SdpMediaSection::kVideo, sdp::kSend);
+ videoTrack.UpdateStreamIds(std::vector<std::string>(1, "stream_id"));
+ // populate codecs and then make sure we don't have any audio codecs
+ // in the video track
+ videoTrack.PopulateCodecs(offerCodecs);
+
+ bool found = false;
+ videoTrack.ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
+ ASSERT_FALSE(found);
+
+ found = false;
+ videoTrack.ForEachCodec(CheckForCodecType(SdpMediaSection::kVideo, &found));
+ ASSERT_TRUE(found); // for sanity, make sure we did find video codecs
+}
+
+TEST_F(JsepTrackTest, CheckVideoTrackWithHackedDtmfSdp) {
+ Init(SdpMediaSection::kVideo);
+ CreateOffer();
+ // make sure we don't find sdp containing telephone-event in video track
+ ASSERT_EQ(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ // force audio codec telephone-event into video m= section of offer
+ GetOffer().AddCodec("101", "telephone-event", 8000, 1);
+ // make sure we _do_ find sdp containing telephone-event in video track
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ CreateAnswer();
+ // make sure we don't find sdp containing telephone-event in video track
+ ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ // force audio codec telephone-event into video m= section of answer
+ GetAnswer().AddCodec("101", "telephone-event", 8000, 1);
+ // make sure we _do_ find sdp containing telephone-event in video track
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ Negotiate();
+ SanityCheck();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ // make sure we still don't find any audio codecs in the video track after
+ // hacking the sdp
+ bool found = false;
+ mSendOff.ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
+ ASSERT_FALSE(found);
+ mRecvOff.ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
+ ASSERT_FALSE(found);
+ mSendAns.ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
+ ASSERT_FALSE(found);
+ mRecvAns.ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
+ ASSERT_FALSE(found);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationOffererDtmf) {
+ mOffCodecs = MakeCodecs(false, false, true);
+ mAnsCodecs = MakeCodecs(false, false, false);
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+ OfferAnswer();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ ASSERT_NE(mOffer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=fmtp:101"), std::string::npos);
+
+ UniquePtr<JsepAudioCodecDescription> track;
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 2, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 2, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 2, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 2, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 2, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 2, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 2, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 2, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationAnswererDtmf) {
+ mOffCodecs = MakeCodecs(false, false, false);
+ mAnsCodecs = MakeCodecs(false, false, true);
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+ OfferAnswer();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_EQ(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ ASSERT_EQ(mOffer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=fmtp:101"), std::string::npos);
+
+ UniquePtr<JsepAudioCodecDescription> track;
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 2, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 2, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 2, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 2, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 2, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 2, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 2, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 2, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationOffererAnswererDtmf) {
+ mOffCodecs = MakeCodecs(false, false, true);
+ mAnsCodecs = MakeCodecs(false, false, true);
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+ OfferAnswer();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ ASSERT_NE(mOffer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
+
+ UniquePtr<JsepAudioCodecDescription> track;
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationDtmfOffererNoFmtpAnswererFmtp) {
+ mOffCodecs = MakeCodecs(false, false, true);
+ mAnsCodecs = MakeCodecs(false, false, true);
+
+ mExpectDifferingFmtp = true;
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+
+ CreateOffer();
+ GetOffer().RemoveFmtp("101");
+
+ CreateAnswer();
+
+ Negotiate();
+ SanityCheck();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ ASSERT_EQ(mOffer->ToString().find("a=fmtp:101"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
+
+ UniquePtr<JsepAudioCodecDescription> track;
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_EQ("0-15", track->mSdpFmtpLine.valueOr("nothing"));
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_EQ("0-15", track->mSdpFmtpLine.valueOr("nothing"));
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationDtmfOffererFmtpAnswererNoFmtp) {
+ mOffCodecs = MakeCodecs(false, false, true);
+ mAnsCodecs = MakeCodecs(false, false, true);
+
+ mExpectDifferingFmtp = true;
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+
+ CreateOffer();
+
+ CreateAnswer();
+ GetAnswer().RemoveFmtp("101");
+
+ Negotiate();
+ SanityCheck();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ ASSERT_NE(mOffer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=fmtp:101"), std::string::npos);
+
+ UniquePtr<JsepAudioCodecDescription> track;
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_EQ("0-15", track->mSdpFmtpLine.valueOr("nothing"));
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_EQ("0-15", track->mSdpFmtpLine.valueOr("nothing"));
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationDtmfOffererNoFmtpAnswererNoFmtp) {
+ mOffCodecs = MakeCodecs(false, false, true);
+ mAnsCodecs = MakeCodecs(false, false, true);
+
+ mExpectDifferingFmtp = true;
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+
+ CreateOffer();
+ GetOffer().RemoveFmtp("101");
+
+ CreateAnswer();
+ GetAnswer().RemoveFmtp("101");
+
+ Negotiate();
+ SanityCheck();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ ASSERT_EQ(mOffer->ToString().find("a=fmtp:101"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=fmtp:101"), std::string::npos);
+
+ UniquePtr<JsepAudioCodecDescription> track;
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 0)));
+ ASSERT_EQ("109", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 1)));
+ ASSERT_EQ("9", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
+ ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
+ ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
+ ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 2)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
+}
+
+TEST_F(JsepTrackTest, VideoNegotationOffererFEC) {
+ mOffCodecs = MakeCodecs(true);
+ mAnsCodecs = MakeCodecs(false);
+
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
+
+ UniquePtr<JsepVideoCodecDescription> track;
+ ASSERT_TRUE((track = GetVideoCodec(mSendOff, 2, 0)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 2, 0)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mSendAns, 2, 0)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 2, 0)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mSendOff, 2, 1)));
+ ASSERT_EQ("126", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 2, 1)));
+ ASSERT_EQ("126", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mSendAns, 2, 1)));
+ ASSERT_EQ("126", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 2, 1)));
+ ASSERT_EQ("126", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, VideoNegotationAnswererFEC) {
+ mOffCodecs = MakeCodecs(false);
+ mAnsCodecs = MakeCodecs(true);
+
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_EQ(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
+ ASSERT_EQ(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
+
+ UniquePtr<JsepVideoCodecDescription> track;
+ ASSERT_TRUE((track = GetVideoCodec(mSendOff, 2, 0)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 2, 0)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mSendAns, 2, 0)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 2, 0)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mSendOff, 2, 1)));
+ ASSERT_EQ("126", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 2, 1)));
+ ASSERT_EQ("126", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mSendAns, 2, 1)));
+ ASSERT_EQ("126", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 2, 1)));
+ ASSERT_EQ("126", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFEC) {
+ mOffCodecs = MakeCodecs(true);
+ mAnsCodecs = MakeCodecs(true);
+
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
+
+ UniquePtr<JsepVideoCodecDescription> track;
+ ASSERT_TRUE((track = GetVideoCodec(mSendOff, 4)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 4)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mSendAns, 4)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 4)));
+ ASSERT_EQ("120", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFECPreferred) {
+ mOffCodecs = MakeCodecs(true, true);
+ mAnsCodecs = MakeCodecs(true);
+
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
+
+ UniquePtr<JsepVideoCodecDescription> track;
+ // We should have 4 codecs, the first of which is VP8, because having a
+ // pseudo codec come first is silly.
+ ASSERT_TRUE((track = GetVideoCodec(mSendOff, 4)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 4)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mSendAns, 4)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 4)));
+ ASSERT_EQ("120", track->mDefaultPt);
+}
+
+// Make sure we only put the right things in the fmtp:122 120/.... line
+TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFECMismatch) {
+ mOffCodecs = MakeCodecs(true, true);
+ mAnsCodecs = MakeCodecs(true);
+ // remove h264 from answer codecs
+ ASSERT_EQ("H264", mAnsCodecs[3]->mName);
+ mAnsCodecs.erase(mAnsCodecs.begin() + 3);
+
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
+
+ // We should have 3 codecs, the first of which is VP8, because having a
+ // pseudo codec come first is silly.
+ UniquePtr<JsepVideoCodecDescription> track;
+ ASSERT_TRUE((track = GetVideoCodec(mSendOff, 3)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 3)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mSendAns, 3)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 3)));
+ ASSERT_EQ("120", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFECZeroVP9Codec) {
+ mOffCodecs = MakeCodecs(true);
+ auto vp9 = JsepVideoCodecDescription::CreateDefaultVP9(false);
+ vp9->mDefaultPt = "0";
+ mOffCodecs.push_back(std::move(vp9));
+
+ ASSERT_EQ(8U, mOffCodecs.size());
+ JsepVideoCodecDescription& red =
+ static_cast<JsepVideoCodecDescription&>(*mOffCodecs[4]);
+ ASSERT_EQ("red", red.mName);
+
+ mAnsCodecs = MakeCodecs(true);
+
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
+}
+
+TEST_F(JsepTrackTest, VideoNegotiationOfferRemb) {
+ InitCodecs();
+ // enable remb on the offer codecs
+ ((JsepVideoCodecDescription&)*mOffCodecs[2]).EnableRemb();
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ // make sure REMB is on offer and not on answer
+ ASSERT_NE(mOffer->ToString().find("a=rtcp-fb:120 goog-remb"),
+ std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=rtcp-fb:120 goog-remb"),
+ std::string::npos);
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ UniquePtr<JsepVideoCodecDescription> codec;
+ ASSERT_TRUE((codec = GetVideoCodec(mSendOff, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvAns, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+ ASSERT_TRUE((codec = GetVideoCodec(mSendAns, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+}
+
+TEST_F(JsepTrackTest, VideoNegotiationAnswerRemb) {
+ InitCodecs();
+ // enable remb on the answer codecs
+ ((JsepVideoCodecDescription&)*mAnsCodecs[2]).EnableRemb();
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ // make sure REMB is not on offer and not on answer
+ ASSERT_EQ(mOffer->ToString().find("a=rtcp-fb:120 goog-remb"),
+ std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=rtcp-fb:120 goog-remb"),
+ std::string::npos);
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ UniquePtr<JsepVideoCodecDescription> codec;
+ ASSERT_TRUE((codec = GetVideoCodec(mSendOff, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvAns, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+ ASSERT_TRUE((codec = GetVideoCodec(mSendAns, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+}
+
+TEST_F(JsepTrackTest, VideoNegotiationOfferAnswerRemb) {
+ InitCodecs();
+ // enable remb on the offer and answer codecs
+ ((JsepVideoCodecDescription&)*mOffCodecs[2]).EnableRemb();
+ ((JsepVideoCodecDescription&)*mAnsCodecs[2]).EnableRemb();
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ // make sure REMB is on offer and on answer
+ ASSERT_NE(mOffer->ToString().find("a=rtcp-fb:120 goog-remb"),
+ std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtcp-fb:120 goog-remb"),
+ std::string::npos);
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ UniquePtr<JsepVideoCodecDescription> codec;
+ ASSERT_TRUE((codec = GetVideoCodec(mSendOff, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
+ CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kRemb);
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvAns, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
+ CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kRemb);
+ ASSERT_TRUE((codec = GetVideoCodec(mSendAns, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
+ CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kRemb);
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
+ CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kRemb);
+}
+
+TEST_F(JsepTrackTest, VideoNegotiationOfferTransportCC) {
+ InitCodecs();
+ // enable TransportCC on the offer codecs
+ ((JsepVideoCodecDescription&)*mOffCodecs[2]).EnableTransportCC();
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ // make sure TransportCC is on offer and not on answer
+ ASSERT_NE(mOffer->ToString().find("a=rtcp-fb:120 transport-cc"),
+ std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=rtcp-fb:120 transport-cc"),
+ std::string::npos);
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ UniquePtr<JsepVideoCodecDescription> codec;
+ ASSERT_TRUE((codec = GetVideoCodec(mSendOff, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvAns, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+ ASSERT_TRUE((codec = GetVideoCodec(mSendAns, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+}
+
+TEST_F(JsepTrackTest, VideoNegotiationAnswerTransportCC) {
+ InitCodecs();
+ // enable TransportCC on the answer codecs
+ ((JsepVideoCodecDescription&)*mAnsCodecs[2]).EnableTransportCC();
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ // make sure TransportCC is not on offer and not on answer
+ ASSERT_EQ(mOffer->ToString().find("a=rtcp-fb:120 transport-cc"),
+ std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=rtcp-fb:120 transport-cc"),
+ std::string::npos);
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ UniquePtr<JsepVideoCodecDescription> codec;
+ ASSERT_TRUE((codec = GetVideoCodec(mSendOff, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvAns, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+ ASSERT_TRUE((codec = GetVideoCodec(mSendAns, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
+}
+
+TEST_F(JsepTrackTest, VideoNegotiationOfferAnswerTransportCC) {
+ InitCodecs();
+ // enable TransportCC on the offer and answer codecs
+ ((JsepVideoCodecDescription&)*mOffCodecs[2]).EnableTransportCC();
+ ((JsepVideoCodecDescription&)*mAnsCodecs[2]).EnableTransportCC();
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ // make sure TransportCC is on offer and on answer
+ ASSERT_NE(mOffer->ToString().find("a=rtcp-fb:120 transport-cc"),
+ std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtcp-fb:120 transport-cc"),
+ std::string::npos);
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ UniquePtr<JsepVideoCodecDescription> codec;
+ ASSERT_TRUE((codec = GetVideoCodec(mSendOff, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
+ CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kTransportCC);
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvAns, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
+ CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kTransportCC);
+ ASSERT_TRUE((codec = GetVideoCodec(mSendAns, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
+ CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kTransportCC);
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 2, 0)));
+ ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
+ CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kTransportCC);
+}
+
+TEST_F(JsepTrackTest, AudioOffSendonlyAnsRecvonly) {
+ Init(SdpMediaSection::kAudio);
+ GetOffer().SetDirection(SdpDirectionAttribute::kSendonly);
+ GetAnswer().SetDirection(SdpDirectionAttribute::kRecvonly);
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(0);
+}
+
+TEST_F(JsepTrackTest, VideoOffSendonlyAnsRecvonly) {
+ Init(SdpMediaSection::kVideo);
+ GetOffer().SetDirection(SdpDirectionAttribute::kSendonly);
+ GetAnswer().SetDirection(SdpDirectionAttribute::kRecvonly);
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(0);
+}
+
+TEST_F(JsepTrackTest, AudioOffSendrecvAnsRecvonly) {
+ Init(SdpMediaSection::kAudio);
+ GetAnswer().SetDirection(SdpDirectionAttribute::kRecvonly);
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(0);
+}
+
+TEST_F(JsepTrackTest, VideoOffSendrecvAnsRecvonly) {
+ Init(SdpMediaSection::kVideo);
+ GetAnswer().SetDirection(SdpDirectionAttribute::kRecvonly);
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(0);
+}
+
+TEST_F(JsepTrackTest, AudioOffRecvonlyAnsSendonly) {
+ Init(SdpMediaSection::kAudio);
+ GetOffer().SetDirection(SdpDirectionAttribute::kRecvonly);
+ GetAnswer().SetDirection(SdpDirectionAttribute::kSendonly);
+ OfferAnswer();
+ CheckOffEncodingCount(0);
+ CheckAnsEncodingCount(1);
+}
+
+TEST_F(JsepTrackTest, VideoOffRecvonlyAnsSendonly) {
+ Init(SdpMediaSection::kVideo);
+ GetOffer().SetDirection(SdpDirectionAttribute::kRecvonly);
+ GetAnswer().SetDirection(SdpDirectionAttribute::kSendonly);
+ OfferAnswer();
+ CheckOffEncodingCount(0);
+ CheckAnsEncodingCount(1);
+}
+
+TEST_F(JsepTrackTest, AudioOffSendrecvAnsSendonly) {
+ Init(SdpMediaSection::kAudio);
+ GetAnswer().SetDirection(SdpDirectionAttribute::kSendonly);
+ OfferAnswer();
+ CheckOffEncodingCount(0);
+ CheckAnsEncodingCount(1);
+}
+
+TEST_F(JsepTrackTest, VideoOffSendrecvAnsSendonly) {
+ Init(SdpMediaSection::kVideo);
+ GetAnswer().SetDirection(SdpDirectionAttribute::kSendonly);
+ OfferAnswer();
+ CheckOffEncodingCount(0);
+ CheckAnsEncodingCount(1);
+}
+
+TEST_F(JsepTrackTest, DataChannelDraft05) {
+ mOffCodecs = MakeCodecs(false, false, false);
+ mAnsCodecs = MakeCodecs(false, false, false);
+ InitTracks(SdpMediaSection::kApplication);
+
+ mOffer.reset(new SipccSdp(SdpOrigin("", 0, 0, sdp::kIPv4, "")));
+ mOffer->AddMediaSection(SdpMediaSection::kApplication,
+ SdpDirectionAttribute::kSendrecv, 0,
+ SdpMediaSection::kDtlsSctp, sdp::kIPv4, "0.0.0.0");
+ mAnswer.reset(new SipccSdp(SdpOrigin("", 0, 0, sdp::kIPv4, "")));
+ mAnswer->AddMediaSection(SdpMediaSection::kApplication,
+ SdpDirectionAttribute::kSendrecv, 0,
+ SdpMediaSection::kDtlsSctp, sdp::kIPv4, "0.0.0.0");
+
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(std::string::npos,
+ mOffer->ToString().find("a=sctpmap:5999 webrtc-datachannel 256"));
+ ASSERT_NE(std::string::npos,
+ mAnswer->ToString().find("a=sctpmap:5999 webrtc-datachannel 256"));
+ // Note: this is testing for a workaround, see bug 1335262 for details
+ ASSERT_NE(std::string::npos,
+ mOffer->ToString().find("a=max-message-size:499"));
+ ASSERT_NE(std::string::npos,
+ mAnswer->ToString().find("a=max-message-size:499"));
+ ASSERT_EQ(std::string::npos, mOffer->ToString().find("a=sctp-port"));
+ ASSERT_EQ(std::string::npos, mAnswer->ToString().find("a=sctp-port"));
+}
+
+TEST_F(JsepTrackTest, DataChannelDraft21) {
+ Init(SdpMediaSection::kApplication);
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(std::string::npos, mOffer->ToString().find("a=sctp-port:5999"));
+ ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=sctp-port:5999"));
+ ASSERT_NE(std::string::npos,
+ mOffer->ToString().find("a=max-message-size:499"));
+ ASSERT_NE(std::string::npos,
+ mAnswer->ToString().find("a=max-message-size:499"));
+ ASSERT_EQ(std::string::npos, mOffer->ToString().find("a=sctpmap"));
+ ASSERT_EQ(std::string::npos, mAnswer->ToString().find("a=sctpmap"));
+}
+
+TEST_F(JsepTrackTest, DataChannelDraft21AnswerWithDifferentPort) {
+ mOffCodecs = MakeCodecs(false, false, false);
+ mAnsCodecs = MakeCodecs(false, false, false);
+
+ mOffCodecs.pop_back();
+ mOffCodecs.emplace_back(new JsepApplicationCodecDescription(
+ "webrtc-datachannel", 256, 4555, 10544));
+
+ InitTracks(SdpMediaSection::kApplication);
+ InitSdp(SdpMediaSection::kApplication);
+
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(std::string::npos, mOffer->ToString().find("a=sctp-port:4555"));
+ ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=sctp-port:5999"));
+ ASSERT_NE(std::string::npos,
+ mOffer->ToString().find("a=max-message-size:10544"));
+ ASSERT_NE(std::string::npos,
+ mAnswer->ToString().find("a=max-message-size:499"));
+ ASSERT_EQ(std::string::npos, mOffer->ToString().find("a=sctpmap"));
+ ASSERT_EQ(std::string::npos, mAnswer->ToString().find("a=sctpmap"));
+}
+
+TEST_F(JsepTrackTest, SimulcastRejected) {
+ Init(SdpMediaSection::kVideo);
+ std::vector<std::string> rids;
+ rids.push_back("foo");
+ rids.push_back("bar");
+ mSendOff.SetRids(rids);
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+}
+
+TEST_F(JsepTrackTest, SimulcastPrevented) {
+ Init(SdpMediaSection::kVideo);
+ std::vector<std::string> rids;
+ rids.push_back("foo");
+ rids.push_back("bar");
+ mSendAns.SetRids(rids);
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+}
+
+TEST_F(JsepTrackTest, SimulcastOfferer) {
+ Init(SdpMediaSection::kVideo);
+ std::vector<std::string> rids;
+ rids.push_back("foo");
+ rids.push_back("bar");
+ mSendOff.SetRids(rids);
+ CreateOffer();
+ CreateAnswer();
+ // Add simulcast/rid to answer
+ mRecvAns.AddToMsection(rids, sdp::kRecv, mSsrcGenerator, false, &GetAnswer());
+ Negotiate();
+ ASSERT_TRUE(mSendOff.GetNegotiatedDetails());
+ ASSERT_EQ(2U, mSendOff.GetNegotiatedDetails()->GetEncodingCount());
+ ASSERT_EQ("foo", mSendOff.GetNegotiatedDetails()->GetEncoding(0).mRid);
+ ASSERT_EQ("bar", mSendOff.GetNegotiatedDetails()->GetEncoding(1).mRid);
+ ASSERT_NE(std::string::npos,
+ mOffer->ToString().find("a=simulcast:send foo;bar"));
+ ASSERT_NE(std::string::npos,
+ mAnswer->ToString().find("a=simulcast:recv foo;bar"));
+ ASSERT_NE(std::string::npos, mOffer->ToString().find("a=rid:foo send"));
+ ASSERT_NE(std::string::npos, mOffer->ToString().find("a=rid:bar send"));
+ ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=rid:foo recv"));
+ ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=rid:bar recv"));
+}
+
+TEST_F(JsepTrackTest, SimulcastOffererWithRtx) {
+ Init(SdpMediaSection::kVideo);
+ std::vector<std::string> rids;
+ rids.push_back("foo");
+ rids.push_back("bar");
+ rids.push_back("pop");
+ mSendOff.SetRids(rids);
+ mSendOff.AddToMsection(rids, sdp::kSend, mSsrcGenerator, true, &GetOffer());
+ mRecvOff.AddToMsection(rids, sdp::kSend, mSsrcGenerator, true, &GetOffer());
+ CreateAnswer();
+ // Add simulcast/rid to answer
+ mRecvAns.AddToMsection(rids, sdp::kRecv, mSsrcGenerator, false, &GetAnswer());
+ Negotiate();
+
+ ASSERT_EQ(3U, mSendOff.GetSsrcs().size());
+ const auto posSsrc0 =
+ mOffer->ToString().find(std::to_string(mSendOff.GetSsrcs()[0]));
+ const auto posSsrc1 =
+ mOffer->ToString().find(std::to_string(mSendOff.GetSsrcs()[1]));
+ const auto posSsrc2 =
+ mOffer->ToString().find(std::to_string(mSendOff.GetSsrcs()[2]));
+ ASSERT_NE(std::string::npos, posSsrc0);
+ ASSERT_NE(std::string::npos, posSsrc1);
+ ASSERT_NE(std::string::npos, posSsrc2);
+ ASSERT_GT(posSsrc1, posSsrc0);
+ ASSERT_GT(posSsrc2, posSsrc0);
+ ASSERT_GT(posSsrc2, posSsrc1);
+
+ ASSERT_EQ(3U, mSendOff.GetRtxSsrcs().size());
+ const auto posRtxSsrc0 =
+ mOffer->ToString().find(std::to_string(mSendOff.GetRtxSsrcs()[0]));
+ const auto posRtxSsrc1 =
+ mOffer->ToString().find(std::to_string(mSendOff.GetRtxSsrcs()[1]));
+ const auto posRtxSsrc2 =
+ mOffer->ToString().find(std::to_string(mSendOff.GetRtxSsrcs()[2]));
+ ASSERT_NE(std::string::npos, posRtxSsrc0);
+ ASSERT_NE(std::string::npos, posRtxSsrc1);
+ ASSERT_NE(std::string::npos, posRtxSsrc2);
+ ASSERT_GT(posRtxSsrc1, posRtxSsrc0);
+ ASSERT_GT(posRtxSsrc2, posRtxSsrc0);
+ ASSERT_GT(posRtxSsrc2, posRtxSsrc1);
+}
+
+TEST_F(JsepTrackTest, SimulcastAnswerer) {
+ Init(SdpMediaSection::kVideo);
+ std::vector<std::string> rids;
+ rids.push_back("foo");
+ rids.push_back("bar");
+ mSendAns.SetRids(rids);
+ CreateOffer();
+ // Add simulcast/rid to offer
+ mRecvOff.AddToMsection(rids, sdp::kRecv, mSsrcGenerator, false, &GetOffer());
+ CreateAnswer();
+ Negotiate();
+ ASSERT_TRUE(mSendAns.GetNegotiatedDetails());
+ ASSERT_EQ(2U, mSendAns.GetNegotiatedDetails()->GetEncodingCount());
+ ASSERT_EQ("foo", mSendAns.GetNegotiatedDetails()->GetEncoding(0).mRid);
+ ASSERT_EQ("bar", mSendAns.GetNegotiatedDetails()->GetEncoding(1).mRid);
+ ASSERT_NE(std::string::npos,
+ mOffer->ToString().find("a=simulcast:recv foo;bar"));
+ ASSERT_NE(std::string::npos,
+ mAnswer->ToString().find("a=simulcast:send foo;bar"));
+ ASSERT_NE(std::string::npos, mOffer->ToString().find("a=rid:foo recv"));
+ ASSERT_NE(std::string::npos, mOffer->ToString().find("a=rid:bar recv"));
+ ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=rid:foo send"));
+ ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=rid:bar send"));
+}
+
+#define VERIFY_OPUS_MAX_PLAYBACK_RATE(track, expectedRate) \
+ { \
+ JsepTrack& copy(track); \
+ ASSERT_TRUE(copy.GetNegotiatedDetails()); \
+ ASSERT_TRUE(copy.GetNegotiatedDetails()->GetEncodingCount()); \
+ for (const auto& codec : \
+ copy.GetNegotiatedDetails()->GetEncoding(0).GetCodecs()) { \
+ if (codec->mName == "opus") { \
+ JsepAudioCodecDescription& audioCodec = \
+ static_cast<JsepAudioCodecDescription&>(*codec); \
+ ASSERT_EQ((expectedRate), audioCodec.mMaxPlaybackRate); \
+ } \
+ }; \
+ }
+
+#define VERIFY_OPUS_FORCE_MONO(track, expected) \
+ { \
+ JsepTrack& copy(track); \
+ ASSERT_TRUE(copy.GetNegotiatedDetails()); \
+ ASSERT_TRUE(copy.GetNegotiatedDetails()->GetEncodingCount()); \
+ for (const auto& codec : \
+ copy.GetNegotiatedDetails()->GetEncoding(0).GetCodecs()) { \
+ if (codec->mName == "opus") { \
+ JsepAudioCodecDescription& audioCodec = \
+ static_cast<JsepAudioCodecDescription&>(*codec); \
+ /* gtest has some compiler warnings when using ASSERT_EQ with \
+ * booleans. */ \
+ ASSERT_EQ((int)(expected), (int)audioCodec.mForceMono); \
+ } \
+ }; \
+ }
+
+TEST_F(JsepTrackTest, DefaultOpusParameters) {
+ Init(SdpMediaSection::kAudio);
+ OfferAnswer();
+
+ VERIFY_OPUS_MAX_PLAYBACK_RATE(
+ mSendOff, SdpFmtpAttributeList::OpusParameters::kDefaultMaxPlaybackRate);
+ VERIFY_OPUS_MAX_PLAYBACK_RATE(
+ mSendAns, SdpFmtpAttributeList::OpusParameters::kDefaultMaxPlaybackRate);
+ VERIFY_OPUS_MAX_PLAYBACK_RATE(mRecvOff, 0U);
+ VERIFY_OPUS_FORCE_MONO(mRecvOff, false);
+ VERIFY_OPUS_MAX_PLAYBACK_RATE(mRecvAns, 0U);
+ VERIFY_OPUS_FORCE_MONO(mRecvAns, false);
+}
+
+TEST_F(JsepTrackTest, NonDefaultOpusParameters) {
+ InitCodecs();
+ for (auto& codec : mAnsCodecs) {
+ if (codec->mName == "opus") {
+ JsepAudioCodecDescription* audioCodec =
+ static_cast<JsepAudioCodecDescription*>(codec.get());
+ audioCodec->mMaxPlaybackRate = 16000;
+ audioCodec->mForceMono = true;
+ }
+ }
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+ OfferAnswer();
+
+ VERIFY_OPUS_MAX_PLAYBACK_RATE(mSendOff, 16000U);
+ VERIFY_OPUS_FORCE_MONO(mSendOff, true);
+ VERIFY_OPUS_MAX_PLAYBACK_RATE(
+ mSendAns, SdpFmtpAttributeList::OpusParameters::kDefaultMaxPlaybackRate);
+ VERIFY_OPUS_FORCE_MONO(mSendAns, false);
+ VERIFY_OPUS_MAX_PLAYBACK_RATE(mRecvOff, 0U);
+ VERIFY_OPUS_FORCE_MONO(mRecvOff, false);
+ VERIFY_OPUS_MAX_PLAYBACK_RATE(mRecvAns, 16000U);
+ VERIFY_OPUS_FORCE_MONO(mRecvAns, true);
+}
+
+TEST_F(JsepTrackTest, RtcpFbWithPayloadTypeAsymmetry) {
+ std::vector<std::string> expectedAckFbTypes;
+ std::vector<std::string> expectedNackFbTypes{"", "pli"};
+ std::vector<std::string> expectedCcmFbTypes{"fir"};
+ std::vector<SdpRtcpFbAttributeList::Feedback> expectedOtherFbTypes{
+ {"", SdpRtcpFbAttributeList::kRemb, "", ""},
+ {"", SdpRtcpFbAttributeList::kTransportCC, "", ""}};
+
+ InitCodecs();
+
+ // On offerer, configure to support remb and transport-cc on video codecs
+ for (auto& codec : mOffCodecs) {
+ if (codec->Type() == SdpMediaSection::kVideo) {
+ auto& videoCodec = static_cast<JsepVideoCodecDescription&>(*codec);
+ videoCodec.EnableRemb();
+ videoCodec.EnableTransportCC();
+ }
+ }
+
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+
+ CreateOffer();
+ // We do not bother trying to bamboozle the answerer into doing asymmetric
+ // payload types, we just use a raw SDP.
+ const std::string answer =
+ "v=0\r\n"
+ "o=- 0 0 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "t=0 0\r\n"
+ "a=msid-semantic:WMS *\r\n"
+ "m=video 0 UDP/TLS/RTP/SAVPF 136\r\n"
+ "c=IN IP4 0.0.0.0\r\n"
+ "a=sendrecv\r\n"
+ "a=fmtp:136 "
+ "profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode="
+ "1\r\n"
+ "a=msid:stream_id\r\n"
+ "a=rtcp-fb:136 nack\r\n"
+ "a=rtcp-fb:136 nack pli\r\n"
+ "a=rtcp-fb:136 ccm fir\r\n"
+ "a=rtcp-fb:136 goog-remb\r\n"
+ "a=rtcp-fb:136 transport-cc\r\n"
+ "a=rtpmap:136 H264/90000\r\n"
+ "a=ssrc:2025549043 cname:\r\n";
+
+ UniquePtr<SdpParser> parser(new SipccSdpParser);
+ mAnswer = std::move(parser->Parse(answer)->Sdp());
+ ASSERT_TRUE(mAnswer);
+
+ mRecvOff.RecvTrackSetRemote(*mAnswer, GetAnswer());
+ mRecvOff.Negotiate(GetAnswer(), GetAnswer(), GetOffer());
+ mSendOff.Negotiate(GetAnswer(), GetAnswer(), GetOffer());
+
+ ASSERT_TRUE(mSendOff.GetNegotiatedDetails());
+ ASSERT_TRUE(mRecvOff.GetNegotiatedDetails());
+
+ UniquePtr<JsepVideoCodecDescription> codec;
+ ASSERT_TRUE((codec = GetVideoCodec(mSendOff)));
+ ASSERT_EQ("136", codec->mDefaultPt)
+ << "Offerer should have seen answer asymmetry!";
+ ASSERT_TRUE((codec = GetVideoCodec(mRecvOff)));
+ ASSERT_EQ("126", codec->mDefaultPt);
+ ASSERT_EQ(expectedAckFbTypes, codec->mAckFbTypes);
+ ASSERT_EQ(expectedNackFbTypes, codec->mNackFbTypes);
+ ASSERT_EQ(expectedCcmFbTypes, codec->mCcmFbTypes);
+ ASSERT_EQ(expectedOtherFbTypes, codec->mOtherFbTypes);
+}
+
+TEST_F(JsepTrackTest, AudioSdpFmtpLine) {
+ mOffCodecs = MakeCodecs(true, true, true);
+ mAnsCodecs = MakeCodecs(true, true, true);
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+ OfferAnswer();
+
+ // SanityCheck checks that the sdpFmtpLine for a local codec matches that of
+ // the corresponding remote codec.
+ UniquePtr<JsepAudioCodecDescription> codec;
+ EXPECT_TRUE((codec = GetAudioCodec(mSendOff, 3, 0)));
+ EXPECT_EQ("opus", codec->mName);
+ EXPECT_EQ("maxplaybackrate=48000;stereo=1;useinbandfec=0",
+ codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetAudioCodec(mSendAns, 3, 0)));
+ EXPECT_EQ("opus", codec->mName);
+ EXPECT_EQ("maxplaybackrate=48000;stereo=1;useinbandfec=0",
+ codec->mSdpFmtpLine.valueOr("nothing"));
+
+ EXPECT_TRUE((codec = GetAudioCodec(mSendOff, 3, 1)));
+ EXPECT_EQ("G722", codec->mName);
+ EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetAudioCodec(mSendAns, 3, 1)));
+ EXPECT_EQ("G722", codec->mName);
+ EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
+
+ EXPECT_TRUE((codec = GetAudioCodec(mSendOff, 3, 2)));
+ EXPECT_EQ("telephone-event", codec->mName);
+ EXPECT_EQ("0-15", codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetAudioCodec(mSendAns, 3, 2)));
+ EXPECT_EQ("telephone-event", codec->mName);
+ EXPECT_EQ("0-15", codec->mSdpFmtpLine.valueOr("nothing"));
+}
+
+TEST_F(JsepTrackTest, NonDefaultAudioSdpFmtpLine) {
+ mOffCodecs = MakeCodecs(true, true, true);
+ mAnsCodecs = MakeCodecs(true, true, true);
+
+ for (auto& codec : mOffCodecs) {
+ if (codec->mName == "opus") {
+ auto* audio = static_cast<JsepAudioCodecDescription*>(codec.get());
+ audio->mForceMono = true;
+ audio->mMaxPlaybackRate = 32000;
+ }
+ }
+
+ for (auto& codec : mAnsCodecs) {
+ if (codec->mName == "opus") {
+ auto* audio = static_cast<JsepAudioCodecDescription*>(codec.get());
+ audio->mFECEnabled = true;
+ audio->mCbrEnabled = true;
+ audio->mDTXEnabled = true;
+ audio->mFrameSizeMs = 10;
+ audio->mMinFrameSizeMs = 5;
+ audio->mMaxFrameSizeMs = 20;
+ }
+ }
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+
+ {
+ // telephone-event doesn't store any params in JsepAudioCodecDescription.
+ // Set them directly in the offer sdp instead.
+ auto params = MakeUnique<SdpFmtpAttributeList::TelephoneEventParameters>();
+ params->dtmfTones = "2-9";
+ GetOffer().SetFmtp({"101", *params});
+ }
+
+ {
+ // telephone-event doesn't store any params in JsepAudioCodecDescription.
+ // Set them directly in the answer sdp instead.
+ auto params = MakeUnique<SdpFmtpAttributeList::TelephoneEventParameters>();
+ params->dtmfTones = "0-3,10";
+ GetAnswer().SetFmtp({"101", *params});
+ }
+
+ OfferAnswer();
+
+ // SanityCheck checks that the sdpFmtpLine for a local codec matches that of
+ // the corresponding remote codec.
+ UniquePtr<JsepAudioCodecDescription> codec;
+ EXPECT_TRUE((codec = GetAudioCodec(mSendOff, 3, 0)));
+ EXPECT_EQ("opus", codec->mName);
+ EXPECT_EQ(
+ "maxplaybackrate=48000;stereo=1;useinbandfec=1;usedtx=1;ptime=10;"
+ "minptime=5;maxptime=20;cbr=1",
+ codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetAudioCodec(mSendAns, 3, 0)));
+ EXPECT_EQ("opus", codec->mName);
+ EXPECT_EQ("maxplaybackrate=32000;stereo=0;useinbandfec=0",
+ codec->mSdpFmtpLine.valueOr("nothing"));
+
+ EXPECT_TRUE((codec = GetAudioCodec(mSendOff, 3, 1)));
+ EXPECT_EQ("G722", codec->mName);
+ EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetAudioCodec(mSendAns, 3, 1)));
+ EXPECT_EQ("G722", codec->mName);
+ EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
+
+ EXPECT_TRUE((codec = GetAudioCodec(mSendOff, 3, 2)));
+ EXPECT_EQ("telephone-event", codec->mName);
+ EXPECT_EQ("0-3,10", codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetAudioCodec(mSendAns, 3, 2)));
+ EXPECT_EQ("telephone-event", codec->mName);
+ EXPECT_EQ("2-9", codec->mSdpFmtpLine.valueOr("nothing"));
+}
+
+TEST_F(JsepTrackTest, VideoSdpFmtpLine) {
+ mOffCodecs = MakeCodecs(true, true, true);
+ mAnsCodecs = MakeCodecs(true, true, true);
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ // SanityCheck checks that the sdpFmtpLine for a local codec matches that of
+ // the corresponding remote codec.
+ UniquePtr<JsepVideoCodecDescription> codec;
+ EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 4, 0)));
+ EXPECT_EQ("VP8", codec->mName);
+ EXPECT_EQ("max-fs=12288;max-fr=60", codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 4, 0)));
+ EXPECT_EQ("VP8", codec->mName);
+ EXPECT_EQ("max-fs=12288;max-fr=60", codec->mSdpFmtpLine.valueOr("nothing"));
+
+ EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 4, 1)));
+ EXPECT_EQ("H264", codec->mName);
+ EXPECT_EQ(
+ "profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1",
+ codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 4, 1)));
+ EXPECT_EQ("H264", codec->mName);
+ EXPECT_EQ(
+ "profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1",
+ codec->mSdpFmtpLine.valueOr("nothing"));
+
+ EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 4, 2)));
+ EXPECT_EQ("red", codec->mName);
+ EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 4, 2)));
+ EXPECT_EQ("red", codec->mName);
+ EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
+
+ EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 4, 3)));
+ EXPECT_EQ("ulpfec", codec->mName);
+ EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 4, 3)));
+ EXPECT_EQ("ulpfec", codec->mName);
+ EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
+}
+
+TEST_F(JsepTrackTest, NonDefaultVideoSdpFmtpLine) {
+ mOffCodecs = MakeCodecs(true, true, true);
+ mAnsCodecs = MakeCodecs(true, true, true);
+
+ for (auto& codec : mOffCodecs) {
+ if (codec->mName == "VP8" || codec->mName == "H264") {
+ auto* video = static_cast<JsepVideoCodecDescription*>(codec.get());
+ video->mConstraints.maxFs = 1200;
+ if (codec->mName == "VP8") {
+ video->mConstraints.maxFps = Some(15);
+ } else {
+ video->mConstraints.maxDpb = 6400;
+ video->mConstraints.maxBr = 1000;
+ JsepVideoCodecDescription::SetSaneH264Level(0x1F0,
+ &video->mProfileLevelId);
+ }
+ }
+ }
+
+ for (auto& codec : mAnsCodecs) {
+ if (codec->mName == "VP8" || codec->mName == "H264") {
+ auto* video = static_cast<JsepVideoCodecDescription*>(codec.get());
+ video->mConstraints.maxFs = 32400;
+ if (codec->mName == "VP8") {
+ video->mConstraints.maxFps = Some(60);
+ } else {
+ video->mConstraints.maxMbps = 1944000;
+ video->mConstraints.maxCpb = 800000;
+ video->mConstraints.maxDpb = 128000;
+ JsepVideoCodecDescription::SetSaneH264Level(0xAB,
+ &video->mProfileLevelId);
+ video->mPacketizationMode = 1;
+ }
+ }
+ }
+
+ InitTracks(SdpMediaSection::kVideo);
+ InitSdp(SdpMediaSection::kVideo);
+ OfferAnswer();
+
+ // SanityCheck checks that the sdpFmtpLine for a local codec matches that of
+ // the corresponding remote codec.
+ UniquePtr<JsepVideoCodecDescription> codec;
+ EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 4, 0)));
+ EXPECT_EQ("VP8", codec->mName);
+ EXPECT_EQ("max-fs=32400;max-fr=60", codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 4, 0)));
+ EXPECT_EQ("VP8", codec->mName);
+ EXPECT_EQ("max-fs=1200;max-fr=15", codec->mSdpFmtpLine.valueOr("nothing"));
+
+ EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 4, 1)));
+ EXPECT_EQ("H264", codec->mName);
+ EXPECT_EQ(
+ "profile-level-id=42f00b;level-asymmetry-allowed=1;packetization-mode=1;"
+ "max-mbps=1944000;max-fs=32400;max-cpb=800000;max-dpb=128000",
+ codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 4, 1)));
+ EXPECT_EQ("H264", codec->mName);
+ EXPECT_EQ(
+ "profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1;"
+ "max-fs=1200;max-dpb=6400;max-br=1000",
+ codec->mSdpFmtpLine.valueOr("nothing"));
+
+ EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 4, 2)));
+ EXPECT_EQ("red", codec->mName);
+ EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 4, 2)));
+ EXPECT_EQ("red", codec->mName);
+ EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
+
+ EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 4, 3)));
+ EXPECT_EQ("ulpfec", codec->mName);
+ EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
+ EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 4, 3)));
+ EXPECT_EQ("ulpfec", codec->mName);
+ EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
+}
+
+} // namespace mozilla