1d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org/**
2d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org *
4d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org * Use of this source code is governed by a BSD-style license
5d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org * that can be found in the LICENSE file in the root of the source
6d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org * tree. An additional intellectual property rights grant can be found
7d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org * in the file PATENTS.  All contributing project authors may
8d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org * be found in the AUTHORS file in the root of the source tree.
9d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org */
10d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
11d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org// LoopbackTest establish a one way loopback call between 2 peer connections
12d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org// while continuously monitoring bandwidth stats. The idea is to use this as
13d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org// a base for other future tests and to keep track of more than just bandwidth
14d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org// stats.
15d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org//
16d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org// Usage:
17d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org//  var test = new LoopbackTest(stream, callDurationMs,
18ccef356aceb30b18b8cc50d0e55d2caa2a16b00candresp@webrtc.org//                              forceTurn, pcConstraints,
19ccef356aceb30b18b8cc50d0e55d2caa2a16b00candresp@webrtc.org//                              maxVideoBitrateKbps);
20d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org//  test.run(onDone);
21d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org//  function onDone() {
22d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org//    test.getResults(); // return stats recorded during the loopback test.
23d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org//  }
24d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org//
25ccef356aceb30b18b8cc50d0e55d2caa2a16b00candresp@webrtc.orgfunction LoopbackTest(
26ccef356aceb30b18b8cc50d0e55d2caa2a16b00candresp@webrtc.org    stream,
27ccef356aceb30b18b8cc50d0e55d2caa2a16b00candresp@webrtc.org    callDurationMs,
28ccef356aceb30b18b8cc50d0e55d2caa2a16b00candresp@webrtc.org    forceTurn,
29ccef356aceb30b18b8cc50d0e55d2caa2a16b00candresp@webrtc.org    pcConstraints,
30ccef356aceb30b18b8cc50d0e55d2caa2a16b00candresp@webrtc.org    maxVideoBitrateKbps) {
31ccef356aceb30b18b8cc50d0e55d2caa2a16b00candresp@webrtc.org
32d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  var pc1StatTracker;
33d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  var pc2StatTracker;
34d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
35d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // In order to study effect of network (e.g. wifi) on peer connection one can
36d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // establish a loopback call and force it to go via a turn server. This way
37d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // the call won't switch to local addresses. That is achieved by filtering out
38d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // all non-relay ice candidades on both peers.
39d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  function constrainTurnCandidates(pc) {
40d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    var origAddIceCandidate = pc.addIceCandidate;
41d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    pc.addIceCandidate = function (candidate, successCallback,
42d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                                   failureCallback) {
43d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      if (forceTurn && candidate.candidate.indexOf("typ relay ") == -1) {
44d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        trace("Dropping non-turn candidate: " + candidate.candidate);
45d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        successCallback();
46d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        return;
47d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      } else {
48d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        origAddIceCandidate.call(this, candidate, successCallback,
49d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                                 failureCallback);
50d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      }
51d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    }
52d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  }
53d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
54d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // FEC makes it hard to study bwe estimation since there seems to be a spike
55d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // when it is enabled and disabled. Disable it for now. FEC issue tracked on:
56d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // https://code.google.com/p/webrtc/issues/detail?id=3050
57d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  function constrainOfferToRemoveFec(pc) {
58d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    var origCreateOffer = pc.createOffer;
59d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    pc.createOffer = function (successCallback, failureCallback, options) {
60d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      function filteredSuccessCallback(desc) {
61d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        desc.sdp = desc.sdp.replace(/(m=video 1 [^\r]+)(116 117)(\r\n)/g,
62d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                                    '$1\r\n');
63d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        desc.sdp = desc.sdp.replace(/a=rtpmap:116 red\/90000\r\n/g, '');
64d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        desc.sdp = desc.sdp.replace(/a=rtpmap:117 ulpfec\/90000\r\n/g, '');
65d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        successCallback(desc);
66d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      }
67d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      origCreateOffer.call(this, filteredSuccessCallback, failureCallback,
68d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                           options);
69d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    }
70d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  }
71d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
72d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // Constraint max video bitrate by modifying the SDP when creating an answer.
73d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  function constrainBitrateAnswer(pc) {
74d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    var origCreateAnswer = pc.createAnswer;
75d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    pc.createAnswer = function (successCallback, failureCallback, options) {
76d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      function filteredSuccessCallback(desc) {
77d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        if (maxVideoBitrateKbps) {
78d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org          desc.sdp = desc.sdp.replace(
79d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org              /a=mid:video\r\n/g,
80d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org              'a=mid:video\r\nb=AS:' + maxVideoBitrateKbps + '\r\n');
81d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        }
82d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        successCallback(desc);
83d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      }
84d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      origCreateAnswer.call(this, filteredSuccessCallback, failureCallback,
85d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                            options);
86d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    }
87d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  }
88d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
89d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // Run the actual LoopbackTest.
90d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  this.run = function(doneCallback) {
91d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    if (forceTurn) requestTurn(start, fail);
92d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    else start();
93d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
94d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    function start(turnServer) {
95d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      var pcConfig = forceTurn ? { iceServers: [turnServer] } : null;
96d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      console.log(pcConfig);
97ccef356aceb30b18b8cc50d0e55d2caa2a16b00candresp@webrtc.org      var pc1 = new RTCPeerConnection(pcConfig, pcConstraints);
98d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      constrainTurnCandidates(pc1);
99d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      constrainOfferToRemoveFec(pc1);
100d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      pc1StatTracker = new StatTracker(pc1, 50);
101d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      pc1StatTracker.recordStat("EstimatedSendBitrate",
102d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                                "bweforvideo", "googAvailableSendBandwidth");
103d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      pc1StatTracker.recordStat("TransmitBitrate",
104d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                                "bweforvideo", "googTransmitBitrate");
105d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      pc1StatTracker.recordStat("TargetEncodeBitrate",
106d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                                "bweforvideo", "googTargetEncBitrate");
107d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      pc1StatTracker.recordStat("ActualEncodedBitrate",
108d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                                "bweforvideo", "googActualEncBitrate");
109d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
110ccef356aceb30b18b8cc50d0e55d2caa2a16b00candresp@webrtc.org      var pc2 = new RTCPeerConnection(pcConfig, pcConstraints);
111d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      constrainTurnCandidates(pc2);
112d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      constrainBitrateAnswer(pc2);
113d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      pc2StatTracker = new StatTracker(pc2, 50);
114d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      pc2StatTracker.recordStat("REMB",
115d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                                "bweforvideo", "googAvailableReceiveBandwidth");
116d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
117d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      pc1.addStream(stream);
118d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      var call = new Call(pc1, pc2);
119d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
120d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      call.start();
121d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      setTimeout(function () {
122d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org          call.stop();
123d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org          pc1StatTracker.stop();
124d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org          pc2StatTracker.stop();
125d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org          success();
126d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        }, callDurationMs);
127d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    }
128d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
129d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    function success() {
130d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      trace("Success");
131d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      doneCallback();
132d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    }
133d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
1341fb05fcdfaccd145e6433aa1dbbbf6fd9adc5639andresp@webrtc.org    function fail(msg) {
1351fb05fcdfaccd145e6433aa1dbbbf6fd9adc5639andresp@webrtc.org      trace("Fail: " + msg);
136d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      doneCallback();
137d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    }
138d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  }
139d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
140d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // Returns a google visualization datatable with the recorded samples during
141d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // the loopback test.
142d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  this.getResults = function () {
143d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    return mergeDataTable(pc1StatTracker.dataTable(),
144d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                          pc2StatTracker.dataTable());
145d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  }
146d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
147d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // Helper class to establish and manage a call between 2 peer connections.
148d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // Usage:
149d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  //   var c = new Call(pc1, pc2);
150d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  //   c.start();
151d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  //   c.stop();
152d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  //
153d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  function Call(pc1, pc2) {
154d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    pc1.onicecandidate = applyIceCandidate.bind(pc2);
155d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    pc2.onicecandidate = applyIceCandidate.bind(pc1);
156d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
157d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    function applyIceCandidate(e) {
158d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      if (e.candidate) {
159d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        this.addIceCandidate(new RTCIceCandidate(e.candidate),
160d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                             onAddIceCandidateSuccess,
161d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                             onAddIceCandidateError);
162d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      }
163d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    }
164d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
165d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    function onAddIceCandidateSuccess() {}
166d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    function onAddIceCandidateError(error) {
167d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      trace("Failed to add Ice Candidate: " + error.toString());
168d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    }
169d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
170d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    this.start = function() {
171d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      pc1.createOffer(gotDescription1, onCreateSessionDescriptionError);
172d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
173d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      function onCreateSessionDescriptionError(error) {
174d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        trace('Failed to create session description: ' + error.toString());
175d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      }
176d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
177d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      function gotDescription1(desc){
178d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        trace("Offer: " + desc.sdp);
179d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        pc1.setLocalDescription(desc);
180d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        pc2.setRemoteDescription(desc);
181d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        // Since the "remote" side has no media stream we need
182d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        // to pass in the right constraints in order for it to
183d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        // accept the incoming offer of audio and video.
184d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        pc2.createAnswer(gotDescription2, onCreateSessionDescriptionError);
185d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      }
186d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
187d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      function gotDescription2(desc){
188d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        trace("Answer: " + desc.sdp);
189d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        pc2.setLocalDescription(desc);
190d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        pc1.setRemoteDescription(desc);
191d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      }
192d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    }
193d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
194d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    this.stop = function() {
195d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      pc1.close();
196d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      pc2.close();
197d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    }
198d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  }
199d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
200d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  // Request a turn server. This uses the same servers as apprtc.
201d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  function requestTurn(successCallback, failureCallback) {
202d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    var currentDomain = document.domain;
203d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    if (currentDomain.search('localhost') === -1 &&
2041fb05fcdfaccd145e6433aa1dbbbf6fd9adc5639andresp@webrtc.org        currentDomain.search('webrtc.googlecode.com') === -1) {
2051fb05fcdfaccd145e6433aa1dbbbf6fd9adc5639andresp@webrtc.org      failureCallback("Domain not authorized for turn server: " +
2061fb05fcdfaccd145e6433aa1dbbbf6fd9adc5639andresp@webrtc.org                      currentDomain);
207d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      return;
208d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    }
209d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
210d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    // Get a turn server from computeengineondemand.appspot.com.
211d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    var turnUrl = 'https://computeengineondemand.appspot.com/' +
212d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                  'turn?username=156547625762562&key=4080218913';
213d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    var xmlhttp = new XMLHttpRequest();
214d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    xmlhttp.onreadystatechange = onTurnResult;
215d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    xmlhttp.open('GET', turnUrl, true);
216d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    xmlhttp.send();
217d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
218d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    function onTurnResult() {
219d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      if (this.readyState !== 4) {
220d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        return;
221d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      }
222d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org
223d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      if (this.status === 200) {
224d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        var turnServer = JSON.parse(xmlhttp.responseText);
225d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        // Create turnUris using the polyfill (adapter.js).
226d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        turnServer.uris = turnServer.uris.filter(
227d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org            function (e) { return e.search('transport=udp') != -1; }
228d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        );
229d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        var iceServers = createIceServers(turnServer.uris,
230d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                                          turnServer.username,
231d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org                                          turnServer.password);
232d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        if (iceServers !== null) {
233d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org          successCallback(iceServers);
234d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org          return;
235d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org        }
236d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      }
237d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org      failureCallback("Failed to get a turn server.");
238d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org    }
239d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org  }
240d07336255a3f273d8a14d4485dd73c0398491bc3andresp@webrtc.org}
241