OLD | NEW |
| (Empty) |
1 /** | |
2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 // This file requires these functions to be defined globally by someone else: | |
8 // function createPeerConnection(stun_server, useRtpDataChannel) | |
9 // function createOffer(peerConnection, constraints, callback) | |
10 // function receiveOffer(peerConnection, offer, constraints, callback) | |
11 // function receiveAnswer(peerConnection, answer, callback) | |
12 | |
13 // Currently these functions are supplied by jsep01_call.js. | |
14 | |
15 /** | |
16 * We need a STUN server for some API calls. | |
17 * @private | |
18 */ | |
19 var STUN_SERVER = 'stun.l.google.com:19302'; | |
20 | |
21 /** | |
22 * This object represents the call. | |
23 * @private | |
24 */ | |
25 var gPeerConnection = null; | |
26 | |
27 /** | |
28 * If true, any created peer connection will use RTP data | |
29 * channels. Otherwise it will use SCTP data channels. | |
30 */ | |
31 var gUseRtpDataChannels = true; | |
32 | |
33 /** | |
34 * This stores ICE candidates generated on this side. | |
35 * @private | |
36 */ | |
37 var gIceCandidates = []; | |
38 | |
39 // Public interface to tests. These are expected to be called with | |
40 // ExecuteJavascript invocations from the browser tests and will return answers | |
41 // through the DOM automation controller. | |
42 | |
43 /** | |
44 * Creates a peer connection. Must be called before most other public functions | |
45 * in this file. | |
46 */ | |
47 function preparePeerConnection() { | |
48 if (gPeerConnection != null) | |
49 throw failTest('creating peer connection, but we already have one.'); | |
50 | |
51 gPeerConnection = createPeerConnection(STUN_SERVER, gUseRtpDataChannels); | |
52 returnToTest('ok-peerconnection-created'); | |
53 } | |
54 | |
55 /** | |
56 * Asks this page to create a local offer. | |
57 * | |
58 * Returns a string on the format ok-(JSON encoded session description). | |
59 * | |
60 * @param {!object} constraints Any createOffer constraints. | |
61 */ | |
62 function createLocalOffer(constraints) { | |
63 if (gPeerConnection == null) | |
64 throw failTest('Negotiating call, but we have no peer connection.'); | |
65 | |
66 // TODO(phoglund): move jsep01.call stuff into this file and remove need | |
67 // of the createOffer method, etc. | |
68 createOffer(gPeerConnection, constraints, function(localOffer) { | |
69 returnToTest('ok-' + JSON.stringify(localOffer)); | |
70 }); | |
71 } | |
72 | |
73 /** | |
74 * Asks this page to accept an offer and generate an answer. | |
75 * | |
76 * Returns a string on the format ok-(JSON encoded session description). | |
77 * | |
78 * @param {!string} sessionDescJson A JSON-encoded session description of type | |
79 * 'offer'. | |
80 * @param {!object} constraints Any createAnswer constraints. | |
81 */ | |
82 function receiveOfferFromPeer(sessionDescJson, constraints) { | |
83 if (gPeerConnection == null) | |
84 throw failTest('Receiving offer, but we have no peer connection.'); | |
85 | |
86 offer = parseJson_(sessionDescJson); | |
87 if (!offer.type) | |
88 failTest('Got invalid session description from peer: ' + sessionDescJson); | |
89 if (offer.type != 'offer') | |
90 failTest('Expected to receive offer from peer, got ' + offer.type); | |
91 | |
92 receiveOffer(gPeerConnection, offer , constraints, function(answer) { | |
93 returnToTest('ok-' + JSON.stringify(answer)); | |
94 }); | |
95 } | |
96 | |
97 /** | |
98 * Asks this page to accept an answer generated by the peer in response to a | |
99 * previous offer by this page | |
100 * | |
101 * Returns a string ok-accepted-answer on success. | |
102 * | |
103 * @param {!string} sessionDescJson A JSON-encoded session description of type | |
104 * 'answer'. | |
105 */ | |
106 function receiveAnswerFromPeer(sessionDescJson) { | |
107 if (gPeerConnection == null) | |
108 throw failTest('Receiving offer, but we have no peer connection.'); | |
109 | |
110 answer = parseJson_(sessionDescJson); | |
111 if (!answer.type) | |
112 failTest('Got invalid session description from peer: ' + sessionDescJson); | |
113 if (answer.type != 'answer') | |
114 failTest('Expected to receive answer from peer, got ' + answer.type); | |
115 | |
116 receiveAnswer(gPeerConnection, answer, function() { | |
117 returnToTest('ok-accepted-answer'); | |
118 }); | |
119 } | |
120 | |
121 /** | |
122 * Adds the local stream to the peer connection. You will have to re-negotiate | |
123 * the call for this to take effect in the call. | |
124 */ | |
125 function addLocalStream() { | |
126 if (gPeerConnection == null) | |
127 throw failTest('adding local stream, but we have no peer connection.'); | |
128 | |
129 addLocalStreamToPeerConnection(gPeerConnection); | |
130 returnToTest('ok-added'); | |
131 } | |
132 | |
133 /** | |
134 * Loads a file with WebAudio and connects it to the peer connection. | |
135 * | |
136 * The loadAudioAndAddToPeerConnection will return ok-added to the test when | |
137 * the sound is loaded and added to the peer connection. The sound will start | |
138 * playing when you call playAudioFile. | |
139 * | |
140 * @param url URL pointing to the file to play. You can assume that you can | |
141 * serve files from the repository's file system. For instance, to serve a | |
142 * file from chrome/test/data/pyauto_private/webrtc/file.wav, pass in a path | |
143 * relative to this directory (e.g. ../pyauto_private/webrtc/file.wav). | |
144 */ | |
145 function addAudioFile(url) { | |
146 if (gPeerConnection == null) | |
147 throw failTest('adding audio file, but we have no peer connection.'); | |
148 | |
149 loadAudioAndAddToPeerConnection(url, gPeerConnection); | |
150 } | |
151 | |
152 /** | |
153 * Mixes the local audio stream with an audio file through WebAudio. | |
154 * | |
155 * You must have successfully requested access to the user's microphone through | |
156 * getUserMedia before calling this function (see getUserMedia.js). | |
157 * Additionally, you must have loaded an audio file to mix with. | |
158 * | |
159 * When playAudioFile is called, WebAudio will effectively mix the user's | |
160 * microphone input with the previously loaded file and feed that into the | |
161 * peer connection. | |
162 */ | |
163 function mixLocalStreamWithPreviouslyLoadedAudioFile() { | |
164 if (gPeerConnection == null) | |
165 throw failTest('trying to mix in stream, but we have no peer connection.'); | |
166 if (getLocalStream() == null) | |
167 throw failTest('trying to mix in stream, but we have no stream to mix in.'); | |
168 | |
169 mixLocalStreamIntoPeerConnection(gPeerConnection, getLocalStream()); | |
170 } | |
171 | |
172 /** | |
173 * Must be called after addAudioFile. | |
174 */ | |
175 function playAudioFile() { | |
176 if (gPeerConnection == null) | |
177 throw failTest('trying to play file, but we have no peer connection.'); | |
178 | |
179 playPreviouslyLoadedAudioFile(gPeerConnection); | |
180 returnToTest('ok-playing'); | |
181 } | |
182 | |
183 /** | |
184 * Hangs up a started call. Returns ok-call-hung-up on success. | |
185 */ | |
186 function hangUp() { | |
187 if (gPeerConnection == null) | |
188 throw failTest('hanging up, but has no peer connection'); | |
189 gPeerConnection.close(); | |
190 gPeerConnection = null; | |
191 returnToTest('ok-call-hung-up'); | |
192 } | |
193 | |
194 /** | |
195 * Retrieves all ICE candidates generated on this side. Must be called after | |
196 * ICE candidate generation is triggered (for instance by running a call | |
197 * negotiation). This function will wait if necessary if we're not done | |
198 * generating ICE candidates on this side. | |
199 * | |
200 * Returns a JSON-encoded array of RTCIceCandidate instances to the test. | |
201 */ | |
202 function getAllIceCandidates() { | |
203 if (gPeerConnection == null) | |
204 throw failTest('Trying to get ICE candidates, but has no peer connection.'); | |
205 | |
206 if (gPeerConnection.iceGatheringState != 'complete') { | |
207 console.log('Still ICE gathering - waiting...'); | |
208 setTimeout(getAllIceCandidates, 100); | |
209 return; | |
210 } | |
211 | |
212 returnToTest(JSON.stringify(gIceCandidates)); | |
213 } | |
214 | |
215 /** | |
216 * Receives ICE candidates from the peer. | |
217 * | |
218 * Returns ok-received-candidates to the test on success. | |
219 * | |
220 * @param iceCandidatesJson a JSON-encoded array of RTCIceCandidate instances. | |
221 */ | |
222 function receiveIceCandidates(iceCandidatesJson) { | |
223 if (gPeerConnection == null) | |
224 throw failTest('Received ICE candidate, but has no peer connection'); | |
225 | |
226 var iceCandidates = parseJson_(iceCandidatesJson); | |
227 if (!iceCandidates.length) | |
228 throw failTest('Received invalid ICE candidate list from peer: ' + | |
229 iceCandidatesJson); | |
230 | |
231 iceCandidates.forEach(function(iceCandidate) { | |
232 if (!iceCandidate.candidate) | |
233 failTest('Received invalid ICE candidate from peer: ' + | |
234 iceCandidatesJson); | |
235 | |
236 gPeerConnection.addIceCandidate(new RTCIceCandidate(iceCandidate)); | |
237 }); | |
238 | |
239 returnToTest('ok-received-candidates'); | |
240 } | |
241 | |
242 // Public interface to signaling implementations, such as JSEP. | |
243 | |
244 /** | |
245 * Enqueues an ICE candidate for sending to the peer. | |
246 * | |
247 * @param {!RTCIceCandidate} The ICE candidate to send. | |
248 */ | |
249 function sendIceCandidate(message) { | |
250 gIceCandidates.push(message); | |
251 } | |
252 | |
253 /** | |
254 * Parses JSON-encoded session descriptions and ICE candidates. | |
255 * @private | |
256 */ | |
257 function parseJson_(json) { | |
258 // Escape since the \r\n in the SDP tend to get unescaped. | |
259 jsonWithEscapedLineBreaks = json.replace(/\r\n/g, '\\r\\n'); | |
260 try { | |
261 return JSON.parse(jsonWithEscapedLineBreaks); | |
262 } catch (exception) { | |
263 failTest('Failed to parse JSON: ' + jsonWithEscapedLineBreaks + ', got ' + | |
264 exception); | |
265 } | |
266 } | |
OLD | NEW |