Home | History | Annotate | Download | only in functional_test
      1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
      2 
      3 <!--
      4 Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
      5 
      6 Use of this source code is governed by a BSD-style license
      7 that can be found in the LICENSE file in the root of the source
      8 tree. An additional intellectual property rights grant can be found
      9 in the file PATENTS. All contributing project authors may
     10 be found in the AUTHORS file in the root of the source tree.
     11 -->
     12 
     13 <html>
     14 
     15 <head>
     16 <title>WebRTC Test</title>
     17 
     18 <style type="text/css">
     19 body, input, button, select, table {
     20   font-family:"Lucida Grande", "Lucida Sans", Verdana, Arial, sans-serif;
     21   font-size: 13 px;
     22 }
     23 body, input:enable, button:enable, select:enable, table {
     24   color: rgb(51, 51, 51);
     25 }
     26 h1 {font-size: 40 px;}
     27 </style>
     28 
     29 <script type="text/javascript">
     30 
     31 // TODO: Catch more exceptions
     32 
     33 var server;
     34 var myId = -1;
     35 var myName;
     36 var remoteId = -1;
     37 var remoteName;
     38 var request = null;
     39 var hangingGet = null;
     40 var pc = null;
     41 var localStream = null;
     42 var disconnecting = false;
     43 var callState = 0; // 0 - Not started, 1 - Call ongoing
     44 
     45 
     46 // General
     47 
     48 function toggleExtraButtons() {
     49   document.getElementById("createPcBtn").hidden =
     50     !document.getElementById("createPcBtn").hidden;
     51   document.getElementById("test1Btn").hidden =
     52     !document.getElementById("test1Btn").hidden;
     53 }
     54 
     55 function trace(txt) {
     56   var elem = document.getElementById("debug");
     57   elem.innerHTML += txt + "<br>";
     58 }
     59 
     60 function trace_warning(txt) {
     61   var wtxt = "<b>" + txt + "</b>";
     62   trace(wtxt);
     63 }
     64 
     65 function trace_exception(e, txt) {
     66   var etxt = "<b>" + txt + "</b> (" + e.name + " / " + e.message + ")";
     67   trace(etxt);
     68 }
     69 
     70 function setCallState(state) {
     71   trace("Changing call state: " + callState + " -> " + state);
     72   callState = state;
     73 }
     74 
     75 function checkPeerConnection() {
     76   if (!pc) {
     77     trace_warning("No PeerConnection object exists");
     78     return 0;
     79   }
     80   return 1;
     81 }
     82 
     83 
     84 // Local stream generation
     85 
     86 function gotStream(s) {
     87   var url = webkitURL.createObjectURL(s);
     88   document.getElementById("localView").src = url;
     89   trace("User has granted access to local media. url = " + url);
     90   localStream = s;
     91 }
     92 
     93 function gotStreamFailed(error) {
     94   alert("Failed to get access to local media. Error code was " + error.code +
     95     ".");
     96   trace_warning("Failed to get access to local media. Error code was " +
     97     error.code);
     98 }
     99 
    100 function getUserMedia() {
    101   try {
    102     navigator.webkitGetUserMedia("video,audio", gotStream, gotStreamFailed);
    103     trace("Requested access to local media");
    104   } catch (e) {
    105     trace_exception(e, "getUserMedia error");
    106   }
    107 }
    108 
    109 
    110 // Peer list and remote peer handling
    111 
    112 function peerExists(id) {
    113   try {
    114     var peerList = document.getElementById("peers");
    115     for (var i = 0; i < peerList.length; i++) {
    116       if (parseInt(peerList.options[i].value) == id)
    117         return true;
    118     }
    119   } catch (e) {
    120     trace_exception(e, "Error searching for peer");
    121   }
    122   return false;
    123 }
    124 
    125 function addPeer(id, pname) {
    126   var peerList = document.getElementById("peers");
    127   var option = document.createElement("option");
    128   option.text = pname;
    129   option.value = id;
    130   try {
    131     // For IE earlier than version 8
    132     peerList.add(option, x.options[null]);
    133   } catch (e) {
    134     peerList.add(option, null);
    135   }
    136 }
    137 
    138 function removePeer(id) {
    139   try {
    140     var peerList = document.getElementById("peers");
    141     for (var i = 0; i < peerList.length; i++) {
    142       if (parseInt(peerList.options[i].value) == id) {
    143         peerList.remove(i);
    144         break;
    145       }
    146     }
    147   } catch (e) {
    148     trace_exception(e, "Error removing peer");
    149   }
    150 }
    151 
    152 function clearPeerList() {
    153   var peerList = document.getElementById("peers");
    154   while (peerList.length > 0)
    155     peerList.remove(0);
    156 }
    157 
    158 function setSelectedPeer(id) {
    159   try {
    160     var peerList = document.getElementById("peers");
    161     for (var i = 0; i < peerList.length; i++) {
    162       if (parseInt(peerList.options[i].value) == id) {
    163         peerList.options[i].selected = true;
    164         return true;
    165       }
    166     }
    167   } catch (e) {
    168     trace_exception(e, "Error setting selected peer");
    169   }
    170   return false;
    171 }
    172 
    173 function getPeerName(id) {
    174   try {
    175     var peerList = document.getElementById("peers");
    176     for (var i = 0; i < peerList.length; i++) {
    177       if (parseInt(peerList.options[i].value) == id) {
    178         return peerList.options[i].text;
    179       }
    180     }
    181   } catch (e) {
    182     trace_exception(e, "Error finding peer name");
    183     return;
    184   }
    185   return;
    186 }
    187 
    188 function storeRemoteInfo() {
    189   try {
    190     var peerList = document.getElementById("peers");
    191     if (peerList.selectedIndex < 0) {
    192       alert("Please select a peer.");
    193       return false;
    194     } else
    195       remoteId = parseInt(peerList.options[peerList.selectedIndex].value);
    196       remoteName = peerList.options[peerList.selectedIndex].text;
    197   } catch (e) {
    198     trace_exception(e, "Error storing remote peer info");
    199     return false;
    200   }
    201   return true;
    202 }
    203 
    204 
    205 // Call control
    206 
    207 function createPeerConnection() {
    208   if (pc) {
    209     trace_warning("PeerConnection object already exists");
    210   }
    211   trace("Creating PeerConnection object");
    212   try {
    213     pc = new webkitPeerConnection("STUN stun.l.google.com:19302",
    214       onSignalingMessage);
    215   pc.onaddstream = onAddStream;
    216   pc.onremovestream = onRemoveStream;
    217   } catch (e) {
    218     trace_exception(e, "Create PeerConnection error");
    219   }
    220 }
    221 
    222 function doCall() {
    223   if (!storeRemoteInfo())
    224     return;
    225   document.getElementById("call").disabled = true;
    226   document.getElementById("peers").disabled = true;
    227   createPeerConnection();
    228   trace("Adding stream");
    229   pc.addStream(localStream);
    230   document.getElementById("hangup").disabled = false;
    231   setCallState(1);
    232 }
    233 
    234 function hangUp() {
    235   document.getElementById("hangup").disabled = true;
    236   trace("Sending BYE to " + remoteName + " (ID " + remoteId + ")");
    237   sendToPeer(remoteId, "BYE");
    238   closeCall();
    239 }
    240 
    241 function closeCall() {
    242   trace("Stopping showing remote stream");
    243   document.getElementById("remoteView").src = "dummy";
    244   if (pc) {
    245     trace("Stopping call [pc.close()]");
    246     pc.close();
    247     pc = null;
    248   } else
    249     trace("No pc object to close");
    250   remoteId = -1;
    251   document.getElementById("call").disabled = false;
    252   document.getElementById("peers").disabled = false;
    253   setCallState(0);
    254 }
    255 
    256 
    257 // PeerConnection callbacks
    258 
    259 function onAddStream(e) {
    260   var stream = e.stream;
    261   var url = webkitURL.createObjectURL(stream);
    262   document.getElementById("remoteView").src = url;
    263   trace("Started showing remote stream. url = " + url);
    264 }
    265 
    266 function onRemoveStream(e) {
    267   // Currently if we get this callback, call has ended.
    268   document.getElementById("remoteView").src = "";
    269   trace("Stopped showing remote stream");
    270 }
    271 
    272 function onSignalingMessage(msg) {
    273   trace("Sending message to " + remoteName + " (ID " + remoteId + "):\n" + msg);
    274   sendToPeer(remoteId, msg);
    275 }
    276 
    277 // TODO: Add callbacks onconnecting, onopen and onstatechange.
    278 
    279 
    280 // Server interaction
    281 
    282 function handleServerNotification(data) {
    283   trace("Server notification: " + data);
    284   var parsed = data.split(",");
    285   if (parseInt(parsed[2]) == 1) { // New peer
    286     var peerId = parseInt(parsed[1]);
    287     if (!peerExists(peerId)) {
    288       var peerList = document.getElementById("peers");
    289       if (peerList.length == 1 && peerList.options[0].value == -1)
    290         clearPeerList();
    291       addPeer(peerId, parsed[0]);
    292       document.getElementById("peers").disabled = false;
    293       document.getElementById("call").disabled = false;
    294     }
    295   } else if (parseInt(parsed[2]) == 0) { // Removed peer
    296     removePeer(parseInt(parsed[1]));
    297     if (document.getElementById("peers").length == 0) {
    298       document.getElementById("peers").disabled = true;
    299       addPeer(-1, "No other peer connected");
    300     }
    301   }
    302 }
    303 
    304 function handlePeerMessage(peer_id, msg) {
    305   var peerName = getPeerName(peer_id);
    306   if (peerName == undefined) {
    307     trace_warning("Received message from unknown peer (ID " + peer_id +
    308       "), ignoring message:");
    309     trace(msg);
    310     return;
    311   }
    312   trace("Received message from " + peerName + " (ID " + peer_id + "):\n" + msg);
    313   // Assuming we receive the message from the peer we want to communicate with.
    314   // TODO: Only accept messages from peer we communicate with with if call is
    315   // ongoing.
    316   if (msg.search("BYE") == 0) {
    317     // Other side has hung up.
    318     document.getElementById("hangup").disabled = true;
    319     closeCall()
    320   } else {
    321     if (!pc) {
    322       // Other side is calling us, startup
    323       if (!setSelectedPeer(peer_id)) {
    324         trace_warning("Recevied message from unknown peer, ignoring");
    325         return;
    326       }
    327       if (!storeRemoteInfo())
    328         return;
    329       document.getElementById("call").disabled = true;
    330       document.getElementById("peers").disabled = true;
    331       createPeerConnection();
    332       try {
    333         pc.processSignalingMessage(msg);
    334       } catch (e) {
    335         trace_exception(e, "Process signaling message error");
    336       }
    337       trace("Adding stream");
    338       pc.addStream(localStream);
    339       document.getElementById("hangup").disabled = false;
    340     } else {
    341       try {
    342         pc.processSignalingMessage(msg);
    343       } catch (e) {
    344         trace_exception(e, "Process signaling message error");
    345       }
    346     }
    347   }
    348 }
    349 
    350 function getIntHeader(r, name) {
    351   var val = r.getResponseHeader(name);
    352   trace("header value: " + val);
    353   return val != null && val.length ? parseInt(val) : -1;
    354 }
    355 
    356 function hangingGetCallback() {
    357   try {
    358     if (hangingGet.readyState != 4 || disconnecting)
    359       return;
    360     if (hangingGet.status != 200) {
    361       trace_warning("server error, status: " + hangingGet.status + ", text: " +
    362         hangingGet.statusText);
    363       disconnect();
    364     } else {
    365       var peer_id = getIntHeader(hangingGet, "Pragma");
    366       if (peer_id == myId) {
    367         handleServerNotification(hangingGet.responseText);
    368       } else {
    369         handlePeerMessage(peer_id, hangingGet.responseText);
    370       }
    371     }
    372 
    373     if (hangingGet) {
    374       hangingGet.abort();
    375       hangingGet = null;
    376     }
    377 
    378     if (myId != -1)
    379       window.setTimeout(startHangingGet, 0);
    380   } catch (e) {
    381     trace_exception(e, "Hanging get error");
    382   }
    383 }
    384 
    385 function onHangingGetTimeout() {
    386   trace("hanging get timeout. issuing again");
    387   hangingGet.abort();
    388   hangingGet = null;
    389   if (myId != -1)
    390     window.setTimeout(startHangingGet, 0);
    391 }
    392 
    393 function startHangingGet() {
    394   try {
    395     hangingGet = new XMLHttpRequest();
    396     hangingGet.onreadystatechange = hangingGetCallback;
    397     hangingGet.ontimeout = onHangingGetTimeout;
    398     hangingGet.open("GET", server + "/wait?peer_id=" + myId, true);
    399     hangingGet.send();  
    400   } catch (e) {
    401     trace_exception(e, "Start hanging get error");
    402   }
    403 }
    404 
    405 function sendToPeer(peer_id, data) {
    406   if (myId == -1) {
    407     alert("Not connected.");
    408     return;
    409   }
    410   if (peer_id == myId) {
    411     alert("Can't send a message to oneself.");
    412     return;
    413   }
    414   var r = new XMLHttpRequest();
    415   r.open("POST", server + "/message?peer_id=" + myId + "&to=" + peer_id, false);
    416   r.setRequestHeader("Content-Type", "text/plain");
    417   r.send(data);
    418   r = null;
    419 }
    420 
    421 function signInCallback() {
    422   try {
    423     if (request.readyState == 4) {
    424       if (request.status == 200) {
    425         var peers = request.responseText.split("\n");
    426         myId = parseInt(peers[0].split(",")[1]);
    427         trace("My id: " + myId);
    428         clearPeerList();
    429         var added = 0;
    430         for (var i = 1; i < peers.length; ++i) {
    431           if (peers[i].length > 0) {
    432             trace("Peer " + i + ": " + peers[i]);
    433             var parsed = peers[i].split(",");
    434             addPeer(parseInt(parsed[1]), parsed[0]);
    435             ++added;
    436           }
    437         }
    438         if (added == 0)
    439           addPeer(-1, "No other peer connected");
    440         else {
    441           document.getElementById("peers").disabled = false;
    442           document.getElementById("call").disabled = false;
    443         }
    444         startHangingGet();
    445         request = null;
    446         document.getElementById("connect").disabled = true;
    447         document.getElementById("disconnect").disabled = false;
    448       }
    449     }
    450   } catch (e) {
    451     trace_exception(e, "Sign in error");
    452     document.getElementById("connect").disabled = false;
    453   }
    454 }
    455 
    456 function signIn() {
    457   try {
    458     request = new XMLHttpRequest();
    459     request.onreadystatechange = signInCallback;
    460     request.open("GET", server + "/sign_in?" + myName, true);
    461     request.send();
    462   } catch (e) {
    463     trace_exception(e, "Start sign in error");
    464     document.getElementById("connect").disabled = false;
    465   }
    466 }
    467 
    468 function connect() {
    469   myName = document.getElementById("local").value.toLowerCase();
    470   server = document.getElementById("server").value.toLowerCase();
    471   if (myName.length == 0) {
    472     alert("I need a name please.");
    473     document.getElementById("local").focus();
    474   } else {
    475     // TODO: Disable connect button here, but we need a timeout and check if we
    476     // have connected, if so enable it again.
    477     signIn();
    478   }
    479 }
    480 
    481 function disconnect() {
    482   if (callState == 1)
    483     hangUp();
    484 
    485   disconnecting = true;
    486   
    487   if (request) {
    488     request.abort();
    489     request = null;
    490   }
    491 
    492   if (hangingGet) {
    493     hangingGet.abort();
    494     hangingGet = null;
    495   }
    496 
    497   if (myId != -1) {
    498     request = new XMLHttpRequest();
    499     request.open("GET", server + "/sign_out?peer_id=" + myId, false);
    500     request.send();
    501     request = null;
    502     myId = -1;
    503   }
    504 
    505   clearPeerList();
    506   addPeer(-1, "Not connected");
    507   document.getElementById("connect").disabled = false;
    508   document.getElementById("disconnect").disabled = true;
    509   document.getElementById("peers").disabled = true;
    510   document.getElementById("call").disabled = true;
    511 
    512   disconnecting = false;
    513 }
    514 
    515 
    516 // Window event handling
    517 
    518 window.onload = getUserMedia;
    519 window.onbeforeunload = disconnect;
    520 
    521 
    522 </script>
    523 </head>
    524 
    525 <body>
    526 <h1>WebRTC</h1>
    527 You must have a WebRTC capable browser in order to make calls using this test
    528 page.<br>&nbsp;
    529 
    530 <table border="0">
    531 <tr>
    532  <td>Local Preview</td>
    533  <td>Remote Video</td>
    534 </tr>
    535 <tr>
    536  <td>
    537   <video width="320" height="240" id="localView" autoplay="autoplay"></video>
    538  </td>
    539  <td>
    540   <video width="640" height="480" id="remoteView" autoplay="autoplay"></video>
    541  </td>
    542 </tr>
    543 </table>
    544 
    545 <table border="0">
    546 <tr>
    547  <td valign="top">
    548   <table border="0" cellpaddning="0" cellspacing="0">
    549   <tr>
    550    <td>Server:</td>
    551    <td>
    552     <input type="text" id="server" size="30" value="http://localhost:8888"/>
    553    </td>
    554   </tr>
    555   <tr>
    556    <td>Name:</td><td><input type="text" id="local" size="30" value="name"/></td>
    557   </tr>
    558   </table>
    559  </td>
    560  <td valign="top">
    561   <button id="connect" onclick="connect();">Connect</button><br>
    562   <button id="disconnect" onclick="disconnect();" disabled="true">Disconnect
    563   </button>
    564  </td>
    565  <td>&nbsp;&nbsp;&nbsp;</td>
    566  <td valign="top">
    567   Connected peers:<br>
    568   <select id="peers" size="5" disabled="true">
    569    <option value="-1">Not connected</option>
    570   </select>
    571   </td>
    572  <td valign="top">
    573   <!--input type="text" id="peer_id" size="3" value="1"/><br-->
    574   <button id="call" onclick="doCall();" disabled="true">Call</button><br>
    575   <button id="hangup" onclick="hangUp();" disabled="true">Hang up</button><br>
    576  </td>
    577  <td>&nbsp;&nbsp;&nbsp;</td>
    578  <td valign="top">
    579   <button onclick="toggleExtraButtons();">Toggle extra buttons (debug)</button>
    580   <br>
    581   <button id="createPcBtn" onclick="createPeerConnection();" hidden="true">
    582   Create peer connection</button>
    583  </td>
    584 </tr>
    585 </table>
    586 
    587 <button onclick="document.getElementById('debug').innerHTML='';">Clear log
    588 </button>
    589 <pre id="debug"></pre>
    590 
    591 </body>
    592 
    593 </html>
    594 
    595