OLD | NEW |
| (Empty) |
1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> | |
2 <!-- | |
3 Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | |
4 | |
5 Use of this source code is governed by a BSD-style license | |
6 that can be found in the LICENSE file in the root of the source | |
7 tree. An additional intellectual property rights grant can be found | |
8 in the file PATENTS. All contributing project authors may | |
9 be found in the AUTHORS file in the root of the source tree. | |
10 --> | |
11 <html> | |
12 <head> | |
13 <title>WebRTC Multi-PeerConnection Test</title> | |
14 <script type="text/javascript"> | |
15 // This file can create an arbitrary number of peer connection calls, each | |
16 // with an arbitrary number of auto-echoing data channels. It can run with | |
17 // two separate cameras. | |
18 | |
19 // Our two local video / audio streams. | |
20 var gLocalStream1 = null; | |
21 var gLocalStream2 = null; | |
22 | |
23 // The number of remote view windows (2x number of calls). | |
24 var gNumRemoteViews = 0; | |
25 | |
26 // Maps connection id -> { connection1, connection2 }. | |
27 var gAllConnections = []; | |
28 var gNumConnections = 0; | |
29 | |
30 // Maps data channel id -> sending channel. | |
31 // Note: there can be many data channels per connection id. | |
32 var gSendingDataChannels = []; | |
33 var gTotalNumSendChannels = 0; | |
34 | |
35 function startTest() { | |
36 navigator.webkitGetUserMedia( | |
37 {video: true, audio: true}, | |
38 function(localStream) { | |
39 gLocalStream1 = localStream; | |
40 play(localStream, 'local-view-1'); | |
41 }, | |
42 getUserMediaFailedCallback); | |
43 navigator.webkitGetUserMedia( | |
44 {video: true, audio: true}, | |
45 function(localStream) { | |
46 gLocalStream2 = localStream; | |
47 play(localStream, 'local-view-2'); | |
48 }, | |
49 getUserMediaFailedCallback); | |
50 } | |
51 | |
52 function playStreamInNewRemoteView(stream, peerNumber) { | |
53 console.log('Remote stream to connection ' + peerNumber + | |
54 ': ' + stream.label); | |
55 gNumRemoteViews++; | |
56 var viewName = 'remote-view-' + gNumRemoteViews; | |
57 addRemoteView(viewName, peerNumber); | |
58 play(stream, viewName); | |
59 } | |
60 | |
61 function addRemoteView(elementName, peerNumber) { | |
62 var remoteViews = $('remote-views-' + peerNumber); | |
63 remoteViews.innerHTML += | |
64 '<tr><td><video width="320" height="240" id="' + elementName + '" ' + | |
65 'autoplay="autoplay"></video></td></tr>'; | |
66 } | |
67 | |
68 function play(stream, videoElement) { | |
69 var streamUrl = URL.createObjectURL(stream); | |
70 $(videoElement).src = streamUrl; | |
71 } | |
72 | |
73 function getUserMediaFailedCallback(error) { | |
74 console.log('getUserMedia request failed with code ' + error.code); | |
75 } | |
76 | |
77 function call() { | |
78 connection1 = new webkitRTCPeerConnection(null, | |
79 {optional:[{RtpDataChannels: true}]}); | |
80 connection1.addStream(gLocalStream1); | |
81 | |
82 connection2 = new webkitRTCPeerConnection( | |
83 null, {optional:[{RtpDataChannels: true}]}); | |
84 connection2.addStream(gLocalStream2); | |
85 connection2.onicecandidate = function(event) { | |
86 if (event.candidate) { | |
87 var candidate = new RTCIceCandidate(event.candidate); | |
88 connection1.addIceCandidate(candidate); | |
89 } | |
90 }; | |
91 connection1.onicecandidate = function(event) { | |
92 if (event.candidate) { | |
93 console.log('Ice candidate: ' + event.candidate); | |
94 var candidate = new RTCIceCandidate(event.candidate); | |
95 connection2.addIceCandidate(candidate); | |
96 } | |
97 }; | |
98 connection1.onaddstream = function(event) { | |
99 playStreamInNewRemoteView(event.stream, 1); | |
100 //addDataChannelAnchor(connection1, connection2); | |
101 }; | |
102 connection2.onaddstream = function(event) { | |
103 playStreamInNewRemoteView(event.stream, 2); | |
104 }; | |
105 // TODO(phoglund): hack to work around | |
106 // https://code.google.com/p/webrtc/issues/detail?id=1203. When it is fixed, | |
107 // uncomment the negotiate call, remove addDataChannel and uncomment in | |
108 // connection1.onaddstream. Also remove the notice at the top of the HTML! | |
109 // negotiate(connection1, connection2); | |
110 addDataChannelAnchor(connection1, connection2); | |
111 } | |
112 | |
113 function negotiate(connection1, connection2) { | |
114 connection1.createOffer(function(offer) { | |
115 connection1.setLocalDescription(offer); | |
116 connection2.setRemoteDescription(offer); | |
117 connection2.createAnswer(function(answer) { | |
118 console.log('Created answer ' + answer); | |
119 connection2.setLocalDescription(answer); | |
120 connection1.setRemoteDescription(answer); | |
121 }); | |
122 }); | |
123 } | |
124 | |
125 function addDataChannelAnchor(connection1, connection2) { | |
126 var connectionId = gNumConnections++; | |
127 gAllConnections[connectionId] = { connection1: connection1, | |
128 connection2: connection2 }; | |
129 addOneAnchor(1, connectionId); | |
130 addOneAnchor(2, connectionId); | |
131 } | |
132 | |
133 function makeDataChannelAnchorName(peerId, connectionId) { | |
134 return 'data-channels-peer' + peerId + '-' + connectionId; | |
135 } | |
136 | |
137 // This adds a target table we'll add our input fields to later. | |
138 function addOneAnchor(peerId, connectionId) { | |
139 var newButtonId = 'add-data-channel-' + connectionId; | |
140 var remoteViewContainer = 'remote-views-' + peerId; | |
141 $(remoteViewContainer).innerHTML += | |
142 '<tr><td><button id="' + newButtonId + '" ' + | |
143 'onclick="addDataChannel(' + connectionId + ')">' + | |
144 ' Add Echoing Data Channel</button></td></tr>'; | |
145 | |
146 var anchorName = makeDataChannelAnchorName(peerId, connectionId); | |
147 $(remoteViewContainer).innerHTML += | |
148 '<tr><td><table id="' + anchorName + '"></table></td></tr>'; | |
149 } | |
150 | |
151 // Called by clicking Add Echoing Data Channel. | |
152 function addDataChannel(connectionId) { | |
153 var dataChannelId = gTotalNumSendChannels++; | |
154 | |
155 var peer1SinkId = addDataChannelSink(1, connectionId, dataChannelId); | |
156 var peer2SinkId = addDataChannelSink(2, connectionId, dataChannelId); | |
157 var connections = gAllConnections[connectionId]; | |
158 | |
159 configureChannels(connections.connection1, connections.connection2, | |
160 peer1SinkId, peer2SinkId, dataChannelId); | |
161 | |
162 // Add the field the user types in, and a | |
163 // dummy field so everything lines up nicely. | |
164 addDataChannelSource(1, connectionId, dataChannelId); | |
165 addDisabledInputField(2, connectionId, '(the above is echoed)'); | |
166 | |
167 negotiate(connections.connection1, connections.connection2); | |
168 } | |
169 | |
170 function configureChannels(connection1, connection2, targetFor1, targetFor2, | |
171 dataChannelId) { | |
172 // Label the channel so we know where to send the data later in dispatch. | |
173 sendChannel = connection1.createDataChannel( | |
174 targetFor2, { reliable : false }); | |
175 sendChannel.onmessage = function(messageEvent) { | |
176 $(targetFor1).value = messageEvent.data; | |
177 } | |
178 | |
179 gSendingDataChannels[dataChannelId] = sendChannel; | |
180 | |
181 connection2.ondatachannel = function(event) { | |
182 // The channel got created by a message from a sending channel: hook this | |
183 // new receiver channel up to dispatch and then echo any messages. | |
184 event.channel.onmessage = dispatchAndEchoDataMessage; | |
185 } | |
186 } | |
187 | |
188 function addDataChannelSink(peerNumber, connectionId, dataChannelId) { | |
189 var sinkId = 'data-sink-peer' + peerNumber + '-' + dataChannelId; | |
190 var anchor = $(makeDataChannelAnchorName(peerNumber, connectionId)); | |
191 anchor.innerHTML += | |
192 '<tr><td><input type="text" id="' + sinkId + '" disabled/></td></tr>'; | |
193 return sinkId; | |
194 } | |
195 | |
196 function addDataChannelSource(peerNumber, connectionId, dataChannelId) { | |
197 var sourceId = 'data-source-peer' + peerNumber + '-' + dataChannelId; | |
198 var anchor = $(makeDataChannelAnchorName(peerNumber, connectionId)); | |
199 anchor.innerHTML += | |
200 '<tr><td><input type="text" id="' + sourceId + '"' + | |
201 ' onchange="userWroteSomethingIn(\'' + sourceId + '\', ' + | |
202 dataChannelId + ');"/></td></tr>'; | |
203 } | |
204 | |
205 function userWroteSomethingIn(sourceId, dataChannelId) { | |
206 var source = $(sourceId); | |
207 var dataChannel = gSendingDataChannels[dataChannelId]; | |
208 dataChannel.send(source.value); | |
209 } | |
210 | |
211 function addDisabledInputField(peerNumber, connectionId, text) { | |
212 var anchor = $(makeDataChannelAnchorName(peerNumber, connectionId)); | |
213 anchor.innerHTML += | |
214 '<tr><td><input type="text" value="' + text + '" disabled/></td></tr>'; | |
215 } | |
216 | |
217 function dispatchAndEchoDataMessage(messageEvent) { | |
218 // Since we labeled the channel earlier, we know to which input element | |
219 // we should send the data. | |
220 var dataChannel = messageEvent.currentTarget; | |
221 var targetInput = $(dataChannel.label); | |
222 targetInput.value = messageEvent.data; | |
223 dataChannel.send('echo: ' + messageEvent.data); | |
224 } | |
225 | |
226 window.onload = function() { | |
227 startTest(); | |
228 } | |
229 | |
230 $ = function(id) { | |
231 return document.getElementById(id); | |
232 }; | |
233 </script> | |
234 </head> | |
235 <body> | |
236 <table border="0"> | |
237 <tr> | |
238 <td colspan="2"> | |
239 Notes: | |
240 <ul> | |
241 <li>Due to https://code.google.com/p/webrtc/issues/detail?id=1203, | |
242 you must create a data channel to actually get a call negotiated. Add | |
243 one call at a time and click "add echoing data channel" for each and | |
244 you'll be fine.</li> | |
245 <li>For unknown reasons, adding a new data channel will clear the | |
246 input field contents for all other channels on the same call. This is | |
247 not the data channel's fault though.</li> | |
248 </ul> | |
249 </td> | |
250 </tr> | |
251 <tr> | |
252 <td>Local Preview for Peer 1</td> | |
253 <td>Local Preview for Peer 2</td> | |
254 </tr> | |
255 <tr> | |
256 <td><video width="320" height="240" id="local-view-1" | |
257 autoplay="autoplay"></video></td> | |
258 <td><video width="320" height="240" id="local-view-2" | |
259 autoplay="autoplay"></video></td> | |
260 </tr> | |
261 <tr> | |
262 <td><button id="add-call" onclick="call();">Add Call</button></td> | |
263 </tr> | |
264 <tr> | |
265 <td> | |
266 <table id="remote-views-1"> | |
267 <tr> | |
268 <td>Remote (Incoming to Peer 1)</td> | |
269 </tr> | |
270 </table> | |
271 </td> | |
272 <td> | |
273 <table id="remote-views-2"> | |
274 <tr> | |
275 <td>Remote (Incoming to Peer 2)</td> | |
276 </tr> | |
277 </table> | |
278 </td> | |
279 </tr> | |
280 </table> | |
281 </body> | |
282 </html> | |
OLD | NEW |