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