Commit 0b1c0786 authored by Harald Alvestrand's avatar Harald Alvestrand Committed by Commit Bot

Fix senders and receivers when transceivers are removed.

This also adds a failing test for the outstanding issue with transceiver
updates.

Bug: chromium:980879


Checkpoint

Change-Id: Ib7a87042bd5a8dbf14d484731da4a1ffc5d563d5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2437402
Commit-Queue: Harald Alvestrand <hta@chromium.org>
Reviewed-by: default avatarHenrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812012}
parent a9a68b8e
...@@ -3146,6 +3146,17 @@ void RTCPeerConnection::DidModifyTransceivers( ...@@ -3146,6 +3146,17 @@ void RTCPeerConnection::DidModifyTransceivers(
mute_tracks.push_back(transceiver->receiver()->track()); mute_tracks.push_back(transceiver->receiver()->track());
} }
} }
if (sdp_semantics_ == webrtc::SdpSemantics::kUnifiedPlan) {
// Update the rtp_senders_ and rtp_receivers_ members to only contain
// senders and receivers that are in the current set of transceivers.
rtp_senders_.clear();
rtp_receivers_.clear();
for (auto& transceiver : transceivers_) {
rtp_senders_.push_back(transceiver->sender());
rtp_receivers_.push_back(transceiver->receiver());
}
}
MediaStreamVector current_streams = getRemoteStreams(); MediaStreamVector current_streams = getRemoteStreams();
// Modify and fire "pc.onsignalingchange" synchronously. // Modify and fire "pc.onsignalingchange" synchronously.
......
This is a testharness.js-based test. This is a testharness.js-based test.
PASS A transceiver added and stopped before the initial offer generation should not trigger an offer m-section generation PASS A transceiver added and stopped before the initial offer generation should not trigger an offer m-section generation
PASS During renegotiation, adding and stopping a transceiver should not trigger a renegotiated offer m-section generation PASS During renegotiation, adding and stopping a transceiver should not trigger a renegotiated offer m-section generation
FAIL A stopped sendonly transceiver should generate a sendonly m-section in the offer assert_true: The audio m-section should be sendonly expected true got false PASS A stopped sendonly transceiver should generate an inactive m-section in the offer
PASS A stopped inactive transceiver should generate an inactive m-section in the offer PASS A stopped inactive transceiver should generate an inactive m-section in the offer
PASS If a track is stopped locally, setting a locally generated answer should still work PASS If a transceiver is stopped locally, setting a locally generated answer should still work
PASS If a track is stopped remotely, setting a locally generated answer should still work PASS If a transceiver is stopped remotely, setting a locally generated answer should still work
PASS If a transceiver is stopped, transceivers, senders and receivers should disappear after offer/answer
FAIL If a transceiver is stopped, transceivers should end up in state stopped assert_equals: expected "recvonly" but got "stopped"
Harness: the test ran to completion. Harness: the test ran to completion.
...@@ -59,8 +59,8 @@ promise_test(async (t)=> { ...@@ -59,8 +59,8 @@ promise_test(async (t)=> {
const offer = await pc1.createOffer(); const offer = await pc1.createOffer();
assert_true(offer.sdp.includes("a=sendonly"), "The audio m-section should be sendonly"); assert_true(offer.sdp.includes("a=inactive"), "The audio m-section should be inactive");
}, "A stopped sendonly transceiver should generate a sendonly m-section in the offer"); }, "A stopped sendonly transceiver should generate an inactive m-section in the offer");
promise_test(async (t)=> { promise_test(async (t)=> {
const pc1 = new RTCPeerConnection(); const pc1 = new RTCPeerConnection();
...@@ -90,7 +90,7 @@ promise_test(async (t) => { ...@@ -90,7 +90,7 @@ promise_test(async (t) => {
pc1.getTransceivers()[0].stop(); pc1.getTransceivers()[0].stop();
await exchangeOfferAnswer(pc1, pc2); await exchangeOfferAnswer(pc1, pc2);
await pc1.setLocalDescription(await pc1.createOffer()); await pc1.setLocalDescription(await pc1.createOffer());
}, 'If a track is stopped locally, setting a locally generated answer should still work'); }, 'If a transceiver is stopped locally, setting a locally generated answer should still work');
promise_test(async (t) => { promise_test(async (t) => {
const pc1 = new RTCPeerConnection(); const pc1 = new RTCPeerConnection();
...@@ -102,6 +102,42 @@ promise_test(async (t) => { ...@@ -102,6 +102,42 @@ promise_test(async (t) => {
pc2.getTransceivers()[0].stop(); pc2.getTransceivers()[0].stop();
await exchangeOfferAnswer(pc2, pc1); await exchangeOfferAnswer(pc2, pc1);
await pc1.setLocalDescription(await pc1.createOffer()); await pc1.setLocalDescription(await pc1.createOffer());
}, 'If a track is stopped remotely, setting a locally generated answer should still work'); }, 'If a transceiver is stopped remotely, setting a locally generated answer should still work');
promise_test(async (t) => {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
pc1.addTransceiver("audio");
await exchangeOfferAnswer(pc1, pc2);
assert_equals(pc1.getTransceivers().length, 1);
assert_equals(pc2.getTransceivers().length, 1);
pc1.getTransceivers()[0].stop();
await exchangeOfferAnswer(pc1, pc2);
assert_equals(pc1.getTransceivers().length, 0);
assert_equals(pc2.getTransceivers().length, 0);
assert_equals(pc1.getSenders().length, 0, 'caller senders');
assert_equals(pc1.getReceivers().length, 0, 'caller receivers');
assert_equals(pc2.getSenders().length, 0, 'callee senders');
assert_equals(pc2.getReceivers().length, 0, 'callee receivers');
}, 'If a transceiver is stopped, transceivers, senders and receivers should disappear after offer/answer');
promise_test(async (t) => {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
pc1.addTransceiver("audio");
await exchangeOfferAnswer(pc1, pc2);
assert_equals(pc1.getTransceivers().length, 1);
assert_equals(pc2.getTransceivers().length, 1);
pc1Transceiver = pc1.getTransceivers()[0];
pc2Transceiver = pc2.getTransceivers()[0];
pc1.getTransceivers()[0].stop();
await exchangeOfferAnswer(pc1, pc2);
assert_equals('stopped', pc1Transceiver.direction);
assert_equals('stopped', pc2Transceiver.direction);
}, 'If a transceiver is stopped, transceivers should end up in state stopped');
</script> </script>
This is a testharness.js-based test. This is a testharness.js-based test.
Harness Error. harness_status.status = 1 , harness_status.message = Unhandled rejection: assert_equals: RTCRtpTransceiver.stop() with closed PC throws InvalidStateError expected "InvalidStateError" but got "Error"
PASS checkAddTransceiverNoTrack PASS checkAddTransceiverNoTrack
PASS checkAddTransceiverWithTrack PASS checkAddTransceiverWithTrack
PASS checkAddTransceiverWithAddTrack PASS checkAddTransceiverWithAddTrack
...@@ -23,7 +24,7 @@ PASS checkRemoveAndReadd ...@@ -23,7 +24,7 @@ PASS checkRemoveAndReadd
PASS checkAddTrackExistingTransceiverThenRemove PASS checkAddTrackExistingTransceiverThenRemove
FAIL checkRemoveTrackNegotiation assert_equals: pc2.setRemoteDescription(offer) should've added 2 tracks to receive stream expected 2 but got 0 FAIL checkRemoveTrackNegotiation assert_equals: pc2.setRemoteDescription(offer) should've added 2 tracks to receive stream expected 2 but got 0
FAIL checkMute assert_true: expected true got false FAIL checkMute assert_true: expected true got false
FAIL checkStop assert_equals: getReceivers does not expose a receiver of a stopped transceiver expected 0 but got 1 PASS checkStop
PASS checkStopAfterCreateOffer PASS checkStopAfterCreateOffer
PASS checkStopAfterSetLocalOffer PASS checkStopAfterSetLocalOffer
PASS checkStopAfterSetRemoteOffer PASS checkStopAfterSetRemoteOffer
...@@ -32,7 +33,7 @@ PASS checkStopAfterSetLocalAnswer ...@@ -32,7 +33,7 @@ PASS checkStopAfterSetLocalAnswer
PASS checkStopAfterClose PASS checkStopAfterClose
PASS checkLocalRollback PASS checkLocalRollback
PASS checkRollbackAndSetRemoteOfferWithDifferentType PASS checkRollbackAndSetRemoteOfferWithDifferentType
FAIL checkRemoteRollback promise_test: Unhandled rejection with value: object "InvalidAccessError: Failed to execute 'addTrack' on 'RTCPeerConnection': A sender already exists for the track." FAIL checkRemoteRollback assert_equals: track event from remote rollback expected 1 but got 0
FAIL checkMsectionReuse assert_equals: expected "[{currentDirection:null,mid:null}]" but got "[{currentDirection:\"inactive\",mid:\"0\"}]" FAIL checkMsectionReuse assert_equals: expected "[{currentDirection:null,mid:null}]" but got "[{currentDirection:\"inactive\",mid:\"0\"}]"
PASS checkStopAfterCreateOfferWithReusedMsection PASS checkStopAfterCreateOfferWithReusedMsection
PASS checkAddIceCandidateToStoppedTransceiver PASS checkAddIceCandidateToStoppedTransceiver
......
...@@ -1235,9 +1235,8 @@ ...@@ -1235,9 +1235,8 @@
stoppedTransceiver.receiver.track.onended = resolve; stoppedTransceiver.receiver.track.onended = resolve;
}); });
stoppedTransceiver.stop(); stoppedTransceiver.stop();
assert_equals(pc1.getReceivers().length, 0, 'getReceivers does not expose a receiver of a stopped transceiver'); assert_equals(pc1.getReceivers().length, 1, 'getReceivers exposes a receiver of a stopped transceiver before negotiation');
assert_equals(pc1.getSenders().length, 0, 'getSenders does not expose a sender of a stopped transceiver'); assert_equals(pc1.getSenders().length, 1, 'getSenders exposes a sender of a stopped transceiver before negotiation');
await onended; await onended;
// The transceiver has [[stopping]] = true, [[stopped]] = false // The transceiver has [[stopping]] = true, [[stopped]] = false
hasPropsAndUniqueMids(pc1.getTransceivers(), hasPropsAndUniqueMids(pc1.getTransceivers(),
...@@ -1273,30 +1272,38 @@ ...@@ -1273,30 +1272,38 @@
offer = await pc1.createOffer(); offer = await pc1.createOffer();
await pc1.setLocalDescription(offer); await pc1.setLocalDescription(offer);
stoppedTransceiver = pc2.getTransceivers()[0]; const stoppedCalleeTransceiver = pc2.getTransceivers()[0];
onended = new Promise(resolve => { onended = new Promise(resolve => {
stoppedTransceiver.receiver.track.onended = resolve; stoppedCalleeTransceiver.receiver.track.onended = resolve;
}); });
await pc2.setRemoteDescription(offer); await pc2.setRemoteDescription(offer);
await onended; await onended;
// pc2's transceiver was stopped remotely.
// pc2's transceiver was stopped remotely, so has // The track ends when setRemeoteDescription(offer) is set.
// [[stopping]] = true, [[stopped]] = true.
hasProps(pc2.getTransceivers(), hasProps(pc2.getTransceivers(),
[ [
{ {
sender: {track: {kind: "audio"}}, sender: {track: {kind: "audio"}},
receiver: {track: {kind: "audio", readyState: "ended"}}, receiver: {track: {kind: "audio", readyState: "ended"}},
mid: null,
currentDirection: "stopped", currentDirection: "stopped",
direction: "stopped" direction: "sendrecv"
} }
]); ]);
// After setLocalDescription(answer), the transceiver has
// [[stopping]] = true, [[stopped]] = true, and is removed from pc2.
const stoppingAnswer = await pc2.createAnswer();
await pc2.setLocalDescription(stoppingAnswer);
assert_equals(pc2.getTransceivers().length, 0);
assert_equals(pc2.getReceivers().length, 0, 'getReceivers does not expose a receiver of a stopped transceiver after negotiation');
assert_equals(pc2.getSenders().length, 0, 'getSenders does not expose a sender of a stopped transceiver after negotiation');
// Shouldn't throw either // Shouldn't throw either
stoppedTransceiver.stop(); stoppedTransceiver.stop();
await pc1.setRemoteDescription(stoppingAnswer);
assert_equals(pc1.getReceivers().length, 0, 'getReceivers does not expose a receiver of a stopped transceiver after negotiation');
assert_equals(pc1.getSenders().length, 0, 'getSenders does not expose a sender of a stopped transceiver after negotiation');
pc1.close(); pc1.close();
pc2.close(); pc2.close();
...@@ -1897,6 +1904,8 @@ ...@@ -1897,6 +1904,8 @@
// After all this SRD/rollback, we should still get the track event // After all this SRD/rollback, we should still get the track event
let trackEvents = await setRemoteDescriptionReturnTrackEvents(pc2, offer); let trackEvents = await setRemoteDescriptionReturnTrackEvents(pc2, offer);
assert_equals(trackEvents.length, 1);
hasProps(trackEvents, hasProps(trackEvents,
[ [
{ {
...@@ -1910,6 +1919,7 @@ ...@@ -1910,6 +1919,7 @@
// Make sure all this rollback hasn't messed up the signaling // Make sure all this rollback hasn't messed up the signaling
trackEvents = await setRemoteDescriptionReturnTrackEvents(pc1, answer); trackEvents = await setRemoteDescriptionReturnTrackEvents(pc1, answer);
assert_equals(trackEvents.length, 1);
hasProps(trackEvents, hasProps(trackEvents,
[ [
{ {
...@@ -1939,6 +1949,8 @@ ...@@ -1939,6 +1949,8 @@
trackEvents = trackEvents =
await setRemoteDescriptionReturnTrackEvents(pc2, {type: "rollback"}); await setRemoteDescriptionReturnTrackEvents(pc2, {type: "rollback"});
assert_equals(trackEvents.length, 1, 'track event from remote rollback');
hasProps(trackEvents, hasProps(trackEvents,
[ [
{ {
......
This is a testharness.js-based test. This is a testharness.js-based test.
FAIL A transceiver added and stopped before the initial offer generation should not trigger an offer m-section generation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." FAIL A transceiver added and stopped before the initial offer generation should not trigger an offer m-section generation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
FAIL During renegotiation, adding and stopping a transceiver should not trigger a renegotiated offer m-section generation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." FAIL During renegotiation, adding and stopping a transceiver should not trigger a renegotiated offer m-section generation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
FAIL A stopped sendonly transceiver should generate a sendonly m-section in the offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." FAIL A stopped sendonly transceiver should generate an inactive m-section in the offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
FAIL A stopped inactive transceiver should generate an inactive m-section in the offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." FAIL A stopped inactive transceiver should generate an inactive m-section in the offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
FAIL If a track is stopped locally, setting a locally generated answer should still work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." FAIL If a transceiver is stopped locally, setting a locally generated answer should still work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
FAIL If a track is stopped remotely, setting a locally generated answer should still work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." FAIL If a transceiver is stopped remotely, setting a locally generated answer should still work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
FAIL If a transceiver is stopped, transceivers, senders and receivers should disappear after offer/answer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
FAIL If a transceiver is stopped, transceivers should end up in state stopped promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
Harness: the test ran to completion. Harness: the test ran to completion.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment