mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1112692: BundlePolicy support, and support for more than one BUNDLE group. r=mt, r=smaug
This commit is contained in:
parent
7248c7e4fe
commit
324564a2e9
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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"));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user