<html>
<head>
<script type="text/javascript" src="webrtc_test_utilities.js"></script>
<script type="text/javascript" src="webrtc_test_common.js"></script>
<script type="text/javascript" src="webrtc_test_audio.js"></script>
<script type="text/javascript">
$ = function(id) {
return document.getElementById(id);
};
var gFirstConnection = null;
var gSecondConnection = null;
var gLocalStream = null;
var gRemoteStreams = {};
// The second set of constraints should request audio (e.g. audio:true) since
// we expect audio to be playing after the second renegotiation.
function callAndRenegotiateToAudio(constraints, renegotiationConstraints) {
createConnections(null);
return Promise.all([
navigator.mediaDevices.getUserMedia(constraints)
.then(addStreamToBothConnectionsAndNegotiate),
waitForConnectionToStabilize(gFirstConnection).then(() => {
gFirstConnection.removeStream(gLocalStream);
gSecondConnection.removeStream(gLocalStream);
return navigator.mediaDevices.getUserMedia(renegotiationConstraints)
.then(addStreamToTheFirstConnectionAndNegotiate);
}).then(() => {
return waitForConnectionToStabilize(gFirstConnection)
}).then(() => {
return ensureAudioPlaying(gSecondConnection);
}),
])
.then(logSuccess);
}
function setupCallAndPromiseAudioPlaying(constraints) {
createConnections(null);
// Add the local stream to gFirstConnection to play one-way audio.
return Promise.all([
navigator.mediaDevices.getUserMedia(constraints)
.then(addStreamToTheFirstConnectionAndNegotiate),
waitForConnectionToStabilize(gFirstConnection)
.then(() => { return ensureAudioPlaying(gSecondConnection); }),
]);
}
function callAndEnsureAudioIsPlaying(constraints) {
return setupCallAndPromiseAudioPlaying(constraints)
.then(logSuccess);
}
function enableRemoteVideo(peerConnection, enabled) {
remoteStream = peerConnection.getRemoteStreams()[0];
remoteStream.getVideoTracks()[0].enabled = enabled;
}
function enableRemoteAudio(peerConnection, enabled) {
remoteStream = peerConnection.getRemoteStreams()[0];
remoteStream.getAudioTracks()[0].enabled = enabled;
}
function enableLocalVideo(peerConnection, enabled) {
localStream = peerConnection.getLocalStreams()[0];
localStream.getVideoTracks()[0].enabled = enabled;
}
function enableLocalAudio(peerConnection, enabled) {
localStream = peerConnection.getLocalStreams()[0];
localStream.getAudioTracks()[0].enabled = enabled;
}
function callAndEnsureRemoteAudioTrackMutingWorks(constraints) {
return setupCallAndPromiseAudioPlaying(constraints).then(() => {
// Call is up, now mute the remote track and check we stop playing out
// audio (after a small delay, we don't expect it to happen instantly).
enableRemoteAudio(gSecondConnection, false);
return new Promise(resolve => {
setTimeout(resolve, 250);
})
.then(() => ensureSilence(gSecondConnection));
})
.then(logSuccess);
}
function callAndEnsureLocalAudioTrackMutingWorks(constraints) {
return setupCallAndPromiseAudioPlaying(constraints).then(() => {
// Call is up, now mute the local track of the sending side and ensure
// the receiving side stops receiving audio.
enableLocalAudio(gFirstConnection, false);
return new Promise(resolve => {
setTimeout(resolve, 250);
})
.then(() => ensureSilence(gSecondConnection));
})
.then(logSuccess);
}
function callAndEnsureAudioTrackUnmutingWorks(constraints) {
return setupCallAndPromiseAudioPlaying(constraints).then(() => {
// Mute, wait a while, unmute, verify audio gets back up.
// (Also, ensure video muting doesn't affect audio).
enableRemoteAudio(gSecondConnection, false);
enableRemoteVideo(gSecondConnection, false);
setTimeout(function() {
enableRemoteAudio(gSecondConnection, true);
}, 500);
return new Promise(resolve => {
setTimeout(resolve, 1500);
})
.then(() => ensureAudioPlaying(gSecondConnection));
})
.then(logSuccess);
}
function callAndEnsureLocalVideoMutingDoesntMuteAudio(constraints) {
return setupCallAndPromiseAudioPlaying(constraints).then(() => {
enableLocalVideo(gFirstConnection, false);
return ensureAudioPlaying(gSecondConnection)
.then(logSuccess);
});
}
function callAndEnsureRemoteVideoMutingDoesntMuteAudio(constraints) {
return setupCallAndPromiseAudioPlaying(constraints).then(() => {
enableRemoteVideo(gSecondConnection, false);
return ensureAudioPlaying(gSecondConnection)
.then(logSuccess);
});
}
// TODO(crbug.com/40637961): This test is a temporary replacement for:
// external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html
async function testEstablishAudioOnlyCallAndVerifyGetSynchronizationSourcesWorks() {
const startTime = performance.timeOrigin + performance.now();
await setupCallAndPromiseAudioPlaying({audio: true});
const peerConnection = gSecondConnection;
const receivers = peerConnection.getReceivers();
assertEquals(receivers.length, 1);
const receiver = receivers[0];
assertEquals(receiver.track.kind, 'audio');
const results = receiver.getSynchronizationSources();
assertEquals(results.length, 1);
const result = results[0];
const endTime = performance.timeOrigin + performance.now();
console.log('getSynchronizationSources() = ' + JSON.stringify(result));
// timestamp
assertEquals(typeof result.timestamp, 'number');
assertTrue(result.timestamp >= startTime);
assertTrue(result.timestamp <= endTime);
// source
assertEquals(typeof result.source, 'number');
assertTrue(result.source >= 0);
assertTrue(result.source <= 0xffffffff);
// rtpTimestamp
assertEquals(typeof result.rtpTimestamp, 'number');
assertTrue(result.rtpTimestamp >= 0);
assertTrue(result.rtpTimestamp <= 0xffffffff);
// audioLevel
assertEquals(typeof result.audioLevel, 'number');
assertTrue(result.audioLevel >= 0);
assertTrue(result.audioLevel <= 1);
// voiceActivityFlag
if (result.voiceActivityFlag != undefined) {
assertEquals(typeof result.voiceActivityFlag, 'boolean');
}
return logSuccess();
}
function createConnections(constraints) {
gFirstConnection = createConnection(constraints, 'remote-view-1');
assertEquals('stable', gFirstConnection.signalingState);
gSecondConnection = createConnection(constraints, 'remote-view-2');
assertEquals('stable', gSecondConnection.signalingState);
}
function createConnection(constraints, remoteView) {
var pc = new RTCPeerConnection(null, constraints);
pc.onaddstream = function(event) {
onRemoteStream(event, remoteView);
}
return pc;
}
function displayAndRemember(localStream) {
$('local-view').srcObject = localStream;
gLocalStream = localStream;
}
// Called if getUserMedia succeeds and we want to send from both connections.
function addStreamToBothConnectionsAndNegotiate(localStream) {
displayAndRemember(localStream);
gFirstConnection.addStream(localStream);
gSecondConnection.addStream(localStream);
negotiate();
}
// Called if getUserMedia succeeds when we want to send from one connection.
function addStreamToTheFirstConnectionAndNegotiate(localStream) {
displayAndRemember(localStream);
gFirstConnection.addStream(localStream);
negotiate();
}
function negotiate() {
negotiateBetween(gFirstConnection, gSecondConnection);
}
function onRemoteStream(e, target) {
console.log("Receiving remote stream...");
gRemoteStreams[target] = e.stream;
var remoteVideo = $(target);
remoteVideo.srcObject = e.stream;
}
</script>
</head>
<body>
<table border="0">
<tr>
<td><video width="320" height="240" id="local-view" style="display:none"
autoplay muted></video></td>
<td><video width="320" height="240" id="remote-view-1"
style="display:none" autoplay></video></td>
<td><video width="320" height="240" id="remote-view-2"
style="display:none" autoplay></video></td>
<!-- Canvases are named after their corresponding video elements. -->
<td><canvas width="320" height="240" id="remote-view-1-canvas"
style="display:none"></canvas></td>
<td><canvas width="320" height="240" id="remote-view-2-canvas"
style="display:none"></canvas></td>
</tr>
</table>
</body>
</html>