1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
<!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>
|