diff options
Diffstat (limited to 'media/webrtc/signaling/test/jsep_track_unittest.cpp')
-rw-r--r-- | media/webrtc/signaling/test/jsep_track_unittest.cpp | 1269 |
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(); +} + |