Bug 1112692: BundlePolicy support, and support for more than one BUNDLE group. r=mt, r=smaug

This commit is contained in:
Byron Campen [:bwc] 2015-06-12 14:27:08 -07:00
parent 7248c7e4fe
commit 324564a2e9
8 changed files with 380 additions and 195 deletions

View File

@ -14,7 +14,14 @@ dictionary RTCIceServer {
DOMString? username = null;
};
enum RTCBundlePolicy {
"balanced",
"max-compat",
"max-bundle"
};
dictionary RTCConfiguration {
sequence<RTCIceServer> iceServers;
RTCBundlePolicy bundlePolicy = "balanced";
DOMString? peerIdentity = null;
};

View File

@ -47,6 +47,12 @@ struct JsepOfferOptions : public JsepOAOptions {
};
struct JsepAnswerOptions : public JsepOAOptions {};
enum JsepBundlePolicy {
kBundleBalanced,
kBundleMaxCompat,
kBundleMaxBundle
};
class JsepSession
{
public:
@ -73,6 +79,7 @@ public:
// Set up the ICE And DTLS data.
virtual nsresult SetIceCredentials(const std::string& ufrag,
const std::string& pwd) = 0;
virtual nsresult SetBundlePolicy(JsepBundlePolicy policy) = 0;
virtual bool RemoteIsIceLite() const = 0;
virtual std::vector<std::string> GetIceOptions() const = 0;

View File

@ -156,6 +156,20 @@ JsepSessionImpl::SetIceCredentials(const std::string& ufrag,
return NS_OK;
}
nsresult
JsepSessionImpl::SetBundlePolicy(JsepBundlePolicy policy)
{
mLastError.clear();
if (mCurrentLocalDescription) {
JSEP_SET_ERROR("Changing the bundle policy is only supported before the "
"first SetLocalDescription.");
return NS_ERROR_UNEXPECTED;
}
mBundlePolicy = policy;
return NS_OK;
}
nsresult
JsepSessionImpl::AddDtlsFingerprint(const std::string& algorithm,
const std::vector<uint8_t>& value)
@ -514,6 +528,7 @@ void
JsepSessionImpl::SetupBundle(Sdp* sdp) const
{
std::vector<std::string> mids;
std::set<SdpMediaSection::MediaType> observedTypes;
// This has the effect of changing the bundle level if the first m-section
// goes from disabled to enabled. This is kinda inefficient.
@ -521,11 +536,36 @@ JsepSessionImpl::SetupBundle(Sdp* sdp) const
for (size_t i = 0; i < sdp->GetMediaSectionCount(); ++i) {
auto& attrs = sdp->GetMediaSection(i).GetAttributeList();
if (attrs.HasAttribute(SdpAttribute::kMidAttribute)) {
bool useBundleOnly = false;
switch (mBundlePolicy) {
case kBundleMaxCompat:
// We don't use bundle-only for max-compat
break;
case kBundleBalanced:
// balanced means we use bundle-only on everything but the first
// m-section of a given type
if (observedTypes.count(sdp->GetMediaSection(i).GetMediaType())) {
useBundleOnly = true;
}
observedTypes.insert(sdp->GetMediaSection(i).GetMediaType());
break;
case kBundleMaxBundle:
// max-bundle means we use bundle-only on everything but the first
// m-section
useBundleOnly = !mids.empty();
break;
}
if (useBundleOnly) {
attrs.SetAttribute(
new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute));
}
mids.push_back(attrs.GetMid());
}
}
if (!mids.empty()) {
if (mids.size() > 1) {
UniquePtr<SdpGroupAttributeList> groupAttr(new SdpGroupAttributeList);
groupAttr->PushEntry(SdpGroupAttributeList::kBundle, mids);
sdp->GetAttributeList().SetAttribute(groupAttr.release());
@ -987,13 +1027,12 @@ JsepSessionImpl::CreateAnswer(const JsepAnswerOptions& options,
const Sdp& offer = *mPendingRemoteDescription;
auto* group = FindBundleGroup(offer);
if (group) {
// Copy the bundle group into our answer
UniquePtr<SdpGroupAttributeList> groupAttr(new SdpGroupAttributeList);
groupAttr->mGroups.push_back(*group);
sdp->GetAttributeList().SetAttribute(groupAttr.release());
}
std::vector<SdpGroupAttributeList::Group> bundleGroups;
// Copy the bundle groups into our answer
UniquePtr<SdpGroupAttributeList> groupAttr(new SdpGroupAttributeList);
GetBundleGroups(offer, &groupAttr->mGroups);
sdp->GetAttributeList().SetAttribute(groupAttr.release());
// Disable send for local tracks if the offer no longer allows it
// (i.e., the m-section is recvonly, inactive or disabled)
@ -1519,10 +1558,8 @@ JsepSessionImpl::HandleNegotiatedSession(const UniquePtr<Sdp>& local,
const Sdp& answer = mIsOfferer ? *remote : *local;
std::set<std::string> bundleMids;
const SdpMediaSection* bundleMsection = nullptr;
// TODO(bug 1112692): Support more than one bundle group
nsresult rv = GetBundleInfo(answer, &bundleMids, &bundleMsection);
BundledMids bundledMids;
nsresult rv = GetBundledMids(answer, &bundledMids);
NS_ENSURE_SUCCESS(rv, rv);
std::vector<JsepTrackPair> trackPairs;
@ -1550,8 +1587,10 @@ JsepSessionImpl::HandleNegotiatedSession(const UniquePtr<Sdp>& local,
const SdpMediaSection& answerMsection(answer.GetMediaSection(i));
if (answerMsection.GetAttributeList().HasAttribute(
SdpAttribute::kMidAttribute)) {
if (bundleMids.count(answerMsection.GetAttributeList().GetMid())) {
transportLevel = bundleMsection->GetLevel();
if (bundledMids.count(answerMsection.GetAttributeList().GetMid())) {
const SdpMediaSection* masterBundleMsection =
bundledMids[answerMsection.GetAttributeList().GetMid()];
transportLevel = masterBundleMsection->GetLevel();
usingBundle = true;
if (i != transportLevel) {
mTransports[i]->Close();
@ -2262,14 +2301,13 @@ JsepSessionImpl::ValidateRemoteDescription(const Sdp& description)
return NS_ERROR_INVALID_ARG;
}
std::set<std::string> oldBundleMids;
const SdpMediaSection* oldBundleMsection;
nsresult rv = GetNegotiatedBundleInfo(&oldBundleMids, &oldBundleMsection);
// These are solely to check that bundle is valid
BundledMids bundledMids;
nsresult rv = GetNegotiatedBundledMids(&bundledMids);
NS_ENSURE_SUCCESS(rv, rv);
std::set<std::string> newBundleMids;
const SdpMediaSection* newBundleMsection;
rv = GetBundleInfo(description, &newBundleMids, &newBundleMsection);
BundledMids newBundledMids;
rv = GetBundledMids(description, &newBundledMids);
NS_ENSURE_SUCCESS(rv, rv);
for (size_t i = 0;
@ -2734,6 +2772,14 @@ JsepSessionImpl::EndOfLocalCandidates(const std::string& defaultCandidateAddr,
return NS_OK;
}
BundledMids bundledMids;
nsresult rv = GetNegotiatedBundledMids(&bundledMids);
if (NS_FAILED(rv)) {
MOZ_ASSERT(false);
mLastError += " (This should have been caught sooner!)";
return NS_ERROR_FAILURE;
}
std::string defaultRtcpCandidateAddrCopy(defaultRtcpCandidateAddr);
if (mState == kJsepStateStable && mTransports[level]->mComponents == 1) {
// We know we're doing rtcp-mux by now. Don't create an rtcp attr.
@ -2743,38 +2789,35 @@ JsepSessionImpl::EndOfLocalCandidates(const std::string& defaultCandidateAddr,
SdpMediaSection& msection = sdp->GetMediaSection(level);
// TODO(bug 1142105): Factor some of this out into a helper class
if (mState == kJsepStateStable) {
// offer/answer is done. Do we actually incorporate these defaults?
const Sdp* answer(GetAnswer());
std::set<std::string> bundleMids;
const SdpMediaSection* bundleMsection;
nsresult rv = GetBundleInfo(*answer, &bundleMids, &bundleMsection);
if (NS_FAILED(rv)) {
MOZ_ASSERT(false);
mLastError += " (This should have been caught sooner!)";
return NS_ERROR_FAILURE;
}
if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute) &&
bundleMids.count(msection.GetAttributeList().GetMid())) {
if (msection.GetLevel() != bundleMsection->GetLevel()) {
// Slave bundle m-section. Skip.
return NS_OK;
}
// Master bundle m-section. Set defaultCandidateAddr and
// defaultCandidatePort on all bundled m-sections.
for (auto i = bundleMids.begin(); i != bundleMids.end(); ++i) {
SdpMediaSection* bundledMsection = FindMsectionByMid(*sdp, *i);
if (!bundledMsection) {
MOZ_ASSERT(false);
continue;
if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
std::string mid(msection.GetAttributeList().GetMid());
if (bundledMids.count(mid)) {
const SdpMediaSection* masterBundleMsection(bundledMids[mid]);
if (msection.GetLevel() != masterBundleMsection->GetLevel()) {
// Slave bundle m-section. Skip.
return NS_OK;
}
// Master bundle m-section. Set defaultCandidateAddr and
// defaultCandidatePort on all bundled m-sections.
for (auto i = bundledMids.begin(); i != bundledMids.end(); ++i) {
if (i->second != masterBundleMsection) {
continue;
}
SdpMediaSection* bundledMsection = FindMsectionByMid(*sdp, i->first);
if (!bundledMsection) {
MOZ_ASSERT(false);
continue;
}
SetDefaultAddresses(defaultCandidateAddr,
defaultCandidatePort,
defaultRtcpCandidateAddrCopy,
defaultRtcpCandidatePort,
bundledMsection);
}
SetDefaultAddresses(defaultCandidateAddr,
defaultCandidatePort,
defaultRtcpCandidateAddrCopy,
defaultRtcpCandidatePort,
bundledMsection);
}
}
}
@ -2825,78 +2868,68 @@ JsepSessionImpl::FindMsectionByMid(const Sdp& sdp,
return nullptr;
}
const SdpGroupAttributeList::Group*
JsepSessionImpl::FindBundleGroup(const Sdp& sdp) const
void
JsepSessionImpl::GetBundleGroups(
const Sdp& sdp,
std::vector<SdpGroupAttributeList::Group>* bundleGroups) const
{
if (sdp.GetAttributeList().HasAttribute(SdpAttribute::kGroupAttribute)) {
auto& groups = sdp.GetAttributeList().GetGroup().mGroups;
for (auto i = groups.begin(); i != groups.end(); ++i) {
if (i->semantics == SdpGroupAttributeList::kBundle) {
return &(*i);
for (auto& group : sdp.GetAttributeList().GetGroup().mGroups) {
if (group.semantics == SdpGroupAttributeList::kBundle) {
bundleGroups->push_back(group);
}
}
}
return nullptr;
}
nsresult
JsepSessionImpl::GetNegotiatedBundleInfo(
std::set<std::string>* bundleMids,
const SdpMediaSection** bundleMsection)
JsepSessionImpl::GetNegotiatedBundledMids(BundledMids* bundledMids)
{
mozilla::Sdp* answerSdp = 0;
*bundleMsection = nullptr;
const Sdp* answerSdp = GetAnswer();
if (IsOfferer()) {
if (!mCurrentRemoteDescription) {
// Offer/answer not done.
return NS_OK;
}
answerSdp = mCurrentRemoteDescription.get();
} else {
if (mPendingLocalDescription) {
answerSdp = mPendingLocalDescription.get();
} else if (mCurrentLocalDescription) {
answerSdp = mCurrentLocalDescription.get();
} else {
MOZ_ASSERT(false);
JSEP_SET_ERROR("Is answerer, but no local description. This is a bug.");
return NS_ERROR_FAILURE;
}
if (!answerSdp) {
return NS_OK;
}
return GetBundleInfo(*answerSdp, bundleMids, bundleMsection);
return GetBundledMids(*answerSdp, bundledMids);
}
nsresult
JsepSessionImpl::GetBundleInfo(const Sdp& sdp,
std::set<std::string>* bundleMids,
const SdpMediaSection** bundleMsection)
JsepSessionImpl::GetBundledMids(const Sdp& sdp, BundledMids* bundledMids)
{
*bundleMsection = nullptr;
std::vector<SdpGroupAttributeList::Group> bundleGroups;
GetBundleGroups(sdp, &bundleGroups);
auto* group = FindBundleGroup(sdp);
if (group && !group->tags.empty()) {
bundleMids->insert(group->tags.begin(), group->tags.end());
*bundleMsection = FindMsectionByMid(sdp, group->tags[0]);
}
if (!bundleMids->empty()) {
if (!*bundleMsection) {
JSEP_SET_ERROR("mid specified for bundle transport in group attribute"
" does not exist in the SDP. (mid="
<< group->tags[0] << ")");
for (SdpGroupAttributeList::Group& group : bundleGroups) {
if (group.tags.empty()) {
JSEP_SET_ERROR("Empty BUNDLE group");
return NS_ERROR_INVALID_ARG;
}
if (MsectionIsDisabled(**bundleMsection)) {
const SdpMediaSection* masterBundleMsection(
FindMsectionByMid(sdp, group.tags[0]));
if (!masterBundleMsection) {
JSEP_SET_ERROR("mid specified for bundle transport in group attribute"
" points at a disabled m-section. (mid="
<< group->tags[0] << ")");
" does not exist in the SDP. (mid=" << group.tags[0] << ")");
return NS_ERROR_INVALID_ARG;
}
if (MsectionIsDisabled(*masterBundleMsection)) {
JSEP_SET_ERROR("mid specified for bundle transport in group attribute"
" points at a disabled m-section. (mid=" << group.tags[0] << ")");
return NS_ERROR_INVALID_ARG;
}
for (const std::string& mid : group.tags) {
if (bundledMids->count(mid)) {
JSEP_SET_ERROR("mid \'" << mid << "\' appears more than once in a "
"BUNDLE group");
return NS_ERROR_INVALID_ARG;
}
(*bundledMids)[mid] = masterBundleMsection;
}
}
return NS_OK;
@ -2911,23 +2944,17 @@ JsepSessionImpl::IsBundleSlave(const Sdp& sdp, uint16_t level)
// No mid, definitely no bundle for this m-section
return false;
}
std::string mid(msection.GetAttributeList().GetMid());
std::set<std::string> bundleMids;
const SdpMediaSection* bundleMsection;
nsresult rv = GetBundleInfo(sdp, &bundleMids, &bundleMsection);
BundledMids bundledMids;
nsresult rv = GetBundledMids(sdp, &bundledMids);
if (NS_FAILED(rv)) {
// Should have been caught sooner.
MOZ_ASSERT(false);
return false;
}
if (!bundleMsection) {
return false;
}
std::string mid(msection.GetAttributeList().GetMid());
if (bundleMids.count(mid) && level != bundleMsection->GetLevel()) {
if (bundledMids.count(mid) && level != bundledMids[mid]->GetLevel()) {
// mid is bundled, and isn't the bundle m-section
return true;
}

View File

@ -35,6 +35,7 @@ public:
mWasOffererLastTime(false),
mIceControlling(false),
mRemoteIsIceLite(false),
mBundlePolicy(kBundleBalanced),
mSessionId(0),
mSessionVersion(0),
mUuidGen(Move(uuidgen))
@ -51,6 +52,7 @@ public:
virtual nsresult SetIceCredentials(const std::string& ufrag,
const std::string& pwd) override;
nsresult SetBundlePolicy(JsepBundlePolicy policy) override;
virtual bool
RemoteIsIceLite() const override
@ -303,14 +305,16 @@ private:
const SdpMediaSection* FindMsectionByMid(const Sdp& sdp,
const std::string& mid) const;
const SdpGroupAttributeList::Group* FindBundleGroup(const Sdp& sdp) const;
void GetBundleGroups(const Sdp& sdp,
std::vector<SdpGroupAttributeList::Group>* groups) const;
nsresult GetNegotiatedBundleInfo(std::set<std::string>* bundleMids,
const SdpMediaSection** bundleMsection);
// Maps each mid to the m-section that is the master of its bundle.
// Mids that do not appear in an a=group:BUNDLE do not appear here.
typedef std::map<std::string, const SdpMediaSection*> BundledMids;
nsresult GetBundleInfo(const Sdp& sdp,
std::set<std::string>* bundleMids,
const SdpMediaSection** bundleMsection);
nsresult GetNegotiatedBundledMids(BundledMids* bundledMids);
nsresult GetBundledMids(const Sdp& sdp, BundledMids* bundledMids);
bool IsBundleSlave(const Sdp& localSdp, uint16_t level);
@ -341,6 +345,7 @@ private:
std::string mIcePwd;
bool mRemoteIsIceLite;
std::vector<std::string> mIceOptions;
JsepBundlePolicy mBundlePolicy;
std::vector<JsepDtlsFingerprint> mDtlsFingerprints;
uint64_t mSessionId;
uint64_t mSessionVersion;

View File

@ -493,6 +493,7 @@ PeerConnectionImpl::CreateRemoteSourceStreamInfo(nsRefPtr<RemoteSourceStreamInfo
return NS_OK;
}
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
/**
* In JS, an RTCConfiguration looks like this:
*
@ -500,28 +501,38 @@ PeerConnectionImpl::CreateRemoteSourceStreamInfo(nsRefPtr<RemoteSourceStreamInfo
* { url:"turn:turn.example.org?transport=udp",
* username: "jib", credential:"mypass"} ] }
*
* This function converts that into an internal IceConfiguration object.
* This function converts that into an internal PeerConnectionConfiguration
* object.
*/
nsresult
PeerConnectionImpl::ConvertRTCConfiguration(const RTCConfiguration& aSrc,
IceConfiguration *aDst)
PeerConnectionConfiguration::Init(const RTCConfiguration& aSrc)
{
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
if (aSrc.mIceServers.WasPassed()) {
for (size_t i = 0; i < aSrc.mIceServers.Value().Length(); i++) {
nsresult rv = AddIceServer(aSrc.mIceServers.Value()[i], aDst);
nsresult rv = AddIceServer(aSrc.mIceServers.Value()[i]);
NS_ENSURE_SUCCESS(rv, rv);
}
}
#endif
switch (aSrc.mBundlePolicy) {
case dom::RTCBundlePolicy::Balanced:
setBundlePolicy(kBundleBalanced);
break;
case dom::RTCBundlePolicy::Max_compat:
setBundlePolicy(kBundleMaxCompat);
break;
case dom::RTCBundlePolicy::Max_bundle:
setBundlePolicy(kBundleMaxBundle);
break;
default:
MOZ_CRASH();
}
return NS_OK;
}
nsresult
PeerConnectionImpl::AddIceServer(const RTCIceServer &aServer,
IceConfiguration *aDst)
PeerConnectionConfiguration::AddIceServer(const RTCIceServer &aServer)
{
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
NS_ENSURE_STATE(aServer.mUrls.WasPassed());
NS_ENSURE_STATE(aServer.mUrls.Value().IsStringSequence());
auto &urls = aServer.mUrls.Value().GetAsStringSequence();
@ -602,35 +613,32 @@ PeerConnectionImpl::AddIceServer(const RTCIceServer &aServer,
continue;
}
if (!aDst->addTurnServer(host.get(), port,
username.get(),
credential.get(),
(transport.IsEmpty() ?
kNrIceTransportUdp : transport.get()))) {
if (!addTurnServer(host.get(), port,
username.get(),
credential.get(),
(transport.IsEmpty() ?
kNrIceTransportUdp : transport.get()))) {
return NS_ERROR_FAILURE;
}
} else {
if (!aDst->addStunServer(host.get(), port, (transport.IsEmpty() ?
kNrIceTransportUdp : transport.get()))) {
if (!addStunServer(host.get(), port, (transport.IsEmpty() ?
kNrIceTransportUdp : transport.get()))) {
return NS_ERROR_FAILURE;
}
}
}
#endif
return NS_OK;
}
#endif
NS_IMETHODIMP
nsresult
PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
nsGlobalWindow* aWindow,
const IceConfiguration* aConfiguration,
const RTCConfiguration* aRTCConfiguration,
const PeerConnectionConfiguration& aConfiguration,
nsISupports* aThread)
{
nsresult res;
// Invariant: we receive configuration one way or the other but not both (XOR)
MOZ_ASSERT(!aConfiguration != !aRTCConfiguration);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aThread);
mThread = do_QueryInterface(aThread);
@ -659,11 +667,6 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
MOZ_ASSERT(aWindow);
mWindow = aWindow;
NS_ENSURE_STATE(mWindow);
if (!aRTCConfiguration->mPeerIdentity.IsEmpty()) {
mPeerIdentity = new PeerIdentity(aRTCConfiguration->mPeerIdentity);
mPrivacyRequested = true;
}
#endif // MOZILLA_INTERNAL_API
PRTime timestamp = PR_Now();
@ -723,17 +726,6 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
res = PeerConnectionCtx::InitializeGlobal(mThread, mSTSThread);
NS_ENSURE_SUCCESS(res, res);
IceConfiguration converted;
if (aRTCConfiguration) {
res = ConvertRTCConfiguration(*aRTCConfiguration, &converted);
if (NS_FAILED(res)) {
CSFLogError(logTag, "%s: Invalid RTCConfiguration", __FUNCTION__);
return res;
}
aConfiguration = &converted;
}
mMedia = new PeerConnectionMedia(this);
// Connect ICE slots.
@ -750,8 +742,8 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
mMedia->SignalCandidate.connect(this, &PeerConnectionImpl::CandidateReady);
// Initialize the media object.
res = mMedia->Init(aConfiguration->getStunServers(),
aConfiguration->getTurnServers());
res = mMedia->Init(aConfiguration.getStunServers(),
aConfiguration.getTurnServers());
if (NS_FAILED(res)) {
CSFLogError(logTag, "%s: Couldn't initialize media object", __FUNCTION__);
return res;
@ -801,9 +793,46 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
return res;
}
res = mJsepSession->SetBundlePolicy(aConfiguration.getBundlePolicy());
if (NS_FAILED(res)) {
CSFLogError(logTag, "%s: Couldn't set bundle policy, res=%u, error=%s",
__FUNCTION__,
static_cast<unsigned>(res),
mJsepSession->GetLastError().c_str());
return res;
}
return NS_OK;
}
#ifndef MOZILLA_EXTERNAL_LINKAGE
void
PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
nsGlobalWindow& aWindow,
const RTCConfiguration& aConfiguration,
nsISupports* aThread,
ErrorResult &rv)
{
PeerConnectionConfiguration converted;
nsresult res = converted.Init(aConfiguration);
if (NS_FAILED(res)) {
CSFLogError(logTag, "%s: Invalid RTCConfiguration", __FUNCTION__);
rv.Throw(res);
return;
}
res = Initialize(aObserver, &aWindow, converted, aThread);
if (NS_FAILED(res)) {
rv.Throw(res);
}
if (!aConfiguration.mPeerIdentity.IsEmpty()) {
mPeerIdentity = new PeerIdentity(aConfiguration.mPeerIdentity);
mPrivacyRequested = true;
}
}
#endif
class CompareCodecPriority {
public:
void SetPreferredCodec(int32_t preferredCodec) {

View File

@ -146,9 +146,11 @@ class PCUuidGenerator : public mozilla::JsepUuidGenerator {
nsCOMPtr<nsIUUIDGenerator> mGenerator;
};
class IceConfiguration
class PeerConnectionConfiguration
{
public:
PeerConnectionConfiguration() : mBundlePolicy(kBundleBalanced) {}
bool addStunServer(const std::string& addr, uint16_t port,
const char* transport)
{
@ -182,9 +184,18 @@ public:
void addTurnServer(const NrIceTurnServer& server) { mTurnServers.push_back (server); }
const std::vector<NrIceStunServer>& getStunServers() const { return mStunServers; }
const std::vector<NrIceTurnServer>& getTurnServers() const { return mTurnServers; }
void setBundlePolicy(JsepBundlePolicy policy) { mBundlePolicy = policy;}
JsepBundlePolicy getBundlePolicy() const { return mBundlePolicy; }
#ifndef MOZILLA_EXTERNAL_LINKAGE
nsresult Init(const RTCConfiguration& aSrc);
nsresult AddIceServer(const RTCIceServer& aServer);
#endif
private:
std::vector<NrIceStunServer> mStunServers;
std::vector<NrIceTurnServer> mTurnServers;
JsepBundlePolicy mBundlePolicy;
};
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
@ -261,10 +272,6 @@ public:
static already_AddRefed<PeerConnectionImpl>
Constructor(const mozilla::dom::GlobalObject& aGlobal, ErrorResult& rv);
static PeerConnectionImpl* CreatePeerConnection();
static nsresult ConvertRTCConfiguration(const RTCConfiguration& aSrc,
IceConfiguration *aDst);
static nsresult AddIceServer(const RTCIceServer& aServer,
IceConfiguration* aDst);
already_AddRefed<DOMMediaStream> MakeMediaStream();
nsresult CreateRemoteSourceStreamInfo(nsRefPtr<RemoteSourceStreamInfo>* aInfo,
@ -334,26 +341,22 @@ public:
return mWindow;
}
// Initialize PeerConnection from an IceConfiguration object (unit-tests)
// Initialize PeerConnection from a PeerConnectionConfiguration object
// (used directly by unit-tests, and indirectly by the JS entry point)
// This is necessary because RTCConfiguration can't be used by unit-tests
nsresult Initialize(PeerConnectionObserver& aObserver,
nsGlobalWindow* aWindow,
const IceConfiguration& aConfiguration,
nsIThread* aThread) {
return Initialize(aObserver, aWindow, &aConfiguration, nullptr, aThread);
}
const PeerConnectionConfiguration& aConfiguration,
nsISupports* aThread);
#ifndef MOZILLA_EXTERNAL_LINKAGE
// Initialize PeerConnection from an RTCConfiguration object (JS entrypoint)
void Initialize(PeerConnectionObserver& aObserver,
nsGlobalWindow& aWindow,
const RTCConfiguration& aConfiguration,
nsISupports* aThread,
ErrorResult &rv)
{
nsresult r = Initialize(aObserver, &aWindow, nullptr, &aConfiguration, aThread);
if (NS_FAILED(r)) {
rv.Throw(r);
}
}
ErrorResult &rv);
#endif
NS_IMETHODIMP_TO_ERRORRESULT(CreateOffer, ErrorResult &rv,
const RTCOfferOptions& aOptions)
@ -616,11 +619,6 @@ private:
virtual ~PeerConnectionImpl();
PeerConnectionImpl(const PeerConnectionImpl&rhs);
PeerConnectionImpl& operator=(PeerConnectionImpl);
NS_IMETHODIMP Initialize(PeerConnectionObserver& aObserver,
nsGlobalWindow* aWindow,
const IceConfiguration* aConfiguration,
const RTCConfiguration* aRTCConfiguration,
nsISupports* aThread);
nsresult CalculateFingerprint(const std::string& algorithm,
std::vector<uint8_t>& fingerprint) const;
nsresult ConfigureJsepSessionCodecs();

View File

@ -312,14 +312,8 @@ protected:
return false;
}
if (p1.mBundleLevel.isSome() != p2.mBundleLevel.isSome()) {
return false;
}
if (p1.mBundleLevel.isSome() &&
*p1.mBundleLevel != *p2.mBundleLevel) {
return false;
}
// We don't check things like mBundleLevel, since that can change without
// any changes to the transport, which is what we're really interested in.
if (p1.mSending.get() != p2.mSending.get()) {
return false;
@ -797,6 +791,20 @@ protected:
}
}
void CheckPairs(const JsepSession& session, const std::string& context)
{
auto pairs = session.GetNegotiatedTrackPairs();
for (JsepTrackPair& pair : pairs) {
if (types.size() == 1) {
ASSERT_FALSE(pair.mBundleLevel.isSome()) << context;
} else {
ASSERT_TRUE(pair.mBundleLevel.isSome()) << context;
ASSERT_EQ(0U, *pair.mBundleLevel) << context;
}
}
}
void
DisableMsid(std::string* sdp) const {
size_t pos = sdp->find("a=msid-semantic");
@ -1888,6 +1896,12 @@ TEST_P(JsepSessionTest, RenegotiationOffererEnablesBundle)
{
AddTracks(mSessionOff);
AddTracks(mSessionAns);
if (types.size() < 2) {
// No bundle will happen here.
return;
}
std::string offer = CreateOffer();
DisableBundle(&offer);
@ -3619,6 +3633,82 @@ TEST_P(JsepSessionTest, TestInvalidRollback)
mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
}
size_t GetActiveTransportCount(const JsepSession& session)
{
auto transports = session.GetTransports();
size_t activeTransportCount = 0;
for (RefPtr<JsepTransport>& transport : transports) {
activeTransportCount += transport->mComponents;
}
return activeTransportCount;
}
TEST_P(JsepSessionTest, TestBalancedBundle)
{
AddTracks(mSessionOff);
AddTracks(mSessionAns);
mSessionOff.SetBundlePolicy(kBundleBalanced);
std::string offer = CreateOffer();
SipccSdpParser parser;
UniquePtr<Sdp> parsedOffer = parser.Parse(offer);
ASSERT_TRUE(parsedOffer.get());
std::map<SdpMediaSection::MediaType, SdpMediaSection*> firstByType;
for (size_t i = 0; i < parsedOffer->GetMediaSectionCount(); ++i) {
SdpMediaSection& msection(parsedOffer->GetMediaSection(i));
bool firstOfType = !firstByType.count(msection.GetMediaType());
if (firstOfType) {
firstByType[msection.GetMediaType()] = &msection;
}
ASSERT_EQ(!firstOfType,
msection.GetAttributeList().HasAttribute(
SdpAttribute::kBundleOnlyAttribute));
}
SetLocalOffer(offer);
SetRemoteOffer(offer);
std::string answer = CreateAnswer();
SetLocalAnswer(answer);
SetRemoteAnswer(answer);
CheckPairs(mSessionOff, "Offerer pairs");
CheckPairs(mSessionAns, "Answerer pairs");
EXPECT_EQ(1U, GetActiveTransportCount(mSessionOff));
EXPECT_EQ(1U, GetActiveTransportCount(mSessionAns));
}
TEST_P(JsepSessionTest, TestMaxBundle)
{
AddTracks(mSessionOff);
AddTracks(mSessionAns);
mSessionOff.SetBundlePolicy(kBundleMaxBundle);
OfferAnswer();
std::string offer = mSessionOff.GetLocalDescription();
SipccSdpParser parser;
UniquePtr<Sdp> parsedOffer = parser.Parse(offer);
ASSERT_TRUE(parsedOffer.get());
ASSERT_FALSE(
parsedOffer->GetMediaSection(0).GetAttributeList().HasAttribute(
SdpAttribute::kBundleOnlyAttribute));
for (size_t i = 1; i < parsedOffer->GetMediaSectionCount(); ++i) {
ASSERT_TRUE(
parsedOffer->GetMediaSection(i).GetAttributeList().HasAttribute(
SdpAttribute::kBundleOnlyAttribute));
}
CheckPairs(mSessionOff, "Offerer pairs");
CheckPairs(mSessionAns, "Answerer pairs");
EXPECT_EQ(1U, GetActiveTransportCount(mSessionOff));
EXPECT_EQ(1U, GetActiveTransportCount(mSessionAns));
}
} // namespace mozilla
int

View File

@ -645,9 +645,9 @@ class PCDispatchWrapper : public nsSupportsWeakReference
}
NS_IMETHODIMP Initialize(TestObserver* aObserver,
nsGlobalWindow* aWindow,
const IceConfiguration& aConfiguration,
nsIThread* aThread) {
nsGlobalWindow* aWindow,
const PeerConnectionConfiguration& aConfiguration,
nsIThread* aThread) {
nsresult rv;
observer_ = aObserver;
@ -942,6 +942,11 @@ class SignalingAgent {
mBundleEnabled = enabled;
}
void SetBundlePolicy(JsepBundlePolicy policy)
{
cfg_.setBundlePolicy(policy);
}
void SetExpectedFrameRequestType(VideoSessionConduit::FrameRequestType type)
{
mExpectedFrameRequestType = type;
@ -1540,7 +1545,7 @@ public:
std::string offer_;
std::string answer_;
std::vector<nsRefPtr<DOMMediaStream>> domMediaStreams_;
IceConfiguration cfg_;
PeerConnectionConfiguration cfg_;
const std::string name;
bool mBundleEnabled;
VideoSessionConduit::FrameRequestType mExpectedFrameRequestType;
@ -1701,20 +1706,35 @@ public:
a1_ = new SignalingAgent(callerName, stun_addr_, stun_port_);
a2_ = new SignalingAgent(calleeName, stun_addr_, stun_port_);
a1_->Init();
a2_->Init();
if (GetParam() == "no_bundle") {
a1_->SetBundleEnabled(false);
} else if(GetParam() == "reject_bundle") {
a2_->SetBundleEnabled(false);
} else if (GetParam() == "max-bundle") {
a1_->SetBundlePolicy(JsepBundlePolicy::kBundleMaxBundle);
a2_->SetBundlePolicy(JsepBundlePolicy::kBundleMaxBundle);
} else if (GetParam() == "balanced") {
a1_->SetBundlePolicy(JsepBundlePolicy::kBundleBalanced);
a2_->SetBundlePolicy(JsepBundlePolicy::kBundleBalanced);
} else if (GetParam() == "max-compat") {
a1_->SetBundlePolicy(JsepBundlePolicy::kBundleMaxCompat);
a2_->SetBundlePolicy(JsepBundlePolicy::kBundleMaxCompat);
}
a1_->Init();
a2_->Init();
a1_->SetPeer(a2_.get());
a2_->SetPeer(a1_.get());
init_ = true;
}
bool UseBundle()
{
return (GetParam() != "no_bundle") && (GetParam() != "reject_bundle");
}
void WaitForGather() {
a1_->WaitForGather();
a2_->WaitForGather();
@ -2470,7 +2490,7 @@ TEST_P(SignalingTest, RenegotiationAnswererReplacesTrack)
TEST_P(SignalingTest, BundleRenegotiation)
{
if (GetParam() == "bundle") {
if (UseBundle()) {
// We don't support ICE restart, which is a prereq for renegotiating bundle
// off.
return;
@ -3377,7 +3397,7 @@ TEST_P(SignalingTest, AudioOnlyG722Rejected)
TEST_P(SignalingTest, FullCallAudioNoMuxVideoMux)
{
if (GetParam() == "bundle") {
if (UseBundle()) {
// This test doesn't make sense for bundle
return;
}
@ -4510,7 +4530,7 @@ TEST_P(SignalingTest, AudioNegotiationFails)
TEST_P(SignalingTest, BundleStreamCorrelationBySsrc)
{
if (GetParam() != "bundle") {
if (!UseBundle()) {
return;
}
@ -4558,7 +4578,7 @@ TEST_P(SignalingTest, BundleStreamCorrelationBySsrc)
TEST_P(SignalingTest, BundleStreamCorrelationByUniquePt)
{
if (GetParam() != "bundle") {
if (!UseBundle()) {
return;
}
@ -4609,7 +4629,9 @@ TEST_P(SignalingTest, BundleStreamCorrelationByUniquePt)
}
INSTANTIATE_TEST_CASE_P(Variants, SignalingTest,
::testing::Values("bundle",
::testing::Values("max-bundle",
"balanced",
"max-compat",
"no_bundle",
"reject_bundle"));