Bug 952145: Rollback support r=mt, r=smaug

--HG--
extra : rebase_source : c5615117716ca907d61b4b7c7eeff71e926821bd
This commit is contained in:
Byron Campen [:bwc] 2015-03-19 17:32:51 -07:00
parent 9fce2db2f1
commit 1fec8cf0e8
19 changed files with 689 additions and 292 deletions

View File

@ -695,6 +695,9 @@ RTCPeerConnection.prototype = {
case "pranswer":
throw new this._win.DOMException("pranswer not yet implemented",
"NotSupportedError");
case "rollback":
type = Ci.IPeerConnection.kActionRollback;
break;
default:
throw new this._win.DOMException(
"Invalid type " + desc.type + " provided to setLocalDescription",
@ -768,6 +771,9 @@ RTCPeerConnection.prototype = {
case "pranswer":
throw new this._win.DOMException("pranswer not yet implemented",
"NotSupportedError");
case "rollback":
type = Ci.IPeerConnection.kActionRollback;
break;
default:
throw new this._win.DOMException(
"Invalid type " + desc.type + " provided to setRemoteDescription",

View File

@ -28,7 +28,7 @@ interface IPeerConnectionObserver : nsISupports
{
};
[scriptable, uuid(c9c31639-1a49-4533-8429-f6a348c4d8c3)]
[scriptable, uuid(14afc8e7-e421-4d0c-99a5-69308d871481)]
interface IPeerConnection : nsISupports
{
const unsigned long kHintAudio = 0x00000001;
@ -38,6 +38,7 @@ interface IPeerConnection : nsISupports
const long kActionOffer = 0;
const long kActionAnswer = 1;
const long kActionPRAnswer = 2;
const long kActionRollback = 3;
const long kIceGathering = 0;
const long kIceWaiting = 1;

View File

@ -380,8 +380,8 @@ CommandChain.prototype = {
/**
* Inserts the new commands after the specified command.
*/
insertAfter: function(functionOrName, commands) {
this._insertHelper(functionOrName, commands, 1);
insertAfter: function(functionOrName, commands, all, start) {
this._insertHelper(functionOrName, commands, 1, all, start);
},
/**
@ -431,8 +431,8 @@ CommandChain.prototype = {
/**
* Removes all commands after the specified one, returns what was removed.
*/
removeAfter: function(functionOrName) {
var index = this.indexOf(functionOrName);
removeAfter: function(functionOrName, start) {
var index = this.indexOf(functionOrName, start);
if (index >= 0) {
return this.commands.splice(index + 1);
}
@ -461,8 +461,8 @@ CommandChain.prototype = {
/**
* Replaces all commands after the specified one, returns what was removed.
*/
replaceAfter: function(functionOrName, commands) {
var oldCommands = this.removeAfter(functionOrName);
replaceAfter: function(functionOrName, commands, start) {
var oldCommands = this.removeAfter(functionOrName, start);
this.append(commands);
return oldCommands;
},

View File

@ -196,6 +196,14 @@ skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
[test_peerConnection_webAudio.html]
tags = webaudio webrtc
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
[test_peerConnection_localRollback.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
[test_peerConnection_localReofferRollback.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
[test_peerConnection_remoteRollback.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
[test_peerConnection_remoteReofferRollback.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
# Bug 950317: Hack for making a cleanup hook after finishing all WebRTC cases
[test_zmedia_cleanup.html]

View File

@ -774,7 +774,6 @@ function PeerConnectionWrapper(label, configuration, h264) {
this._local_ice_candidates = [];
this._remote_ice_candidates = [];
this.holdIceCandidates = new Promise(r => this.releaseIceCandidates = r);
this.localRequiresTrickleIce = false;
this.remoteRequiresTrickleIce = false;
this.localMediaElements = [];
@ -1085,7 +1084,12 @@ PeerConnectionWrapper.prototype = {
this.observedNegotiationNeeded = undefined;
return this._pc.setRemoteDescription(desc).then(() => {
info(this + ": Successfully set remote description");
this.releaseIceCandidates();
if (desc.type == "rollback") {
this.holdIceCandidates = new Promise(r => this.releaseIceCandidates = r);
} else {
this.releaseIceCandidates();
}
});
},
@ -1318,6 +1322,7 @@ PeerConnectionWrapper.prototype = {
var resolveEndOfTrickle;
this.endOfTrickleIce = new Promise(r => resolveEndOfTrickle = r);
this.holdIceCandidates = new Promise(r => this.releaseIceCandidates = r);
this.endOfTrickleIce.then(() => {
this._pc.onicecandidate = () =>

View File

@ -0,0 +1,57 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "952145",
title: "Rollback local reoffer"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain, [
function PC_LOCAL_ADD_SECOND_STREAM(test) {
test.setMediaConstraints([{audio: true}, {audio: true}],
[{audio: true}]);
return test.pcLocal.getAllUserMedia([{audio: true}]);
},
function PC_REMOTE_SETUP_ICE_HANDLER(test) {
test.pcRemote.setupIceCandidateHandler(test);
if (test.steeplechase) {
test.pcRemote.endOfTrickleIce.then(() => {
send_message({"type": "end_of_trickle_ice"});
});
}
},
function PC_REMOTE_CREATE_AND_SET_OFFER(test) {
return test.createOffer(test.pcRemote).then(offer => {
return test.setLocalDescription(test.pcRemote, offer, HAVE_LOCAL_OFFER);
});
},
function PC_REMOTE_ROLLBACK(test) {
return test.setLocalDescription(
test.pcRemote,
new mozRTCSessionDescription({ type: "rollback", sdp: ""}),
STABLE);
},
// Rolling back should shut down gathering
function PC_REMOTE_WAIT_FOR_END_OF_TRICKLE(test) {
return test.pcRemote.endOfTrickleIce;
},
]);
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,51 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "952145",
title: "Rollback local offer"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.chain.insertBefore('PC_LOCAL_CREATE_OFFER', [
function PC_REMOTE_CREATE_AND_SET_OFFER(test) {
return test.createOffer(test.pcRemote).then(offer => {
return test.setLocalDescription(test.pcRemote, offer, HAVE_LOCAL_OFFER);
});
},
function PC_REMOTE_ROLLBACK(test) {
return test.setLocalDescription(
test.pcRemote,
new mozRTCSessionDescription({ type: "rollback", sdp: ""}),
STABLE);
},
// Rolling back should shut down gathering
function PC_REMOTE_WAIT_FOR_END_OF_TRICKLE(test) {
return test.pcRemote.endOfTrickleIce;
},
function PC_REMOTE_SETUP_ICE_HANDLER(test) {
test.pcRemote.setupIceCandidateHandler(test);
if (test.steeplechase) {
test.pcRemote.endOfTrickleIce.then(() => {
send_message({"type": "end_of_trickle_ice"});
});
}
},
]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,66 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "952145",
title: "Rollback remote reoffer"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
addRenegotiation(test.chain,
[
function PC_LOCAL_ADD_SECOND_STREAM(test) {
test.setMediaConstraints([{audio: true}, {audio: true}],
[{audio: true}]);
return test.pcLocal.getAllUserMedia([{audio: true}]);
},
]
);
test.chain.replaceAfter('PC_REMOTE_SET_REMOTE_DESCRIPTION',
[
function PC_LOCAL_SETUP_ICE_HANDLER(test) {
test.pcLocal.setupIceCandidateHandler(test);
if (test.steeplechase) {
test.pcLocal.endOfTrickleIce.then(() => {
send_message({"type": "end_of_trickle_ice"});
});
}
},
function PC_REMOTE_ROLLBACK(test) {
return test.setRemoteDescription(
test.pcRemote,
new mozRTCSessionDescription({ type: "rollback" }),
STABLE);
},
function PC_LOCAL_ROLLBACK(test) {
return test.setLocalDescription(
test.pcLocal,
new mozRTCSessionDescription({ type: "rollback", sdp: ""}),
STABLE);
},
// Rolling back should shut down gathering
function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) {
return test.pcLocal.endOfTrickleIce;
},
],
1 // Second PC_REMOTE_SET_REMOTE_DESCRIPTION
);
test.chain.append(commandsPeerConnectionOfferAnswer);
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,46 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "952145",
title: "Rollback remote offer"
});
var test;
runNetworkTest(function (options) {
test = new PeerConnectionTest(options);
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.chain.removeAfter('PC_REMOTE_SET_REMOTE_DESCRIPTION');
test.chain.append([
function PC_REMOTE_ROLLBACK(test) {
return test.setRemoteDescription(
test.pcRemote,
new mozRTCSessionDescription({ type: "rollback" }),
STABLE);
},
function PC_LOCAL_ROLLBACK(test) {
return test.setLocalDescription(
test.pcLocal,
new mozRTCSessionDescription({ type: "rollback", sdp: ""}),
STABLE);
},
// Rolling back should shut down gathering
function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) {
return test.pcLocal.endOfTrickleIce;
},
]);
test.chain.append(commandsPeerConnectionOfferAnswer);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -10,12 +10,13 @@
enum RTCSdpType {
"offer",
"pranswer",
"answer"
"answer",
"rollback"
};
dictionary RTCSessionDescriptionInit {
RTCSdpType? type = null;
DOMString? sdp = null;
DOMString? sdp = "";
};
[Pref="media.peerconnection.enabled",

View File

@ -36,6 +36,7 @@ enum JsepSdpType {
kJsepSdpOffer,
kJsepSdpAnswer,
kJsepSdpPranswer,
kJsepSdpRollback
};
struct JsepOAOptions {};

View File

@ -119,7 +119,7 @@ JsepSessionImpl::AddTrack(const RefPtr<JsepTrack>& track)
JsepSendingTrack strack;
strack.mTrack = track;
strack.mSetInLocalDescription = false;
strack.mNegotiated = false;
mLocalTracks.push_back(strack);
@ -400,7 +400,7 @@ JsepSessionImpl::BindTrackToMsection(
}
msection->SetSending(true);
track->mAssignedMLine = Some(msection->GetLevel());
track->mSetInLocalDescription = false;
track->mNegotiated = false;
return NS_OK;
}
@ -533,7 +533,7 @@ JsepSessionImpl::SetupBundle(Sdp* sdp) const
}
nsresult
JsepSessionImpl::SetupTransportAttributes(Sdp* offer)
JsepSessionImpl::FinalizeTransportAttributes(Sdp* offer)
{
const Sdp* oldAnswer = GetAnswer();
@ -754,11 +754,10 @@ JsepSessionImpl::CreateOffer(const JsepOfferOptions& options,
NS_ENSURE_SUCCESS(rv, rv);
}
// Get rid of all m-line assignments that have not been executed by a call
// to SetLocalDescription.
// Get rid of all m-line assignments that have not been negotiated
if (NS_SUCCEEDED(rv)) {
for (auto i = mLocalTracks.begin(); i != mLocalTracks.end(); ++i) {
if (!i->mSetInLocalDescription) {
if (!i->mNegotiated) {
i->mAssignedMLine.reset();
}
}
@ -770,7 +769,7 @@ JsepSessionImpl::CreateOffer(const JsepOfferOptions& options,
SetupBundle(sdp.get());
rv = SetupTransportAttributes(sdp.get());
rv = FinalizeTransportAttributes(sdp.get());
NS_ENSURE_SUCCESS(rv,rv);
*offer = sdp->ToString();
@ -1003,9 +1002,8 @@ JsepSessionImpl::CreateAnswer(const JsepAnswerOptions& options,
continue;
}
// Get rid of all m-line assignments that have not been executed by a call
// to SetLocalDescription.
if (!i->mSetInLocalDescription) {
// Get rid of all m-line assignments that have not been negotiated
if (!i->mNegotiated) {
i->mAssignedMLine.reset();
continue;
}
@ -1323,6 +1321,21 @@ JsepSessionImpl::SetLocalDescription(JsepSdpType type, const std::string& sdp)
MOZ_MTLOG(ML_DEBUG, "SetLocalDescription type=" << type << "\nSDP=\n"
<< sdp);
if (type == kJsepSdpRollback) {
if (mState != kJsepStateHaveLocalOffer) {
JSEP_SET_ERROR("Cannot rollback local description in "
<< GetStateStr(mState));
return NS_ERROR_UNEXPECTED;
}
mPendingLocalDescription.reset();
SetState(kJsepStateStable);
mTransports = mOldTransports;
mOldTransports.clear();
return NS_OK;
}
switch (mState) {
case kJsepStateStable:
if (type != kJsepSdpOffer) {
@ -1354,18 +1367,13 @@ JsepSessionImpl::SetLocalDescription(JsepSdpType type, const std::string& sdp)
NS_ENSURE_SUCCESS(rv, rv);
// Create transport objects.
size_t numMsections = parsed->GetMediaSectionCount();
for (size_t t = 0; t < numMsections; ++t) {
if (t < mTransports.size()) {
mTransports[t]->mState = JsepTransport::kJsepTransportOffered;
continue; // This transport already exists (assume we are renegotiating).
mOldTransports = mTransports;
for (size_t t = 0; t < parsed->GetMediaSectionCount(); ++t) {
if (t >= mTransports.size()) {
mTransports.push_back(RefPtr<JsepTransport>(new JsepTransport));
}
RefPtr<JsepTransport> transport;
nsresult rv = CreateTransport(parsed->GetMediaSection(t), &transport);
NS_ENSURE_SUCCESS(rv, rv);
mTransports.push_back(transport);
UpdateTransport(parsed->GetMediaSection(t), mTransports[t].get());
}
switch (type) {
@ -1376,15 +1384,8 @@ JsepSessionImpl::SetLocalDescription(JsepSdpType type, const std::string& sdp)
case kJsepSdpPranswer:
rv = SetLocalDescriptionAnswer(type, Move(parsed));
break;
}
// Mark all assigned local tracks as added to the local description
if (NS_SUCCEEDED(rv)) {
for (auto i = mLocalTracks.begin(); i != mLocalTracks.end(); ++i) {
if (i->mAssignedMLine.isSome()) {
i->mSetInLocalDescription = true;
}
}
case kJsepSdpRollback:
MOZ_CRASH(); // Handled above
}
return rv;
@ -1430,6 +1431,21 @@ JsepSessionImpl::SetRemoteDescription(JsepSdpType type, const std::string& sdp)
MOZ_MTLOG(ML_DEBUG, "SetRemoteDescription type=" << type << "\nSDP=\n"
<< sdp);
if (type == kJsepSdpRollback) {
if (mState != kJsepStateHaveRemoteOffer) {
JSEP_SET_ERROR("Cannot rollback remote description in "
<< GetStateStr(mState));
return NS_ERROR_UNEXPECTED;
}
mPendingRemoteDescription.reset();
SetState(kJsepStateStable);
// Update the remote tracks to what they were before the SetRemote
return SetRemoteTracksFromDescription(mCurrentRemoteDescription.get());
}
switch (mState) {
case kJsepStateStable:
if (type != kJsepSdpOffer) {
@ -1478,6 +1494,8 @@ JsepSessionImpl::SetRemoteDescription(JsepSdpType type, const std::string& sdp)
case kJsepSdpPranswer:
rv = SetRemoteDescriptionAnswer(type, Move(parsed));
break;
case kJsepSdpRollback:
MOZ_CRASH(); // Handled above
}
if (NS_SUCCEEDED(rv)) {
@ -1507,11 +1525,18 @@ JsepSessionImpl::HandleNegotiatedSession(const UniquePtr<Sdp>& local,
std::vector<JsepTrackPair> trackPairs;
if (mTransports.size() < local->GetMediaSectionCount()) {
JSEP_SET_ERROR("Fewer transports set up than m-lines");
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
// Now walk through the m-sections, make sure they match, and create
// track pairs that describe the media to be set up.
for (size_t i = 0; i < local->GetMediaSectionCount(); ++i) {
// Skip disabled m-sections.
if (answer.GetMediaSection(i).GetPort() == 0) {
mTransports[i]->Close();
continue;
}
@ -1526,20 +1551,16 @@ JsepSessionImpl::HandleNegotiatedSession(const UniquePtr<Sdp>& local,
if (bundleMids.count(answerMsection.GetAttributeList().GetMid())) {
transportLevel = bundleMsection->GetLevel();
usingBundle = true;
if (i != transportLevel) {
mTransports[i]->Close();
}
}
}
}
// Transports are created in SetLocal.
if (mTransports.size() < transportLevel) {
JSEP_SET_ERROR("Fewer transports set up than m-lines");
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
RefPtr<JsepTransport> transport = mTransports[transportLevel];
rv = SetupTransport(
rv = FinalizeTransport(
remote->GetMediaSection(transportLevel).GetAttributeList(),
answer.GetMediaSection(transportLevel).GetAttributeList(),
transport);
@ -1571,15 +1592,16 @@ JsepSessionImpl::HandleNegotiatedSession(const UniquePtr<Sdp>& local,
// stuff for renegotiation.
mNegotiatedTrackPairs = trackPairs;
// Mark transports that weren't accepted as closed
for (auto i = mTransports.begin(); i != mTransports.end(); ++i) {
if ((*i)->mState != JsepTransport::kJsepTransportAccepted) {
(*i)->mState = JsepTransport::kJsepTransportClosed;
// Mark all assigned local tracks as negotiated
if (NS_SUCCEEDED(rv)) {
for (auto i = mLocalTracks.begin(); i != mLocalTracks.end(); ++i) {
if (i->mAssignedMLine.isSome()) {
i->mNegotiated = true;
}
}
}
mGeneratedLocalDescription.reset();
return NS_OK;
}
@ -1591,6 +1613,7 @@ JsepSessionImpl::MakeNegotiatedTrackPair(const SdpMediaSection& remote,
size_t transportLevel,
JsepTrackPair* trackPairOut)
{
MOZ_ASSERT(transport->mComponents);
const SdpMediaSection& answer = mIsOfferer ? remote : local;
bool sending;
@ -1674,20 +1697,11 @@ JsepSessionImpl::MakeNegotiatedTrackPair(const SdpMediaSection& remote,
trackPairOut->mRtpTransport = transport;
const SdpAttributeList& remoteAttrs(remote.GetAttributeList());
const SdpAttributeList& localAttrs(local.GetAttributeList());
if (HasRtcp(local.GetProtocol())) {
if (transport->mComponents == 2) {
// RTCP MUX or not.
// TODO(bug 1095743): verify that the PTs are consistent with mux.
if (remoteAttrs.HasAttribute(SdpAttribute::kRtcpMuxAttribute) &&
localAttrs.HasAttribute(SdpAttribute::kRtcpMuxAttribute)) {
trackPairOut->mRtcpTransport = nullptr; // We agree on mux.
MOZ_MTLOG(ML_DEBUG, "RTCP-MUX is on");
} else {
MOZ_MTLOG(ML_DEBUG, "RTCP-MUX is off");
trackPairOut->mRtcpTransport = transport;
}
MOZ_MTLOG(ML_DEBUG, "RTCP-MUX is off");
trackPairOut->mRtcpTransport = transport;
}
return NS_OK;
@ -1765,36 +1779,34 @@ JsepSessionImpl::NegotiateTrack(const SdpMediaSection& remoteMsection,
return NS_OK;
}
nsresult
JsepSessionImpl::CreateTransport(const SdpMediaSection& msection,
RefPtr<JsepTransport>* transport)
void
JsepSessionImpl::UpdateTransport(const SdpMediaSection& msection,
JsepTransport* transport)
{
size_t components;
if (HasRtcp(msection.GetProtocol())) {
components = 2;
} else {
components = 1;
if (MsectionIsDisabled(msection)) {
transport->Close();
return;
}
if (HasRtcp(msection.GetProtocol())) {
transport->mComponents = 2;
} else {
transport->mComponents = 1;
}
std::string id;
if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
id = msection.GetAttributeList().GetMid();
transport->mTransportId = msection.GetAttributeList().GetMid();
} else {
std::ostringstream os;
os << "level_" << msection.GetLevel() << "(no mid)";
id = os.str();
transport->mTransportId = os.str();
}
*transport = new JsepTransport(id, components);
return NS_OK;
}
nsresult
JsepSessionImpl::SetupTransport(const SdpAttributeList& remote,
const SdpAttributeList& answer,
const RefPtr<JsepTransport>& transport)
JsepSessionImpl::FinalizeTransport(const SdpAttributeList& remote,
const SdpAttributeList& answer,
const RefPtr<JsepTransport>& transport)
{
UniquePtr<JsepIceTransport> ice = MakeUnique<JsepIceTransport>();
@ -1842,8 +1854,6 @@ JsepSessionImpl::SetupTransport(const SdpAttributeList& remote,
transport->mComponents = 1;
}
transport->mState = JsepTransport::kJsepTransportAccepted;
return NS_OK;
}
@ -2024,7 +2034,7 @@ JsepSessionImpl::SetRemoteDescriptionOffer(UniquePtr<Sdp> offer)
// TODO(bug 1095780): Note that we create remote tracks even when
// They contain only codecs we can't negotiate or other craziness.
nsresult rv = SetRemoteTracksFromDescription(*offer);
nsresult rv = SetRemoteTracksFromDescription(offer.get());
NS_ENSURE_SUCCESS(rv, rv);
mPendingRemoteDescription = Move(offer);
@ -2048,7 +2058,7 @@ JsepSessionImpl::SetRemoteDescriptionAnswer(JsepSdpType type,
// TODO(bug 1095780): Note that this creates remote tracks even if
// we offered sendonly and other side offered sendrecv or recvonly.
rv = SetRemoteTracksFromDescription(*mPendingRemoteDescription);
rv = SetRemoteTracksFromDescription(mPendingRemoteDescription.get());
NS_ENSURE_SUCCESS(rv, rv);
rv = HandleNegotiatedSession(mPendingLocalDescription,
@ -2063,51 +2073,54 @@ JsepSessionImpl::SetRemoteDescriptionAnswer(JsepSdpType type,
}
nsresult
JsepSessionImpl::SetRemoteTracksFromDescription(const Sdp& remoteDescription)
JsepSessionImpl::SetRemoteTracksFromDescription(const Sdp* remoteDescription)
{
// Unassign all remote tracks
for (auto i = mRemoteTracks.begin(); i != mRemoteTracks.end(); ++i) {
i->mAssignedMLine.reset();
}
size_t numMlines = remoteDescription.GetMediaSectionCount();
nsresult rv;
// This will not exist if we're rolling back the first remote description
if (remoteDescription) {
size_t numMlines = remoteDescription->GetMediaSectionCount();
nsresult rv;
// Iterate over the sdp, re-assigning or creating remote tracks as we go
for (size_t i = 0; i < numMlines; ++i) {
const SdpMediaSection& msection = remoteDescription.GetMediaSection(i);
// Iterate over the sdp, re-assigning or creating remote tracks as we go
for (size_t i = 0; i < numMlines; ++i) {
const SdpMediaSection& msection = remoteDescription->GetMediaSection(i);
if (MsectionIsDisabled(msection) || !msection.IsSending()) {
continue;
}
if (MsectionIsDisabled(msection) || !msection.IsSending()) {
continue;
}
std::vector<JsepReceivingTrack>::iterator track;
std::vector<JsepReceivingTrack>::iterator track;
if (msection.GetMediaType() == SdpMediaSection::kApplication) {
// Datachannel doesn't have msid, just search by type
track = FindUnassignedTrackByType(mRemoteTracks,
msection.GetMediaType());
} else {
std::string streamId;
std::string trackId;
rv = GetRemoteIds(remoteDescription, msection, &streamId, &trackId);
NS_ENSURE_SUCCESS(rv, rv);
if (msection.GetMediaType() == SdpMediaSection::kApplication) {
// Datachannel doesn't have msid, just search by type
track = FindUnassignedTrackByType(mRemoteTracks,
msection.GetMediaType());
} else {
std::string streamId;
std::string trackId;
rv = GetRemoteIds(*remoteDescription, msection, &streamId, &trackId);
NS_ENSURE_SUCCESS(rv, rv);
track = FindTrackByIds(mRemoteTracks, streamId, trackId);
}
track = FindTrackByIds(mRemoteTracks, streamId, trackId);
}
if (track == mRemoteTracks.end()) {
RefPtr<JsepTrack> track;
rv = CreateReceivingTrack(i, remoteDescription, msection, &track);
NS_ENSURE_SUCCESS(rv, rv);
if (track == mRemoteTracks.end()) {
RefPtr<JsepTrack> track;
rv = CreateReceivingTrack(i, *remoteDescription, msection, &track);
NS_ENSURE_SUCCESS(rv, rv);
JsepReceivingTrack rtrack;
rtrack.mTrack = track;
rtrack.mAssignedMLine = Some(i);
mRemoteTracks.push_back(rtrack);
mRemoteTracksAdded.push_back(rtrack);
} else {
track->mAssignedMLine = Some(i);
JsepReceivingTrack rtrack;
rtrack.mTrack = track;
rtrack.mAssignedMLine = Some(i);
mRemoteTracks.push_back(rtrack);
mRemoteTracksAdded.push_back(rtrack);
} else {
track->mAssignedMLine = Some(i);
}
}
}
@ -3049,5 +3062,4 @@ JsepSessionImpl::AllLocalTracksAreAssigned() const
return true;
}
} // namespace mozilla

View File

@ -164,7 +164,7 @@ private:
struct JsepSendingTrack {
RefPtr<JsepTrack> mTrack;
Maybe<size_t> mAssignedMLine;
bool mSetInLocalDescription;
bool mNegotiated;
};
struct JsepReceivingTrack {
@ -204,7 +204,7 @@ private:
nsresult ValidateLocalDescription(const Sdp& description);
nsresult ValidateRemoteDescription(const Sdp& description);
nsresult ValidateAnswer(const Sdp& offer, const Sdp& answer);
nsresult SetRemoteTracksFromDescription(const Sdp& remoteDescription);
nsresult SetRemoteTracksFromDescription(const Sdp* remoteDescription);
// Non-const because we use our Uuid generator
nsresult CreateReceivingTrack(size_t mline,
const Sdp& sdp,
@ -240,7 +240,7 @@ private:
const Sdp& oldAnswer,
Sdp* newSdp);
void SetupBundle(Sdp* sdp) const;
nsresult SetupTransportAttributes(Sdp* sdp);
nsresult FinalizeTransportAttributes(Sdp* sdp);
void SetupMsidSemantic(const std::vector<std::string>& msids, Sdp* sdp) const;
nsresult GetIdsFromMsid(const Sdp& sdp,
const SdpMediaSection& msection,
@ -280,12 +280,12 @@ private:
JsepTrack::Direction,
RefPtr<JsepTrack>* track);
nsresult CreateTransport(const SdpMediaSection& msection,
RefPtr<JsepTransport>* transport);
void UpdateTransport(const SdpMediaSection& msection,
JsepTransport* transport);
nsresult SetupTransport(const SdpAttributeList& remote,
const SdpAttributeList& answer,
const RefPtr<JsepTransport>& transport);
nsresult FinalizeTransport(const SdpAttributeList& remote,
const SdpAttributeList& answer,
const RefPtr<JsepTransport>& transport);
nsresult AddCandidateToSdp(Sdp* sdp,
const std::string& candidate,
@ -325,6 +325,8 @@ private:
std::vector<JsepReceivingTrack> mRemoteTracksAdded;
std::vector<JsepReceivingTrack> mRemoteTracksRemoved;
std::vector<RefPtr<JsepTransport> > mTransports;
// So we can rollback
std::vector<RefPtr<JsepTransport> > mOldTransports;
std::vector<JsepTrackPair> mNegotiatedTrackPairs;
bool mIsOfferer;

View File

@ -82,23 +82,22 @@ private:
class JsepTransport
{
public:
JsepTransport(const std::string& id, size_t components)
: mTransportId(id), mState(kJsepTransportOffered), mComponents(components)
JsepTransport()
: mComponents(0)
{
}
enum State {
kJsepTransportOffered,
kJsepTransportAccepted,
kJsepTransportClosed
};
void Close()
{
mComponents = 0;
mTransportId.clear();
mIce.reset();
mDtls.reset();
}
// Unique identifier for this transport within this call. Group?
std::string mTransportId;
// State.
State mState;
// ICE stuff.
UniquePtr<JsepIceTransport> mIce;
UniquePtr<JsepDtlsTransport> mDtls;

View File

@ -1491,6 +1491,9 @@ PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
case IPeerConnection::kActionPRAnswer:
sdpType = mozilla::kJsepSdpPranswer;
break;
case IPeerConnection::kActionRollback:
sdpType = mozilla::kJsepSdpRollback;
break;
default:
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
@ -1519,7 +1522,7 @@ PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
pco->OnSetLocalDescriptionSuccess(rv);
}
UpdateSignalingState();
UpdateSignalingState(sdpType == mozilla::kJsepSdpRollback);
return NS_OK;
}
@ -1587,11 +1590,14 @@ PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
case IPeerConnection::kActionPRAnswer:
sdpType = mozilla::kJsepSdpPranswer;
break;
case IPeerConnection::kActionRollback:
sdpType = mozilla::kJsepSdpRollback;
break;
default:
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
nsresult nrv = mJsepSession->SetRemoteDescription(sdpType,
mRemoteRequestedSDP);
if (NS_FAILED(nrv)) {
@ -1725,7 +1731,7 @@ PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
#endif
}
UpdateSignalingState();
UpdateSignalingState(sdpType == mozilla::kJsepSdpRollback);
return NS_OK;
}
@ -2409,7 +2415,8 @@ PeerConnectionImpl::destructorSafeDestroyNSSReference()
#endif
void
PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState)
PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState,
bool rollback)
{
PC_AUTO_ENTER_API_CALL_NO_CHECK();
if (mSignalingState == aSignalingState ||
@ -2417,33 +2424,35 @@ PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState)
return;
}
bool restartGathering =
aSignalingState == PCImplSignalingState::SignalingHaveLocalOffer ||
(aSignalingState == PCImplSignalingState::SignalingStable &&
mSignalingState == PCImplSignalingState::SignalingHaveRemoteOffer);
if (aSignalingState == PCImplSignalingState::SignalingHaveLocalOffer ||
(aSignalingState == PCImplSignalingState::SignalingStable &&
mSignalingState == PCImplSignalingState::SignalingHaveRemoteOffer &&
!rollback)) {
mMedia->EnsureTransports(*mJsepSession);
}
mSignalingState = aSignalingState;
if (mSignalingState == PCImplSignalingState::SignalingHaveLocalOffer ||
mSignalingState == PCImplSignalingState::SignalingStable) {
mMedia->UpdateTransports(*mJsepSession, restartGathering);
}
bool fireNegotiationNeeded = false;
if (mSignalingState == PCImplSignalingState::SignalingStable) {
mMedia->UpdateMediaPipelines(*mJsepSession);
InitializeDataChannel();
mMedia->StartIceChecks(*mJsepSession);
mShouldSuppressNegotiationNeeded = false;
if (!mJsepSession->AllLocalTracksAreAssigned()) {
CSFLogInfo(logTag, "Not all local tracks were assigned to an "
"m-section, either because the offerer did not offer"
" to receive enough tracks, or because tracks were "
"added after CreateOffer/Answer, but before "
"offer/answer completed. This requires "
"renegotiation.");
fireNegotiationNeeded = true;
// If we're rolling back a local offer, we might need to remove some
// transports, but nothing further needs to be done.
mMedia->ActivateOrRemoveTransports(*mJsepSession);
if (!rollback) {
mMedia->UpdateMediaPipelines(*mJsepSession);
InitializeDataChannel();
mMedia->StartIceChecks(*mJsepSession);
mShouldSuppressNegotiationNeeded = false;
if (!mJsepSession->AllLocalTracksAreAssigned()) {
CSFLogInfo(logTag, "Not all local tracks were assigned to an "
"m-section, either because the offerer did not offer"
" to receive enough tracks, or because tracks were "
"added after CreateOffer/Answer, but before "
"offer/answer completed. This requires "
"renegotiation.");
fireNegotiationNeeded = true;
}
}
} else {
mShouldSuppressNegotiationNeeded = true;
@ -2466,7 +2475,7 @@ PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState)
}
void
PeerConnectionImpl::UpdateSignalingState() {
PeerConnectionImpl::UpdateSignalingState(bool rollback) {
mozilla::JsepSignalingState state =
mJsepSession->GetState();
@ -2495,7 +2504,7 @@ PeerConnectionImpl::UpdateSignalingState() {
MOZ_CRASH();
}
SetSignalingState_m(newState);
SetSignalingState_m(newState, rollback);
}
bool
@ -3287,6 +3296,10 @@ PeerConnectionImpl::IceStreamReady(NrIceMediaStream *aStream)
//Telemetry for when calls start
void
PeerConnectionImpl::startCallTelem() {
if (!mStartTime.IsNull()) {
return;
}
// Start time for calls
mStartTime = TimeStamp::Now();

View File

@ -572,10 +572,11 @@ public:
const std::vector<std::string> &GetSdpParseErrors();
// Sets the RTC Signaling State
void SetSignalingState_m(mozilla::dom::PCImplSignalingState aSignalingState);
void SetSignalingState_m(mozilla::dom::PCImplSignalingState aSignalingState,
bool rollback = false);
// Updates the RTC signaling state based on the JsepSession state
void UpdateSignalingState();
void UpdateSignalingState(bool rollback = false);
bool IsClosed() const;
// called when DTLS connects; we only need this once

View File

@ -348,10 +348,55 @@ nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_serv
}
void
PeerConnectionMedia::UpdateTransports(const JsepSession& session,
bool restartGathering) {
PeerConnectionMedia::EnsureTransports(const JsepSession& aSession)
{
auto transports = aSession.GetTransports();
for (size_t i = 0; i < transports.size(); ++i) {
RefPtr<JsepTransport> transport = transports[i];
RUN_ON_THREAD(
GetSTSThread(),
WrapRunnable(RefPtr<PeerConnectionMedia>(this),
&PeerConnectionMedia::EnsureTransport_s,
i,
transport->mComponents),
NS_DISPATCH_NORMAL);
}
auto transports = session.GetTransports();
GatherIfReady();
}
void
PeerConnectionMedia::EnsureTransport_s(size_t aLevel, size_t aComponentCount)
{
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aLevel));
if (!stream) {
CSFLogDebug(logTag, "%s: Creating ICE media stream=%u components=%u",
mParentHandle.c_str(),
static_cast<unsigned>(aLevel),
static_cast<unsigned>(aComponentCount));
std::ostringstream os;
os << mParentName << " aLevel=" << aLevel;
RefPtr<NrIceMediaStream> stream = mIceCtx->CreateStream(os.str().c_str(),
aComponentCount);
if (!stream) {
CSFLogError(logTag, "Failed to create ICE stream.");
return;
}
stream->SetLevel(aLevel);
stream->SignalReady.connect(this, &PeerConnectionMedia::IceStreamReady_s);
stream->SignalCandidate.connect(this,
&PeerConnectionMedia::OnCandidateFound_s);
mIceCtx->SetStream(aLevel, stream);
}
}
void
PeerConnectionMedia::ActivateOrRemoveTransports(const JsepSession& aSession)
{
auto transports = aSession.GetTransports();
for (size_t i = 0; i < transports.size(); ++i) {
RefPtr<JsepTransport> transport = transports[i];
@ -359,31 +404,93 @@ PeerConnectionMedia::UpdateTransports(const JsepSession& session,
std::string pwd;
std::vector<std::string> candidates;
bool hasAttrs = false;
if (transport->mIce) {
CSFLogDebug(logTag, "Transport %u is active",
static_cast<unsigned>(i));
hasAttrs = true;
if (transport->mComponents) {
MOZ_ASSERT(transport->mIce);
CSFLogDebug(logTag, "Transport %u is active", static_cast<unsigned>(i));
ufrag = transport->mIce->GetUfrag();
pwd = transport->mIce->GetPassword();
candidates = transport->mIce->GetCandidates();
} else {
CSFLogDebug(logTag, "Transport %u is disabled", static_cast<unsigned>(i));
// Make sure the MediaPipelineFactory doesn't try to use these.
RemoveTransportFlow(i, false);
RemoveTransportFlow(i, true);
}
// Update the transport.
RUN_ON_THREAD(GetSTSThread(),
WrapRunnable(RefPtr<PeerConnectionMedia>(this),
&PeerConnectionMedia::UpdateIceMediaStream_s,
i,
transport->mComponents,
hasAttrs,
ufrag,
pwd,
candidates),
NS_DISPATCH_NORMAL);
RUN_ON_THREAD(
GetSTSThread(),
WrapRunnable(RefPtr<PeerConnectionMedia>(this),
&PeerConnectionMedia::ActivateOrRemoveTransport_s,
i,
transport->mComponents,
ufrag,
pwd,
candidates),
NS_DISPATCH_NORMAL);
}
if (restartGathering) {
GatherIfReady();
// We can have more streams than m-lines due to rollback.
RUN_ON_THREAD(
GetSTSThread(),
WrapRunnable(RefPtr<PeerConnectionMedia>(this),
&PeerConnectionMedia::RemoveTransportsAtOrAfter_s,
transports.size()),
NS_DISPATCH_NORMAL);
}
void
PeerConnectionMedia::ActivateOrRemoveTransport_s(
size_t aMLine,
size_t aComponentCount,
const std::string& aUfrag,
const std::string& aPassword,
const std::vector<std::string>& aCandidateList) {
if (!aComponentCount) {
CSFLogDebug(logTag, "%s: Removing ICE media stream=%u",
mParentHandle.c_str(),
static_cast<unsigned>(aMLine));
mIceCtx->SetStream(aMLine, nullptr);
return;
}
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aMLine));
if (!stream) {
MOZ_ASSERT(false);
return;
}
if (!stream->HasParsedAttributes()) {
CSFLogDebug(logTag, "%s: Activating ICE media stream=%u components=%u",
mParentHandle.c_str(),
static_cast<unsigned>(aMLine),
static_cast<unsigned>(aComponentCount));
std::vector<std::string> attrs;
for (auto i = aCandidateList.begin(); i != aCandidateList.end(); ++i) {
attrs.push_back("candidate:" + *i);
}
attrs.push_back("ice-ufrag:" + aUfrag);
attrs.push_back("ice-pwd:" + aPassword);
nsresult rv = stream->ParseAttributes(attrs);
if (NS_FAILED(rv)) {
CSFLogError(logTag, "Couldn't parse ICE attributes, rv=%u",
static_cast<unsigned>(rv));
}
for (size_t c = aComponentCount; c < stream->components(); ++c) {
// components are 1-indexed
stream->DisableComponent(c + 1);
}
}
}
void
PeerConnectionMedia::RemoveTransportsAtOrAfter_s(size_t aMLine)
{
for (size_t i = aMLine; i < mIceCtx->GetStreamCount(); ++i) {
mIceCtx->SetStream(i, nullptr);
}
}
@ -419,36 +526,16 @@ nsresult PeerConnectionMedia::UpdateMediaPipelines(
}
void
PeerConnectionMedia::StartIceChecks(const JsepSession& session) {
std::vector<size_t> numComponentsByLevel;
auto transports = session.GetTransports();
for (size_t i = 0; i < transports.size(); ++i) {
RefPtr<JsepTransport> transport = transports[i];
if (transport->mState == JsepTransport::kJsepTransportClosed) {
CSFLogDebug(logTag, "Transport %s is disabled",
transport->mTransportId.c_str());
numComponentsByLevel.push_back(0);
// Make sure the MediaPipelineFactory doesn't try to use these.
RemoveTransportFlow(i, false);
RemoveTransportFlow(i, true);
} else {
CSFLogDebug(logTag, "Transport %s has %u components",
transport->mTransportId.c_str(),
static_cast<unsigned>(transport->mComponents));
numComponentsByLevel.push_back(transport->mComponents);
}
}
PeerConnectionMedia::StartIceChecks(const JsepSession& aSession)
{
nsCOMPtr<nsIRunnable> runnable(
WrapRunnable(
RefPtr<PeerConnectionMedia>(this),
&PeerConnectionMedia::StartIceChecks_s,
session.IsIceControlling(),
session.RemoteIsIceLite(),
aSession.IsIceControlling(),
aSession.RemoteIsIceLite(),
// Copy, just in case API changes to return a ref
std::vector<std::string>(session.GetIceOptions()),
numComponentsByLevel));
std::vector<std::string>(aSession.GetIceOptions())));
PerformOrEnqueueIceCtxOperation(runnable);
}
@ -457,8 +544,7 @@ void
PeerConnectionMedia::StartIceChecks_s(
bool aIsControlling,
bool aIsIceLite,
const std::vector<std::string>& aIceOptionsList,
const std::vector<size_t>& aComponentCountByLevel) {
const std::vector<std::string>& aIceOptionsList) {
CSFLogDebug(logTag, "Starting ICE Checking");
@ -483,24 +569,6 @@ PeerConnectionMedia::StartIceChecks_s(
NrIceCtx::ICE_CONTROLLING :
NrIceCtx::ICE_CONTROLLED);
for (size_t i = 0; i < aComponentCountByLevel.size(); ++i) {
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(i));
if (!stream) {
continue;
}
if (!aComponentCountByLevel[i]) {
// Inactive stream. Remove.
mIceCtx->SetStream(i, nullptr);
continue;
}
for (size_t c = aComponentCountByLevel[i]; c < stream->components(); ++c) {
// components are 1-indexed
stream->DisableComponent(c + 1);
}
}
mIceCtx->StartChecks();
}
@ -579,56 +647,19 @@ PeerConnectionMedia::EnsureIceGathering_s() {
if (mProxyServer) {
mIceCtx->SetProxyServer(*mProxyServer);
}
mIceCtx->StartGathering();
}
void
PeerConnectionMedia::UpdateIceMediaStream_s(size_t aMLine,
size_t aComponentCount,
bool aHasAttrs,
const std::string& aUfrag,
const std::string& aPassword,
const std::vector<std::string>&
aCandidateList) {
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aMLine));
if (!stream) {
CSFLogDebug(logTag, "%s: Creating ICE media stream=%u components=%u",
mParentHandle.c_str(),
static_cast<unsigned>(aMLine),
static_cast<unsigned>(aComponentCount));
std::ostringstream os;
os << mParentName << " level=" << aMLine;
stream = mIceCtx->CreateStream(os.str().c_str(),
aComponentCount);
if (!stream) {
CSFLogError(logTag, "Failed to create ICE stream.");
// Start gathering, but only if there are streams
for (size_t i = 0; i < mIceCtx->GetStreamCount(); ++i) {
if (mIceCtx->GetStream(i)) {
mIceCtx->StartGathering();
return;
}
stream->SetLevel(aMLine);
stream->SignalReady.connect(this, &PeerConnectionMedia::IceStreamReady_s);
stream->SignalCandidate.connect(this,
&PeerConnectionMedia::OnCandidateFound_s);
mIceCtx->SetStream(aMLine, stream);
}
if (aHasAttrs && !stream->HasParsedAttributes()) {
std::vector<std::string> attrs;
for (auto i = aCandidateList.begin(); i != aCandidateList.end(); ++i) {
attrs.push_back("candidate:" + *i);
}
attrs.push_back("ice-ufrag:" + aUfrag);
attrs.push_back("ice-pwd:" + aPassword);
nsresult rv = stream->ParseAttributes(attrs);
if (NS_FAILED(rv)) {
CSFLogError(logTag, "Couldn't parse ICE attributes, rv=%u",
static_cast<unsigned>(rv));
}
}
// If there are no streams, we're probably in a situation where we've rolled
// back while still waiting for our proxy configuration to come back. Make
// sure content knows that the rollback has stuck wrt gathering.
IceGatheringStateChange_s(mIceCtx.get(), NrIceCtx::ICE_CTX_GATHER_COMPLETE);
}
nsresult

View File

@ -245,8 +245,12 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
return mIceCtx->GetStreamCount();
}
// Create and modify transports in response to negotiation events.
void UpdateTransports(const JsepSession& session, bool restartGathering);
// Ensure ICE transports exist that we might need when offer/answer concludes
void EnsureTransports(const JsepSession& aSession);
// Activate or remove ICE transports at the conclusion of offer/answer,
// or when rollback occurs.
void ActivateOrRemoveTransports(const JsepSession& aSession);
// Start ICE checks.
void StartIceChecks(const JsepSession& session);
@ -421,19 +425,22 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
void SelfDestruct_m();
// Manage ICE transports.
void UpdateIceMediaStream_s(size_t aMLine, size_t aComponentCount,
bool aHasAttrs,
const std::string& aUfrag,
const std::string& aPassword,
const std::vector<std::string>& aCandidateList);
void EnsureTransport_s(size_t aLevel, size_t aComponentCount);
void ActivateOrRemoveTransport_s(
size_t aMLine,
size_t aComponentCount,
const std::string& aUfrag,
const std::string& aPassword,
const std::vector<std::string>& aCandidateList);
void RemoveTransportsAtOrAfter_s(size_t aMLine);
void GatherIfReady();
void FlushIceCtxOperationQueueIfReady();
void PerformOrEnqueueIceCtxOperation(nsIRunnable* runnable);
void EnsureIceGathering_s();
void StartIceChecks_s(bool aIsControlling,
bool aIsIceLite,
const std::vector<std::string>& aIceOptionsList,
const std::vector<size_t>& aComponentCountByLevel);
const std::vector<std::string>& aIceOptionsList);
// Process a trickle ICE candidate.
void AddIceCandidate_s(const std::string& aCandidate, const std::string& aMid,

View File

@ -1932,10 +1932,8 @@ TEST_P(JsepSessionTest, RenegotiationOffererDisablesBundleTransport)
ASSERT_LE(1U, mSessionOff.GetTransports().size());
ASSERT_LE(1U, mSessionAns.GetTransports().size());
ASSERT_EQ(JsepTransport::kJsepTransportClosed,
mSessionOff.GetTransports()[0]->mState);
ASSERT_EQ(JsepTransport::kJsepTransportClosed,
mSessionAns.GetTransports()[0]->mState);
ASSERT_EQ(0U, mSessionOff.GetTransports()[0]->mComponents);
ASSERT_EQ(0U, mSessionAns.GetTransports()[0]->mComponents);
}
TEST_P(JsepSessionTest, RenegotiationAnswererDisablesBundleTransport)
@ -3209,6 +3207,98 @@ TEST_F(JsepSessionTest, LowDynamicPayloadType)
ASSERT_EQ("12", codec->mDefaultPt);
}
TEST_P(JsepSessionTest, TestGlareRollback)
{
AddTracks(mSessionOff);
AddTracks(mSessionAns);
JsepOfferOptions options;
std::string offer;
ASSERT_EQ(NS_OK, mSessionAns.CreateOffer(options, &offer));
ASSERT_EQ(NS_OK,
mSessionAns.SetLocalDescription(kJsepSdpOffer, offer));
ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionAns.GetState());
ASSERT_EQ(NS_OK, mSessionOff.CreateOffer(options, &offer));
ASSERT_EQ(NS_OK,
mSessionOff.SetLocalDescription(kJsepSdpOffer, offer));
ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff.GetState());
ASSERT_EQ(NS_ERROR_UNEXPECTED,
mSessionAns.SetRemoteDescription(kJsepSdpOffer, offer));
ASSERT_EQ(NS_OK,
mSessionAns.SetLocalDescription(kJsepSdpRollback, ""));
ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
SetRemoteOffer(offer);
std::string answer = CreateAnswer();
SetLocalAnswer(answer);
SetRemoteAnswer(answer);
}
TEST_P(JsepSessionTest, TestRejectOfferRollback)
{
AddTracks(mSessionOff);
AddTracks(mSessionAns);
std::string offer = CreateOffer();
SetLocalOffer(offer);
SetRemoteOffer(offer);
ASSERT_EQ(NS_OK,
mSessionAns.SetRemoteDescription(kJsepSdpRollback, ""));
ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
ASSERT_EQ(types.size(), mSessionAns.GetRemoteTracksRemoved().size());
ASSERT_EQ(NS_OK,
mSessionOff.SetLocalDescription(kJsepSdpRollback, ""));
ASSERT_EQ(kJsepStateStable, mSessionOff.GetState());
OfferAnswer();
}
TEST_P(JsepSessionTest, TestInvalidRollback)
{
AddTracks(mSessionOff);
AddTracks(mSessionAns);
ASSERT_EQ(NS_ERROR_UNEXPECTED,
mSessionOff.SetLocalDescription(kJsepSdpRollback, ""));
ASSERT_EQ(NS_ERROR_UNEXPECTED,
mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
std::string offer = CreateOffer();
ASSERT_EQ(NS_ERROR_UNEXPECTED,
mSessionOff.SetLocalDescription(kJsepSdpRollback, ""));
ASSERT_EQ(NS_ERROR_UNEXPECTED,
mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
SetLocalOffer(offer);
ASSERT_EQ(NS_ERROR_UNEXPECTED,
mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
SetRemoteOffer(offer);
ASSERT_EQ(NS_ERROR_UNEXPECTED,
mSessionAns.SetLocalDescription(kJsepSdpRollback, ""));
std::string answer = CreateAnswer();
ASSERT_EQ(NS_ERROR_UNEXPECTED,
mSessionAns.SetLocalDescription(kJsepSdpRollback, ""));
SetLocalAnswer(answer);
ASSERT_EQ(NS_ERROR_UNEXPECTED,
mSessionAns.SetLocalDescription(kJsepSdpRollback, ""));
ASSERT_EQ(NS_ERROR_UNEXPECTED,
mSessionAns.SetRemoteDescription(kJsepSdpRollback, ""));
SetRemoteAnswer(answer);
ASSERT_EQ(NS_ERROR_UNEXPECTED,
mSessionOff.SetLocalDescription(kJsepSdpRollback, ""));
ASSERT_EQ(NS_ERROR_UNEXPECTED,
mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
}
} // namespace mozilla
int