Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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(bool offerCodecsMatchAnswer = true) {
CreateOffer();
CreateAnswer();
Negotiate();
SanityCheck(offerCodecsMatchAnswer);
}
// 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,
bool codecsMustMatch) const {
ASSERT_EQ(a.GetEncodingCount(), b.GetEncodingCount());
if (codecsMustMatch) {
for (size_t i = 0; i < a.GetEncodingCount(); ++i) {
SanityCheckEncodings(a.GetEncoding(i), b.GetEncoding(i));
}
}
ASSERT_EQ(a.GetUniqueReceivePayloadTypes().size(),
b.GetUniqueReceivePayloadTypes().size());
for (size_t i = 0; i < a.GetUniqueReceivePayloadTypes().size(); ++i) {
ASSERT_EQ(a.GetUniqueReceivePayloadTypes()[i],
b.GetUniqueReceivePayloadTypes()[i]);
}
}
void SanityCheckTracks(const JsepTrack& a, const JsepTrack& b,
bool codecsMustMatch) 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(), codecsMustMatch);
}
void SanityCheck(bool offerCodecsMatchAnswer = true) const {
SanityCheckTracks(mSendOff, mRecvAns, true);
SanityCheckTracks(mRecvOff, mSendAns, offerCodecsMatchAnswer);
}
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(false);
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, 3, 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, 3, 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(false);
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, 4, 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, 4, 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(false);
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, 4)));
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);