1
<!DOCTYPE HTML PUBLIC
"-//IETF//DTD HTML//EN">
3 Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
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.
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.
19 // Our two local video / audio streams.
20 var gLocalStream1
= null;
21 var gLocalStream2
= null;
23 // The number of remote view windows (2x number of calls).
24 var gNumRemoteViews
= 0;
26 // Maps connection id -> { connection1, connection2 }.
27 var gAllConnections
= [];
28 var gNumConnections
= 0;
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;
35 function startTest() {
36 navigator
.webkitGetUserMedia(
37 {video
: true, audio
: true},
38 function(localStream
) {
39 gLocalStream1
= localStream
;
40 play(localStream
, 'local-view-1');
42 getUserMediaFailedCallback
);
43 navigator
.webkitGetUserMedia(
44 {video
: true, audio
: true},
45 function(localStream
) {
46 gLocalStream2
= localStream
;
47 play(localStream
, 'local-view-2');
49 getUserMediaFailedCallback
);
52 function playStreamInNewRemoteView(stream
, peerNumber
) {
53 console
.log('Remote stream to connection ' + peerNumber
+
56 var viewName
= 'remote-view-' + gNumRemoteViews
;
57 addRemoteView(viewName
, peerNumber
);
58 play(stream
, viewName
);
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>';
68 function play(stream
, videoElement
) {
69 var streamUrl
= URL
.createObjectURL(stream
);
70 $(videoElement
).src
= streamUrl
;
73 function getUserMediaFailedCallback(error
) {
74 console
.log('getUserMedia request failed with code ' + error
.code
);
78 connection1
= new webkitRTCPeerConnection(null,
79 {optional
:[{RtpDataChannels
: true}]});
80 connection1
.addStream(gLocalStream1
);
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
);
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
);
98 connection1
.onaddstream = function(event
) {
99 playStreamInNewRemoteView(event
.stream
, 1);
100 //addDataChannelAnchor(connection1, connection2);
102 connection2
.onaddstream = function(event
) {
103 playStreamInNewRemoteView(event
.stream
, 2);
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
);
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
);
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
);
133 function makeDataChannelAnchorName(peerId
, connectionId
) {
134 return 'data-channels-peer' + peerId
+ '-' + connectionId
;
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>';
146 var anchorName
= makeDataChannelAnchorName(peerId
, connectionId
);
147 $(remoteViewContainer
).innerHTML
+=
148 '<tr><td><table id="' + anchorName
+ '"></table></td></tr>';
151 // Called by clicking Add Echoing Data Channel.
152 function addDataChannel(connectionId
) {
153 var dataChannelId
= gTotalNumSendChannels
++;
155 var peer1SinkId
= addDataChannelSink(1, connectionId
, dataChannelId
);
156 var peer2SinkId
= addDataChannelSink(2, connectionId
, dataChannelId
);
157 var connections
= gAllConnections
[connectionId
];
159 configureChannels(connections
.connection1
, connections
.connection2
,
160 peer1SinkId
, peer2SinkId
, dataChannelId
);
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)');
167 negotiate(connections
.connection1
, connections
.connection2
);
170 function configureChannels(connection1
, connection2
, targetFor1
, targetFor2
,
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
;
179 gSendingDataChannels
[dataChannelId
] = sendChannel
;
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
;
188 function addDataChannelSink(peerNumber
, connectionId
, dataChannelId
) {
189 var sinkId
= 'data-sink-peer' + peerNumber
+ '-' + dataChannelId
;
190 var anchor
= $(makeDataChannelAnchorName(peerNumber
, connectionId
));
192 '<tr><td><input type="text" id="' + sinkId
+ '" disabled/></td></tr>';
196 function addDataChannelSource(peerNumber
, connectionId
, dataChannelId
) {
197 var sourceId
= 'data-source-peer' + peerNumber
+ '-' + dataChannelId
;
198 var anchor
= $(makeDataChannelAnchorName(peerNumber
, connectionId
));
200 '<tr><td><input type="text" id="' + sourceId
+ '"' +
201 ' onchange="userWroteSomethingIn(\'' + sourceId
+ '\', ' +
202 dataChannelId
+ ');"/></td></tr>';
205 function userWroteSomethingIn(sourceId
, dataChannelId
) {
206 var source
= $(sourceId
);
207 var dataChannel
= gSendingDataChannels
[dataChannelId
];
208 dataChannel
.send(source
.value
);
211 function addDisabledInputField(peerNumber
, connectionId
, text
) {
212 var anchor
= $(makeDataChannelAnchorName(peerNumber
, connectionId
));
214 '<tr><td><input type="text" value="' + text
+ '" disabled/></td></tr>';
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
);
226 window
.onload = function() {
231 return document
.getElementById(id
);
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
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>
252 <td>Local Preview for Peer
1</td>
253 <td>Local Preview for Peer
2</td>
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>
262 <td><button id=
"add-call" onclick=
"call();">Add Call
</button></td>
266 <table id=
"remote-views-1">
268 <td>Remote (Incoming to Peer
1)
</td>
273 <table id=
"remote-views-2">
275 <td>Remote (Incoming to Peer
2)
</td>