summaryrefslogtreecommitdiffstats
path: root/media/webrtc/signaling/test/jsep_track_unittest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/webrtc/signaling/test/jsep_track_unittest.cpp')
-rw-r--r--media/webrtc/signaling/test/jsep_track_unittest.cpp1269
1 files changed, 1269 insertions, 0 deletions
diff --git a/media/webrtc/signaling/test/jsep_track_unittest.cpp b/media/webrtc/signaling/test/jsep_track_unittest.cpp
new file mode 100644
index 000000000..a09d47276
--- /dev/null
+++ b/media/webrtc/signaling/test/jsep_track_unittest.cpp
@@ -0,0 +1,1269 @@
+/* -*- 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/. */
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+// Magic linker includes :(
+#include "FakeMediaStreams.h"
+#include "FakeMediaStreamsImpl.h"
+#include "FakeLogging.h"
+
+#include "signaling/src/jsep/JsepTrack.h"
+#include "signaling/src/sdp/SipccSdp.h"
+#include "signaling/src/sdp/SdpHelper.h"
+
+#include "mtransport_test_utils.h"
+
+#include "FakeIPC.h"
+#include "FakeIPC.cpp"
+
+#include "TestHarness.h"
+
+namespace mozilla {
+
+class JsepTrackTest : public ::testing::Test
+{
+ public:
+ JsepTrackTest() {}
+
+ std::vector<JsepCodecDescription*>
+ MakeCodecs(bool addFecCodecs = false,
+ bool preferRed = false,
+ bool addDtmfCodec = false) const
+ {
+ std::vector<JsepCodecDescription*> results;
+ results.push_back(
+ new JsepAudioCodecDescription("1", "opus", 48000, 2, 960, 40000));
+ results.push_back(
+ new JsepAudioCodecDescription("9", "G722", 8000, 1, 320, 64000));
+ if (addDtmfCodec) {
+ results.push_back(
+ new JsepAudioCodecDescription("101", "telephone-event",
+ 8000, 1, 0, 0));
+ }
+
+ JsepVideoCodecDescription* red = nullptr;
+ if (addFecCodecs && preferRed) {
+ red = new JsepVideoCodecDescription(
+ "122",
+ "red",
+ 90000
+ );
+ results.push_back(red);
+ }
+
+ JsepVideoCodecDescription* vp8 =
+ new JsepVideoCodecDescription("120", "VP8", 90000);
+ vp8->mConstraints.maxFs = 12288;
+ vp8->mConstraints.maxFps = 60;
+ results.push_back(vp8);
+
+ JsepVideoCodecDescription* h264 =
+ new JsepVideoCodecDescription("126", "H264", 90000);
+ h264->mPacketizationMode = 1;
+ h264->mProfileLevelId = 0x42E00D;
+ results.push_back(h264);
+
+ if (addFecCodecs) {
+ if (!preferRed) {
+ red = new JsepVideoCodecDescription(
+ "122",
+ "red",
+ 90000
+ );
+ results.push_back(red);
+ }
+ JsepVideoCodecDescription* ulpfec = new JsepVideoCodecDescription(
+ "123",
+ "ulpfec",
+ 90000
+ );
+ results.push_back(ulpfec);
+ }
+
+ results.push_back(
+ new JsepApplicationCodecDescription(
+ "5000",
+ "webrtc-datachannel",
+ 16
+ ));
+
+ // if we're doing something with red, it needs
+ // to update the redundant encodings list
+ if (red) {
+ red->UpdateRedundantEncodings(results);
+ }
+
+ return results;
+ }
+
+ void Init(SdpMediaSection::MediaType type) {
+ InitCodecs();
+ InitTracks(type);
+ InitSdp(type);
+ }
+
+ void InitCodecs() {
+ mOffCodecs.values = MakeCodecs();
+ mAnsCodecs.values = MakeCodecs();
+ }
+
+ void InitTracks(SdpMediaSection::MediaType type)
+ {
+ mSendOff = new JsepTrack(type, "stream_id", "track_id", sdp::kSend);
+ mRecvOff = new JsepTrack(type, "stream_id", "track_id", sdp::kRecv);
+ mSendOff->PopulateCodecs(mOffCodecs.values);
+ mRecvOff->PopulateCodecs(mOffCodecs.values);
+
+ mSendAns = new JsepTrack(type, "stream_id", "track_id", sdp::kSend);
+ mRecvAns = new JsepTrack(type, "stream_id", "track_id", sdp::kRecv);
+ mSendAns->PopulateCodecs(mAnsCodecs.values);
+ mRecvAns->PopulateCodecs(mAnsCodecs.values);
+ }
+
+ void InitSdp(SdpMediaSection::MediaType type)
+ {
+ mOffer.reset(new SipccSdp(SdpOrigin("", 0, 0, sdp::kIPv4, "")));
+ mOffer->AddMediaSection(
+ type,
+ SdpDirectionAttribute::kInactive,
+ 0,
+ SdpHelper::GetProtocolForMediaType(type),
+ sdp::kIPv4,
+ "0.0.0.0");
+ mAnswer.reset(new SipccSdp(SdpOrigin("", 0, 0, sdp::kIPv4, "")));
+ mAnswer->AddMediaSection(
+ type,
+ SdpDirectionAttribute::kInactive,
+ 0,
+ SdpHelper::GetProtocolForMediaType(type),
+ sdp::kIPv4,
+ "0.0.0.0");
+ }
+
+ SdpMediaSection& GetOffer()
+ {
+ return mOffer->GetMediaSection(0);
+ }
+
+ SdpMediaSection& GetAnswer()
+ {
+ return mAnswer->GetMediaSection(0);
+ }
+
+ void CreateOffer()
+ {
+ if (mSendOff) {
+ mSendOff->AddToOffer(&GetOffer());
+ }
+
+ if (mRecvOff) {
+ mRecvOff->AddToOffer(&GetOffer());
+ }
+ }
+
+ void CreateAnswer()
+ {
+ if (mSendAns && GetOffer().IsReceiving()) {
+ mSendAns->AddToAnswer(GetOffer(), &GetAnswer());
+ }
+
+ if (mRecvAns && GetOffer().IsSending()) {
+ mRecvAns->AddToAnswer(GetOffer(), &GetAnswer());
+ }
+ }
+
+ void Negotiate()
+ {
+ std::cerr << "Offer SDP: " << std::endl;
+ mOffer->Serialize(std::cerr);
+
+ std::cerr << "Answer SDP: " << std::endl;
+ mAnswer->Serialize(std::cerr);
+
+ if (mSendAns && GetAnswer().IsSending()) {
+ mSendAns->Negotiate(GetAnswer(), GetOffer());
+ }
+
+ if (mRecvAns && GetAnswer().IsReceiving()) {
+ mRecvAns->Negotiate(GetAnswer(), GetOffer());
+ }
+
+ if (mSendOff && GetAnswer().IsReceiving()) {
+ mSendOff->Negotiate(GetAnswer(), GetAnswer());
+ }
+
+ if (mRecvOff && GetAnswer().IsSending()) {
+ mRecvOff->Negotiate(GetAnswer(), GetAnswer());
+ }
+ }
+
+ void OfferAnswer()
+ {
+ CreateOffer();
+ CreateAnswer();
+ Negotiate();
+ SanityCheck();
+ }
+
+ static size_t EncodingCount(const RefPtr<JsepTrack>& track)
+ {
+ return track->GetNegotiatedDetails()->GetEncodingCount();
+ }
+
+ // 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 RefPtr<JsepTrack>& send,
+ const RefPtr<JsepTrack>& recv)
+ {
+ if (expected) {
+ ASSERT_TRUE(!!send);
+ ASSERT_TRUE(send->GetNegotiatedDetails());
+ ASSERT_TRUE(!!recv);
+ ASSERT_TRUE(recv->GetNegotiatedDetails());
+ }
+
+ if (send && send->GetNegotiatedDetails()) {
+ ASSERT_EQ(expected, send->GetNegotiatedDetails()->GetEncodingCount());
+ }
+
+ if (recv && 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);
+ }
+
+ const 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 std::vector<JsepCodecDescription*>& 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]->mType != type) {
+ return nullptr;
+ }
+ return codecs[codecIndex];
+ }
+
+ const JsepVideoCodecDescription*
+ GetVideoCodec(const JsepTrack& track,
+ size_t expectedSize = 1,
+ size_t codecIndex = 0) const
+ {
+ return static_cast<const JsepVideoCodecDescription*>
+ (GetCodec(track, SdpMediaSection::kVideo, expectedSize, codecIndex));
+ }
+
+ const JsepAudioCodecDescription*
+ GetAudioCodec(const JsepTrack& track,
+ size_t expectedSize = 1,
+ size_t codecIndex = 0) const
+ {
+ return static_cast<const JsepAudioCodecDescription*>
+ (GetCodec(track, SdpMediaSection::kAudio, expectedSize, codecIndex));
+ }
+
+ void CheckOtherFbsSize(const JsepTrack& track, size_t expected) const
+ {
+ const JsepVideoCodecDescription* videoCodec = GetVideoCodec(track);
+ ASSERT_NE(videoCodec, nullptr);
+ ASSERT_EQ(videoCodec->mOtherFbTypes.size(), expected);
+ }
+
+ void CheckOtherFbExists(const JsepTrack& track,
+ SdpRtcpFbAttributeList::Type type) const
+ {
+ const JsepVideoCodecDescription* videoCodec = GetVideoCodec(track);
+ ASSERT_NE(videoCodec, nullptr);
+ 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
+ {
+ ASSERT_EQ(a.mType, b.mType);
+ ASSERT_EQ(a.mDefaultPt, b.mDefaultPt);
+ ASSERT_EQ(a.mName, b.mName);
+ ASSERT_EQ(a.mClock, b.mClock);
+ ASSERT_EQ(a.mChannels, b.mChannels);
+ ASSERT_NE(a.mDirection, b.mDirection);
+ // These constraints are for fmtp and rid, which _are_ signaled
+ ASSERT_EQ(a.mConstraints, b.mConstraints);
+
+ if (a.mType == 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.GetStreamId(), b.GetStreamId());
+ ASSERT_EQ(a.GetTrackId(), b.GetTrackId());
+ 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
+ {
+ if (mSendOff && mRecvAns) {
+ SanityCheckTracks(*mSendOff, *mRecvAns);
+ }
+ if (mRecvOff && mSendAns) {
+ SanityCheckTracks(*mRecvOff, *mSendAns);
+ }
+ }
+
+ protected:
+ RefPtr<JsepTrack> mSendOff;
+ RefPtr<JsepTrack> mRecvOff;
+ RefPtr<JsepTrack> mSendAns;
+ RefPtr<JsepTrack> mRecvAns;
+ PtrVector<JsepCodecDescription> mOffCodecs;
+ PtrVector<JsepCodecDescription> mAnsCodecs;
+ UniquePtr<Sdp> mOffer;
+ UniquePtr<Sdp> mAnswer;
+};
+
+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()(JsepCodecDescription* codec) {
+ if (codec->mType == mType) {
+ *mResult = true;
+ }
+ }
+
+private:
+ bool *mResult;
+ SdpMediaSection::MediaType mType;
+};
+
+TEST_F(JsepTrackTest, CheckForMismatchedAudioCodecAndVideoTrack)
+{
+ PtrVector<JsepCodecDescription> offerCodecs;
+
+ // make codecs including telephone-event (an audio codec)
+ offerCodecs.values = MakeCodecs(false, false, true);
+ RefPtr<JsepTrack> videoTrack = new JsepTrack(SdpMediaSection::kVideo,
+ "stream_id",
+ "track_id",
+ sdp::kSend);
+ // populate codecs and then make sure we don't have any audio codecs
+ // in the video track
+ videoTrack->PopulateCodecs(offerCodecs.values);
+
+ 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);
+
+ ASSERT_TRUE(mSendOff.get());
+ ASSERT_TRUE(mRecvOff.get());
+ ASSERT_TRUE(mSendAns.get());
+ ASSERT_TRUE(mRecvAns.get());
+
+ // 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.values = MakeCodecs(false, false, true);
+ mAnsCodecs.values = 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);
+
+ const JsepAudioCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns)));
+ ASSERT_EQ("1", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationAnswererDtmf)
+{
+ mOffCodecs.values = MakeCodecs(false, false, false);
+ mAnsCodecs.values = 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);
+
+ const JsepAudioCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns)));
+ ASSERT_EQ("1", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationOffererAnswererDtmf)
+{
+ mOffCodecs.values = MakeCodecs(false, false, true);
+ mAnsCodecs.values = 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);
+
+ const JsepAudioCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationDtmfOffererNoFmtpAnswererFmtp)
+{
+ mOffCodecs.values = MakeCodecs(false, false, true);
+ mAnsCodecs.values = MakeCodecs(false, false, 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);
+
+ const JsepAudioCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationDtmfOffererFmtpAnswererNoFmtp)
+{
+ mOffCodecs.values = MakeCodecs(false, false, true);
+ mAnsCodecs.values = MakeCodecs(false, false, 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);
+
+ const JsepAudioCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationDtmfOffererNoFmtpAnswererNoFmtp)
+{
+ mOffCodecs.values = MakeCodecs(false, false, true);
+ mAnsCodecs.values = MakeCodecs(false, false, 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);
+
+ const JsepAudioCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, VideoNegotationOffererFEC)
+{
+ mOffCodecs.values = MakeCodecs(true);
+ mAnsCodecs.values = 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);
+
+ ASSERT_NE(mOffer->ToString().find("a=fmtp:122 120/126/123"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=fmtp:122"), std::string::npos);
+
+ const JsepVideoCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetVideoCodec(*mSendOff)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(*mRecvOff)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(*mSendAns)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(*mRecvAns)));
+ ASSERT_EQ("120", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, VideoNegotationAnswererFEC)
+{
+ mOffCodecs.values = MakeCodecs(false);
+ mAnsCodecs.values = 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);
+
+ ASSERT_EQ(mOffer->ToString().find("a=fmtp:122"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=fmtp:122"), std::string::npos);
+
+ const JsepVideoCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetVideoCodec(*mSendOff)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(*mRecvOff)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(*mSendAns)));
+ ASSERT_EQ("120", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(*mRecvAns)));
+ ASSERT_EQ("120", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFEC)
+{
+ mOffCodecs.values = MakeCodecs(true);
+ mAnsCodecs.values = 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);
+
+ ASSERT_NE(mOffer->ToString().find("a=fmtp:122 120/126/123"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=fmtp:122 120/126/123"), std::string::npos);
+
+ const JsepVideoCodecDescription* track = nullptr;
+ 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.values = MakeCodecs(true, true);
+ mAnsCodecs.values = 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);
+
+ ASSERT_NE(mOffer->ToString().find("a=fmtp:122 120/126/123"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=fmtp:122 120/126/123"), std::string::npos);
+
+ const JsepVideoCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetVideoCodec(*mSendOff, 4)));
+ ASSERT_EQ("122", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(*mRecvOff, 4)));
+ ASSERT_EQ("122", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(*mSendAns, 4)));
+ ASSERT_EQ("122", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(*mRecvAns, 4)));
+ ASSERT_EQ("122", track->mDefaultPt);
+}
+
+// Make sure we only put the right things in the fmtp:122 120/.... line
+TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFECMismatch)
+{
+ mOffCodecs.values = MakeCodecs(true, true);
+ mAnsCodecs.values = MakeCodecs(true);
+ // remove h264 from answer codecs
+ ASSERT_EQ("H264", mAnsCodecs.values[3]->mName);
+ mAnsCodecs.values.erase(mAnsCodecs.values.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);
+
+ ASSERT_NE(mOffer->ToString().find("a=fmtp:122 120/126/123"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=fmtp:122 120/123"), std::string::npos);
+
+ const JsepVideoCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetVideoCodec(*mSendOff, 3)));
+ ASSERT_EQ("122", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(*mRecvOff, 3)));
+ ASSERT_EQ("122", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(*mSendAns, 3)));
+ ASSERT_EQ("122", track->mDefaultPt);
+ ASSERT_TRUE((track = GetVideoCodec(*mRecvAns, 3)));
+ ASSERT_EQ("122", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFECZeroVP9Codec)
+{
+ mOffCodecs.values = MakeCodecs(true);
+ JsepVideoCodecDescription* vp9 =
+ new JsepVideoCodecDescription("0", "VP9", 90000);
+ vp9->mConstraints.maxFs = 12288;
+ vp9->mConstraints.maxFps = 60;
+ mOffCodecs.values.push_back(vp9);
+
+ ASSERT_EQ(8U, mOffCodecs.values.size());
+ JsepVideoCodecDescription* red =
+ static_cast<JsepVideoCodecDescription*>(mOffCodecs.values[4]);
+ ASSERT_EQ("red", red->mName);
+ // rebuild the redundant encodings with our newly added "wacky" VP9
+ red->mRedundantEncodings.clear();
+ red->UpdateRedundantEncodings(mOffCodecs.values);
+
+ mAnsCodecs.values = 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);
+
+ ASSERT_NE(mOffer->ToString().find("a=fmtp:122 120/126/123/0"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=fmtp:122 120/126/123\r\n"), std::string::npos);
+}
+
+TEST_F(JsepTrackTest, VideoNegotiationOfferRemb)
+{
+ InitCodecs();
+ // enable remb on the offer codecs
+ ((JsepVideoCodecDescription*)mOffCodecs.values[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);
+
+ CheckOtherFbsSize(*mSendOff, 0);
+ CheckOtherFbsSize(*mRecvAns, 0);
+
+ CheckOtherFbsSize(*mSendAns, 0);
+ CheckOtherFbsSize(*mRecvOff, 0);
+}
+
+TEST_F(JsepTrackTest, VideoNegotiationAnswerRemb)
+{
+ InitCodecs();
+ // enable remb on the answer codecs
+ ((JsepVideoCodecDescription*)mAnsCodecs.values[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);
+
+ CheckOtherFbsSize(*mSendOff, 0);
+ CheckOtherFbsSize(*mRecvAns, 0);
+
+ CheckOtherFbsSize(*mSendAns, 0);
+ CheckOtherFbsSize(*mRecvOff, 0);
+}
+
+TEST_F(JsepTrackTest, VideoNegotiationOfferAnswerRemb)
+{
+ InitCodecs();
+ // enable remb on the offer and answer codecs
+ ((JsepVideoCodecDescription*)mOffCodecs.values[2])->EnableRemb();
+ ((JsepVideoCodecDescription*)mAnsCodecs.values[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);
+
+ CheckOtherFbsSize(*mSendOff, 1);
+ CheckOtherFbsSize(*mRecvAns, 1);
+ CheckOtherFbExists(*mSendOff, SdpRtcpFbAttributeList::kRemb);
+ CheckOtherFbExists(*mRecvAns, SdpRtcpFbAttributeList::kRemb);
+
+ CheckOtherFbsSize(*mSendAns, 1);
+ CheckOtherFbsSize(*mRecvOff, 1);
+ CheckOtherFbExists(*mSendAns, SdpRtcpFbAttributeList::kRemb);
+ CheckOtherFbExists(*mRecvOff, SdpRtcpFbAttributeList::kRemb);
+}
+
+TEST_F(JsepTrackTest, AudioOffSendonlyAnsRecvonly)
+{
+ Init(SdpMediaSection::kAudio);
+ mRecvOff = nullptr;
+ mSendAns = nullptr;
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(0);
+}
+
+TEST_F(JsepTrackTest, VideoOffSendonlyAnsRecvonly)
+{
+ Init(SdpMediaSection::kVideo);
+ mRecvOff = nullptr;
+ mSendAns = nullptr;
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(0);
+}
+
+TEST_F(JsepTrackTest, AudioOffSendrecvAnsRecvonly)
+{
+ Init(SdpMediaSection::kAudio);
+ mSendAns = nullptr;
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(0);
+}
+
+TEST_F(JsepTrackTest, VideoOffSendrecvAnsRecvonly)
+{
+ Init(SdpMediaSection::kVideo);
+ mSendAns = nullptr;
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(0);
+}
+
+TEST_F(JsepTrackTest, AudioOffRecvonlyAnsSendrecv)
+{
+ Init(SdpMediaSection::kAudio);
+ mSendOff = nullptr;
+ OfferAnswer();
+ CheckOffEncodingCount(0);
+ CheckAnsEncodingCount(1);
+}
+
+TEST_F(JsepTrackTest, VideoOffRecvonlyAnsSendrecv)
+{
+ Init(SdpMediaSection::kVideo);
+ mSendOff = nullptr;
+ OfferAnswer();
+ CheckOffEncodingCount(0);
+ CheckAnsEncodingCount(1);
+}
+
+TEST_F(JsepTrackTest, AudioOffSendrecvAnsSendonly)
+{
+ Init(SdpMediaSection::kAudio);
+ mRecvAns = nullptr;
+ OfferAnswer();
+ CheckOffEncodingCount(0);
+ CheckAnsEncodingCount(1);
+}
+
+TEST_F(JsepTrackTest, VideoOffSendrecvAnsSendonly)
+{
+ Init(SdpMediaSection::kVideo);
+ mRecvAns = nullptr;
+ OfferAnswer();
+ CheckOffEncodingCount(0);
+ CheckAnsEncodingCount(1);
+}
+
+static JsepTrack::JsConstraints
+MakeConstraints(const std::string& rid, uint32_t maxBitrate)
+{
+ JsepTrack::JsConstraints constraints;
+ constraints.rid = rid;
+ constraints.constraints.maxBr = maxBitrate;
+ return constraints;
+}
+
+TEST_F(JsepTrackTest, SimulcastRejected)
+{
+ Init(SdpMediaSection::kVideo);
+ std::vector<JsepTrack::JsConstraints> constraints;
+ constraints.push_back(MakeConstraints("foo", 40000));
+ constraints.push_back(MakeConstraints("bar", 10000));
+ mSendOff->SetJsConstraints(constraints);
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+}
+
+TEST_F(JsepTrackTest, SimulcastPrevented)
+{
+ Init(SdpMediaSection::kVideo);
+ std::vector<JsepTrack::JsConstraints> constraints;
+ constraints.push_back(MakeConstraints("foo", 40000));
+ constraints.push_back(MakeConstraints("bar", 10000));
+ mSendAns->SetJsConstraints(constraints);
+ OfferAnswer();
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+}
+
+TEST_F(JsepTrackTest, SimulcastOfferer)
+{
+ Init(SdpMediaSection::kVideo);
+ std::vector<JsepTrack::JsConstraints> constraints;
+ constraints.push_back(MakeConstraints("foo", 40000));
+ constraints.push_back(MakeConstraints("bar", 10000));
+ mSendOff->SetJsConstraints(constraints);
+ CreateOffer();
+ CreateAnswer();
+ // Add simulcast/rid to answer
+ JsepTrack::AddToMsection(constraints, sdp::kRecv, &GetAnswer());
+ Negotiate();
+ ASSERT_TRUE(mSendOff->GetNegotiatedDetails());
+ ASSERT_EQ(2U, mSendOff->GetNegotiatedDetails()->GetEncodingCount());
+ ASSERT_EQ("foo", mSendOff->GetNegotiatedDetails()->GetEncoding(0).mRid);
+ ASSERT_EQ(40000U,
+ mSendOff->GetNegotiatedDetails()->GetEncoding(0).mConstraints.maxBr);
+ ASSERT_EQ("bar", mSendOff->GetNegotiatedDetails()->GetEncoding(1).mRid);
+ ASSERT_EQ(10000U,
+ mSendOff->GetNegotiatedDetails()->GetEncoding(1).mConstraints.maxBr);
+}
+
+TEST_F(JsepTrackTest, SimulcastAnswerer)
+{
+ Init(SdpMediaSection::kVideo);
+ std::vector<JsepTrack::JsConstraints> constraints;
+ constraints.push_back(MakeConstraints("foo", 40000));
+ constraints.push_back(MakeConstraints("bar", 10000));
+ mSendAns->SetJsConstraints(constraints);
+ CreateOffer();
+ // Add simulcast/rid to offer
+ JsepTrack::AddToMsection(constraints, sdp::kRecv, &GetOffer());
+ CreateAnswer();
+ Negotiate();
+ ASSERT_TRUE(mSendAns->GetNegotiatedDetails());
+ ASSERT_EQ(2U, mSendAns->GetNegotiatedDetails()->GetEncodingCount());
+ ASSERT_EQ("foo", mSendAns->GetNegotiatedDetails()->GetEncoding(0).mRid);
+ ASSERT_EQ(40000U,
+ mSendAns->GetNegotiatedDetails()->GetEncoding(0).mConstraints.maxBr);
+ ASSERT_EQ("bar", mSendAns->GetNegotiatedDetails()->GetEncoding(1).mRid);
+ ASSERT_EQ(10000U,
+ mSendAns->GetNegotiatedDetails()->GetEncoding(1).mConstraints.maxBr);
+}
+
+#define VERIFY_OPUS_MAX_PLAYBACK_RATE(track, expectedRate) \
+{ \
+ JsepTrack& copy(track); \
+ ASSERT_TRUE(copy.GetNegotiatedDetails()); \
+ ASSERT_TRUE(copy.GetNegotiatedDetails()->GetEncodingCount()); \
+ for (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 (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.values) {
+ if (codec->mName == "opus") {
+ JsepAudioCodecDescription* audioCodec =
+ static_cast<JsepAudioCodecDescription*>(codec);
+ 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);
+}
+
+} // namespace mozilla
+
+int
+main(int argc, char** argv)
+{
+ // Prevents some log spew
+ ScopedXPCOM xpcom("jsep_track_unittest");
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+