Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1342579",
title: "Unbundled PC connects to two different PCs",
visible: true
});
const fakeFingerPrint = "a=fingerprint:sha-256 11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11";
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
const pc3 = new RTCPeerConnection();
const add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
pc1.onicecandidate = e => {
if (e.candidate) {
if (e.candidate.sdpMid === "1") {
add(pc2, e.candidate, generateErrorCallback())
} else {
add(pc3, e.candidate, generateErrorCallback())
}
}
};
pc2.onicecandidate = e => add(pc1, e.candidate, generateErrorCallback());
pc3.onicecandidate = e => add(pc1, e.candidate, generateErrorCallback());
let ice1Finished, ice2Finished, ice3Finished;
const ice1Done = new Promise(r => ice1Finished = r);
const ice2Done = new Promise(r => ice2Finished = r);
const ice3Done = new Promise(r => ice3Finished = r);
const icsc = (pc, str, resolve) => {
const state = pc.iceConnectionState;
info(str + " ICE connection state is: " + state);
if (state == "connected") {
ok(true, str + " ICE connected");
resolve();
} else if (state == "failed") {
ok(false, str + " ICE failed")
resolve();
}
};
pc1.oniceconnectionstatechange = e => icsc(pc1, "PC1", ice1Finished);
pc2.oniceconnectionstatechange = e => icsc(pc2, "PC2", ice2Finished);
pc3.oniceconnectionstatechange = e => icsc(pc3, "PC3", ice3Finished);
function combineAnswer(origAnswer, answer) {
const sdplines = origAnswer.sdp.split('\r\n');
const fpIndex = sdplines.findIndex(l => l.match('^a=fingerprint'));
const FP = sdplines[fpIndex];
const audioIndex = sdplines.findIndex(l => l.match(/^m=audio [1-9]/));
const videoIndex = sdplines.findIndex(l => l.match(/^m=video [1-9]/));
if (audioIndex > -1) {
var ss = sdplines.slice(0, audioIndex);
ss.splice(fpIndex, 1);
answer.sessionSection = ss;
const rejectedVideoIndex = sdplines.findIndex(l => l.match('m=video 0'));
var ams = sdplines.slice(audioIndex, rejectedVideoIndex);
ams.push(FP);
ams.push(fakeFingerPrint);
answer.audioMsection = ams;
}
if (videoIndex > -1) {
var vms = sdplines.slice(videoIndex, sdplines.length -1);
vms.push(fakeFingerPrint);
vms.push(FP);
answer.videoMsection = vms;
}
return answer;
}
runNetworkTest(async () => {
const v1 = createMediaElement('video', 'v1');
const v2 = createMediaElement('video', 'v2');
const v3 = createMediaElement('video', 'v3');
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
(v1.srcObject = stream).getTracks().forEach(t => pc1.addTrack(t, stream));
const stream2 = await navigator.mediaDevices.getUserMedia({ video: true });
(v2.srcObject = stream2).getTracks().forEach(t => pc2.addTrack(t, stream2));
const stream3 = await navigator.mediaDevices.getUserMedia({ audio: true });
(v3.srcObject = stream3).getTracks().forEach(t => pc3.addTrack(t, stream3));
const offer = await pc1.createOffer();
await pc1.setLocalDescription(offer);
//info("Original OFFER: " + JSON.stringify(offer));
offer.sdp = sdputils.removeBundle(offer.sdp);
//info("OFFER w/o BUNDLE: " + JSON.stringify(offer));
const offerAudio = JSON.parse(JSON.stringify(offer));
offerAudio.sdp = offerAudio.sdp.replace('m=video 9', 'm=video 0');
//info("offerAudio: " + JSON.stringify(offerAudio));
const offerVideo = JSON.parse(JSON.stringify(offer));
offerVideo.sdp = offerVideo.sdp.replace('m=audio 9', 'm=audio 0');
//info("offerVideo: " + JSON.stringify(offerVideo));
// We need to do these in parallel, otherwise pc1 will start firing
// icecandidate events before pc3 is ready.
await Promise.all([pc2.setRemoteDescription(offerVideo), pc3.setRemoteDescription(offerAudio)]);
const answerVideo = await pc2.createAnswer();
const answerAudio = await pc3.createAnswer();
const answer = combineAnswer(answerAudio, combineAnswer(answerVideo, {}));
const fakeAnswer = answer.sessionSection.concat(answer.audioMsection, answer.videoMsection).join('\r\n');
info("ANSWER: " + fakeAnswer);
// We want to do these in parallel, because if we do them seqentially, by the
// time pc3.sLD completes pc2 could have fired icecandidate events, when we
// haven't called pc1.sRD yet.
await Promise.all(
[pc2.setLocalDescription(answerVideo),
pc3.setLocalDescription(answerAudio),
pc1.setRemoteDescription({type: 'answer', sdp: fakeAnswer})]);
await Promise.all([ice1Done, ice2Done, ice3Done]);
ok(true, "Connected.");
});
</script>
</pre>
</body>
</html>