blob: 61b09a97b0cc40f22d507293c1f6536957468698 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>RTCPeerConnection.getReceivers</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<script>
promise_test(function() {
let pc = new RTCPeerConnection();
return createStreams({audio:true}, 1)
.then(function(streams) {
return addRemoteStreamsFromLocalStreams(pc, streams);
})
.then(function() {
verifyStreamAndTrackCounts(pc.getRemoteStreams(), 1, true, false);
verifyRemoteTracksHaveReceivers(pc);
// Make sure object identities are preserved between calls.
assert_array_equals(pc.getReceivers(), pc.getReceivers());
});
}, 'getReceivers() for a single audio track.');
promise_test(function() {
let pc = new RTCPeerConnection();
return createStreams({video:true}, 1)
.then(function(streams) {
return addRemoteStreamsFromLocalStreams(pc, streams);
})
.then(function() {
verifyStreamAndTrackCounts(pc.getRemoteStreams(), 1, false, true);
verifyRemoteTracksHaveReceivers(pc);
// Make sure object identities are preserved between calls.
assert_array_equals(pc.getReceivers(), pc.getReceivers());
});
}, 'getReceivers() for a single video track.');
promise_test(function() {
let pc = new RTCPeerConnection();
return createStreams({audio:true, video:true}, 1)
.then(function(streams) {
return addRemoteStreamsFromLocalStreams(pc, streams);
})
.then(function() {
verifyStreamAndTrackCounts(pc.getRemoteStreams(), 1, true, true);
verifyRemoteTracksHaveReceivers(pc);
// Make sure object identities are preserved between calls.
assert_array_equals(pc.getReceivers(), pc.getReceivers());
});
}, 'getReceivers() for a single stream with an audio and video track.');
promise_test(function() {
let pc = new RTCPeerConnection();
return createStreams({audio:true, video:true}, 2)
.then(function(streams) {
return addRemoteStreamsFromLocalStreams(pc, streams);
})
.then(function() {
verifyStreamAndTrackCounts(pc.getRemoteStreams(), 2, true, true);
verifyRemoteTracksHaveReceivers(pc);
// Make sure object identities are preserved between calls.
assert_array_equals(pc.getReceivers(), pc.getReceivers());
});
}, 'getReceivers() for multiple audio-video streams.');
promise_test(function() {
let pc = new RTCPeerConnection();
let receivers = new Set();
return createStreams({audio:true, video:true}, 2)
.then(function(streams) {
return addRemoteStreamsFromLocalStreams(pc, streams);
})
.then(function() {
verifyStreamAndTrackCounts(pc.getRemoteStreams(), 2, true, true);
verifyRemoteTracksHaveReceivers(pc);
// Make sure object identities are preserved between calls.
assert_array_equals(pc.getReceivers(), pc.getReceivers());
for (let i = 0; i < pc.getReceivers().length; ++i)
receivers.add(pc.getReceivers()[i]);
// 2 receivers per stream, one for audio and one for video
assert_equals(receivers.size, 4);
removeRemoteStreamsFromLocalStreams(pc, [ pc.getLocalStreams()[0] ]);
verifyStreamAndTrackCounts(pc.getRemoteStreams(), 1, true, true);
verifyRemoteTracksHaveReceivers(pc);
// Make sure object identities are preserved between calls.
assert_array_equals(pc.getReceivers(), pc.getReceivers());
for (let i = 0; i < pc.getReceivers().length; ++i)
assert_true(receivers.has(pc.getReceivers()[i]));
return createStreams({audio:true, video:true}, 1);
})
.then(function(streams) {
return addRemoteStreamsFromLocalStreams(pc, streams);
})
.then(function() {
verifyStreamAndTrackCounts(pc.getRemoteStreams(), 2, true, true);
verifyRemoteTracksHaveReceivers(pc);
// Make sure object identities are preserved between calls.
assert_array_equals(pc.getReceivers(), pc.getReceivers());
// |receivers| contains all receivers so far (4), including the ones for
// the removed streams. The set does not contain duplicates, so adding all
// receivers here should only increase the size for the new stream (2 new
// receivers).
for (let i = 0; i < pc.getReceivers().length; ++i)
receivers.add(pc.getReceivers()[i]);
assert_equals(receivers.size, 6);
removeRemoteStreamsFromLocalStreams(pc, pc.getLocalStreams());
assert_equals(pc.getRemoteStreams().length, 0);
assert_equals(pc.getReceivers().length, 0);
});
}, 'pc.getReceivers() for streams being added and removed.');
// |RTCRtpReceiver::getContributingSources| is mocked to update the contributing
// sources with a predictable pattern, see
// |MockWebRTCRtpReceiver::getContributingSources|. This test ensures that the
// sources are correctly cached for subsequent calls in the same event task
// execution, that they are updated between event task execution calls and that
// object identities of contributing sources hold.
promise_test(function() {
let pc = new RTCPeerConnection();
let receiver = null;
let contributingSource0 = null;
return createStreams({audio:true}, 1)
.then(function(streams) {
return addRemoteStreamsFromLocalStreams(pc, streams);
})
.then(function() {
verifyStreamAndTrackCounts(pc.getRemoteStreams(), 1, true, false);
verifyRemoteTracksHaveReceivers(pc);
assert_equals(pc.getReceivers().length, 1);
receiver = pc.getReceivers()[0];
verifyContributingSourcesAreCached(receiver);
let contributingSources = receiver.getContributingSources();
assert_equals(contributingSources.length, 1);
assertContributingSourceEquals(contributingSources[0], 0, 0.0);
// Remember the first contributing source for later.
contributingSource0 = contributingSources[0];
// Pass |contributingSource0| to the next "then". By resolving a promise
// we get an asynchronous callback, i.e. the "then" happens in the next
// iteration of the event loop. This should invalidate the contributing
// sources cache.
return Promise.resolve(contributingSource0);
}).then(function(prevContributingSource) {
verifyContributingSourcesAreCached(receiver);
let contributingSources = receiver.getContributingSources();
assert_equals(contributingSources.length, 2);
assertContributingSourceEquals(contributingSources[0], 0, 0.0);
assertContributingSourceEquals(contributingSources[1], 1, 5000.0);
// Make sure object identities are preserved between event loop task
// executions (between asynchronous calls and contributing sources
// updates).
assert_equals(contributingSources[0], prevContributingSource);
return Promise.resolve(contributingSources[1]);
}).then(function(prevContributingSource) {
verifyContributingSourcesAreCached(receiver);
let contributingSources = receiver.getContributingSources();
assert_equals(contributingSources.length, 2);
assert_equals(contributingSources[0], prevContributingSource);
assertContributingSourceEquals(contributingSources[0], 1, 5000.0);
assertContributingSourceEquals(contributingSources[1], 2, 10000.0);
// Make sure object identities are preserved between event loop task
// executions (between asynchronous calls and contributing sources
// updates).
assert_equals(contributingSources[0], prevContributingSource);
return Promise.resolve(contributingSources[1]);
}).then(function(prevContributingSource) {
verifyContributingSourcesAreCached(receiver);
// It's source 0's turn to be updated. A contributing source should be
// kept up-to-date in a new event loop task execution without having to
// explicitly call |getContributingSources|.
assertContributingSourceEquals(contributingSource0, 0, 15000.0);
let contributingSources = receiver.getContributingSources();
assert_equals(contributingSources.length, 2);
assertContributingSourceEquals(contributingSources[0], 2, 10000.0);
assertContributingSourceEquals(contributingSources[1], 0, 15000.0);
assert_equals(contributingSources[0], prevContributingSource);
assert_equals(contributingSources[1], contributingSource0);
return Promise.resolve();
});
}, 'receiver.getContributingSources()');
/**
* Helper functions to tests.
*/
function createStreams(constraints, numStreams, streamsSoFar = []) {
if (numStreams == 0) {
return Promise.resolve(streamsSoFar);;
}
return navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
return createStreams(constraints,
numStreams - 1,
streamsSoFar.concat([stream]));
});
}
/**
* Adds |localStreams| to the peer connection and
* |makeRemoteStreamsMatchLocalStreams|.
*
* Returns a promise.
*/
function addRemoteStreamsFromLocalStreams(pc, localStreams) {
for (let i = 0; i < localStreams.length; ++i)
pc.addStream(localStreams[i]);
return makeRemoteStreamsMatchLocalStreams(pc);
}
/**
* Removes |localStreams| from the peer connection and
* |makeRemoteStreamsMatchLocalStreams|.
*
* Returns a promise.
*/
function removeRemoteStreamsFromLocalStreams(pc, localStreams) {
for (let i = 0; i < localStreams.length; ++i)
pc.removeStream(localStreams[i]);
return makeRemoteStreamsMatchLocalStreams(pc);
}
/**
* Makes the peer connection's remote streams match its local streams. New local
* streams result in cloning, removed local streams result in removing the
* corresponding remote stream.
*
* Returns a promise.
*/
function makeRemoteStreamsMatchLocalStreams(pc) {
// In LayoutTests a |MockWebRTCPeerConnectionHandler| is used and as such we
// cannot establish a real call. Instead we rely on the mock making remote
// streams match the local streams on |setRemoteDescription|.
return pc.setRemoteDescription(
new RTCSessionDescription({type:'answer', sdp:'remote'}))
.then(function() {
let localStreams = pc.getLocalStreams();
let remoteStreams = pc.getRemoteStreams();
assert_equals(localStreams.length, remoteStreams.length);
for (let i = 0; i < localStreams.length; i++) {
assert_equals(localStreams[i].getAudioTracks().length,
remoteStreams[i].getAudioTracks().length);
assert_equals(localStreams[i].getVideoTracks().length,
remoteStreams[i].getVideoTracks().length);
}
return Promise.resolve();
});
}
function verifyStreamAndTrackCounts(streams, streamCount, audio, video) {
assert_equals(streams.length, streamCount);
for (let i = 0; i < streams.length; ++i) {
assert_equals(streams[i].getAudioTracks().length, audio ? 1 : 0);
assert_equals(streams[i].getVideoTracks().length, video ? 1 : 0);
}
}
function verifyRemoteTracksHaveReceivers(pc) {
let remoteTracks = new Set();
let remoteStreams = pc.getRemoteStreams();
for (let i = 0; i < remoteStreams.length; ++i) {
let remoteStreamTracks = remoteStreams[i].getTracks();
for (let j = 0; j < remoteStreamTracks.length; ++j) {
remoteTracks.add(remoteStreamTracks[j]);
}
}
let receiverTracks = new Set();
let receivers = pc.getReceivers();
for (let i = 0; i < receivers.length; ++i) {
assert_true(receivers[i] != null);
assert_true(receivers[i].track != null);
assert_true(remoteTracks.has(receivers[i].track));
assert_false(receiverTracks.has(receivers[i].track));
receiverTracks.add(receivers[i].track);
}
assert_equals(receiverTracks.size, remoteTracks.size);
}
/**
* The |MockWebRTCRtpReceiver::getContributingSources| produces a new set of
* contributing sources every call. The |RTCRtpReceiver| has a cache to make
* sure its |getContributingSources| does not change during the same event loop
* task execution.
*/
function verifyContributingSourcesAreCached(receiver) {
// If caching is working as intended, this is always true. It should only be
// able to change between asynchronous calls, e.g. |Promise.resolve().then()|.
// Since the mock always produces a new set of sources, this would not be true
// if caching was not enabled.
assert_array_equals(receiver.getContributingSources(),
receiver.getContributingSources());
}
function assertContributingSourceEquals(contributingSource, source, timestamp) {
assert_equals(contributingSource.source, source);
assert_equals(contributingSource.timestamp, timestamp);
}
</script>
</body>
</html>