blob: 300884cd26d0002bc629b874363a517244c55857 [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!--
Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
Use of this source code is governed by a BSD-style license
that can be found in the LICENSE file in the root of the source
tree. An additional intellectual property rights grant can be found
in the file PATENTS. All contributing project authors may
be found in the AUTHORS file in the root of the source tree.
-->
<html>
<head>
<title>WebRTC Test</title>
<style type="text/css">
body, input, button, select, table {
font-family:"Lucida Grande", "Lucida Sans", Verdana, Arial, sans-serif;
font-size: 13 px;
}
body, input:enable, button:enable, select:enable, table {
color: rgb(51, 51, 51);
}
h1 {font-size: 40 px;}
</style>
<script type="text/javascript">
// TODO: Catch more exceptions
var server;
var myId = -1;
var myName;
var remoteId = -1;
var remoteName;
var request = null;
var hangingGet = null;
var pc = null;
var localStream = null;
var disconnecting = false;
var callState = 0; // 0 - Not started, 1 - Call ongoing
// General
function setElementValuesFromURL() {
window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,
function(m, key, value) {
document.getElementById(key).value = unescape(value);
});
}
function trace(txt) {
var elem = document.getElementById("debug");
elem.innerHTML += txt + "<br>";
}
function trace_warning(txt) {
var wtxt = "<b>" + txt + "</b>";
trace(wtxt);
}
function trace_exception(e, txt) {
var etxt = "<b>" + txt + "</b> (" + e.name + " / " + e.message + ")";
trace(etxt);
}
function setCallState(state) {
trace("Changing call state: " + callState + " -> " + state);
callState = state;
}
function checkPeerConnection() {
if (!pc) {
trace_warning("No PeerConnection object exists");
return 0;
}
return 1;
}
// Local stream generation
function gotStream(s) {
var url = webkitURL.createObjectURL(s);
document.getElementById("localView").src = url;
trace("User has granted access to local media. url = " + url);
localStream = s;
}
function gotStreamFailed(error) {
alert("Failed to get access to local media. Error code was " + error.code +
".");
trace_warning("Failed to get access to local media. Error code was " +
error.code);
}
function getUserMedia() {
try {
navigator.webkitGetUserMedia("video,audio", gotStream, gotStreamFailed);
trace("Requested access to local media");
} catch (e) {
trace_exception(e, "getUserMedia error");
}
}
// Peer list and remote peer handling
function peerExists(id) {
try {
var peerList = document.getElementById("peers");
for (var i = 0; i < peerList.length; i++) {
if (parseInt(peerList.options[i].value) == id)
return true;
}
} catch (e) {
trace_exception(e, "Error searching for peer");
}
return false;
}
function addPeer(id, pname) {
try {
var peerList = document.getElementById("peers");
var option = document.createElement("option");
option.text = pname;
option.value = id;
peerList.add(option, null);
} catch (e) {
trace_exception(e, "Error adding peer");
}
}
function removePeer(id) {
try {
var peerList = document.getElementById("peers");
for (var i = 0; i < peerList.length; i++) {
if (parseInt(peerList.options[i].value) == id) {
peerList.remove(i);
break;
}
}
} catch (e) {
trace_exception(e, "Error removing peer");
}
}
function clearPeerList() {
var peerList = document.getElementById("peers");
while (peerList.length > 0)
peerList.remove(0);
}
function setSelectedPeer(id) {
try {
var peerList = document.getElementById("peers");
for (var i = 0; i < peerList.length; i++) {
if (parseInt(peerList.options[i].value) == id) {
peerList.options[i].selected = true;
return true;
}
}
} catch (e) {
trace_exception(e, "Error setting selected peer");
}
return false;
}
function getPeerName(id) {
try {
var peerList = document.getElementById("peers");
for (var i = 0; i < peerList.length; i++) {
if (parseInt(peerList.options[i].value) == id) {
return peerList.options[i].text;
}
}
} catch (e) {
trace_exception(e, "Error finding peer name");
return;
}
return;
}
function storeRemoteInfo() {
try {
var peerList = document.getElementById("peers");
if (peerList.selectedIndex < 0) {
alert("Please select a peer.");
return false;
} else
remoteId = parseInt(peerList.options[peerList.selectedIndex].value);
remoteName = peerList.options[peerList.selectedIndex].text;
} catch (e) {
trace_exception(e, "Error storing remote peer info");
return false;
}
return true;
}
// Call control
function createPeerConnection() {
if (pc) {
trace_warning("PeerConnection object already exists");
}
trace("Creating PeerConnection object");
try {
pc = new webkitPeerConnection("STUN stun.l.google.com:19302",
onSignalingMessage);
pc.onaddstream = onAddStream;
pc.onremovestream = onRemoveStream;
} catch (e) {
trace_exception(e, "Create PeerConnection error");
}
}
function doCall() {
if (!storeRemoteInfo())
return;
document.getElementById("call").disabled = true;
document.getElementById("peers").disabled = true;
createPeerConnection();
trace("Adding stream");
pc.addStream(localStream);
document.getElementById("hangup").disabled = false;
setCallState(1);
}
function hangUp() {
document.getElementById("hangup").disabled = true;
trace("Sending BYE to " + remoteName + " (ID " + remoteId + ")");
sendToPeer(remoteId, "BYE");
closeCall();
}
function closeCall() {
trace("Stopping showing remote stream");
document.getElementById("remoteView").src = "dummy";
if (pc) {
trace("Stopping call [pc.close()]");
pc.close();
pc = null;
} else
trace("No pc object to close");
remoteId = -1;
document.getElementById("call").disabled = false;
document.getElementById("peers").disabled = false;
setCallState(0);
}
// PeerConnection callbacks
function onAddStream(e) {
var stream = e.stream;
var url = webkitURL.createObjectURL(stream);
document.getElementById("remoteView").src = url;
trace("Started showing remote stream. url = " + url);
}
function onRemoveStream(e) {
// Currently if we get this callback, call has ended.
document.getElementById("remoteView").src = "";
trace("Stopped showing remote stream");
}
function onSignalingMessage(msg) {
trace("Sending message to " + remoteName + " (ID " + remoteId + "):\n" + msg);
sendToPeer(remoteId, msg);
}
// TODO: Add callbacks onconnecting, onopen and onstatechange.
// Server interaction
function handleServerNotification(data) {
trace("Server notification: " + data);
var parsed = data.split(",");
if (parseInt(parsed[2]) == 1) { // New peer
var peerId = parseInt(parsed[1]);
if (!peerExists(peerId)) {
var peerList = document.getElementById("peers");
if (peerList.length == 1 && peerList.options[0].value == -1)
clearPeerList();
addPeer(peerId, parsed[0]);
document.getElementById("peers").disabled = false;
document.getElementById("call").disabled = false;
}
} else if (parseInt(parsed[2]) == 0) { // Removed peer
removePeer(parseInt(parsed[1]));
if (document.getElementById("peers").length == 0) {
document.getElementById("peers").disabled = true;
addPeer(-1, "No other peer connected");
}
}
}
function handlePeerMessage(peer_id, msg) {
var peerName = getPeerName(peer_id);
if (peerName == undefined) {
trace_warning("Received message from unknown peer (ID " + peer_id +
"), ignoring message:");
trace(msg);
return;
}
trace("Received message from " + peerName + " (ID " + peer_id + "):\n" + msg);
// Assuming we receive the message from the peer we want to communicate with.
// TODO: Only accept messages from peer we communicate with with if call is
// ongoing.
if (msg.search("BYE") == 0) {
// Other side has hung up.
document.getElementById("hangup").disabled = true;
closeCall()
} else {
if (!pc) {
// Other side is calling us, startup
if (!setSelectedPeer(peer_id)) {
trace_warning("Recevied message from unknown peer, ignoring");
return;
}
if (!storeRemoteInfo())
return;
document.getElementById("call").disabled = true;
document.getElementById("peers").disabled = true;
createPeerConnection();
try {
pc.processSignalingMessage(msg);
} catch (e) {
trace_exception(e, "Process signaling message error");
}
trace("Adding stream");
pc.addStream(localStream);
document.getElementById("hangup").disabled = false;
} else {
try {
pc.processSignalingMessage(msg);
} catch (e) {
trace_exception(e, "Process signaling message error");
}
}
}
}
function getIntHeader(r, name) {
var val = r.getResponseHeader(name);
trace("header value: " + val);
return val != null && val.length ? parseInt(val) : -1;
}
function hangingGetCallback() {
try {
if (hangingGet.readyState != 4 || disconnecting)
return;
if (hangingGet.status != 200) {
trace_warning("server error, status: " + hangingGet.status + ", text: " +
hangingGet.statusText);
disconnect();
} else {
var peer_id = getIntHeader(hangingGet, "Pragma");
if (peer_id == myId) {
handleServerNotification(hangingGet.responseText);
} else {
handlePeerMessage(peer_id, hangingGet.responseText);
}
}
if (hangingGet) {
hangingGet.abort();
hangingGet = null;
}
if (myId != -1)
window.setTimeout(startHangingGet, 0);
} catch (e) {
trace_exception(e, "Hanging get error");
}
}
function onHangingGetTimeout() {
trace("hanging get timeout. issuing again");
hangingGet.abort();
hangingGet = null;
if (myId != -1)
window.setTimeout(startHangingGet, 0);
}
function startHangingGet() {
try {
hangingGet = new XMLHttpRequest();
hangingGet.onreadystatechange = hangingGetCallback;
hangingGet.ontimeout = onHangingGetTimeout;
hangingGet.open("GET", server + "/wait?peer_id=" + myId, true);
hangingGet.send();
} catch (e) {
trace_exception(e, "Start hanging get error");
}
}
function sendToPeer(peer_id, data) {
if (myId == -1) {
alert("Not connected.");
return;
}
if (peer_id == myId) {
alert("Can't send a message to oneself.");
return;
}
var r = new XMLHttpRequest();
r.open("POST", server + "/message?peer_id=" + myId + "&to=" + peer_id, false);
r.setRequestHeader("Content-Type", "text/plain");
r.send(data);
r = null;
}
function signInCallback() {
try {
if (request.readyState == 4) {
if (request.status == 200) {
var peers = request.responseText.split("\n");
myId = parseInt(peers[0].split(",")[1]);
trace("My id: " + myId);
clearPeerList();
var added = 0;
for (var i = 1; i < peers.length; ++i) {
if (peers[i].length > 0) {
trace("Peer " + i + ": " + peers[i]);
var parsed = peers[i].split(",");
addPeer(parseInt(parsed[1]), parsed[0]);
++added;
}
}
if (added == 0)
addPeer(-1, "No other peer connected");
else {
document.getElementById("peers").disabled = false;
document.getElementById("call").disabled = false;
}
startHangingGet();
request = null;
document.getElementById("connect").disabled = true;
document.getElementById("disconnect").disabled = false;
}
}
} catch (e) {
trace_exception(e, "Sign in error");
document.getElementById("connect").disabled = false;
}
}
function signIn() {
try {
request = new XMLHttpRequest();
request.onreadystatechange = signInCallback;
request.open("GET", server + "/sign_in?" + myName, true);
request.send();
} catch (e) {
trace_exception(e, "Start sign in error");
document.getElementById("connect").disabled = false;
}
}
function connect() {
myName = document.getElementById("name").value.toLowerCase();
server = document.getElementById("server").value.toLowerCase();
if (myName.length == 0) {
alert("I need a name please.");
document.getElementById("local").focus();
} else {
// TODO: Disable connect button here, but we need a timeout and check if we
// have connected, if so enable it again.
signIn();
}
}
function disconnect() {
if (callState == 1)
hangUp();
disconnecting = true;
if (request) {
request.abort();
request = null;
}
if (hangingGet) {
hangingGet.abort();
hangingGet = null;
}
if (myId != -1) {
request = new XMLHttpRequest();
request.open("GET", server + "/sign_out?peer_id=" + myId, false);
request.send();
request = null;
myId = -1;
}
clearPeerList();
addPeer(-1, "Not connected");
document.getElementById("connect").disabled = false;
document.getElementById("disconnect").disabled = true;
document.getElementById("peers").disabled = true;
document.getElementById("call").disabled = true;
disconnecting = false;
}
// Window event handling
window.onload = function() {
if (navigator.webkitGetUserMedia) {
document.getElementById('testApp').hidden = false;
setElementValuesFromURL();
getUserMedia();
} else {
document.getElementById('errorText').hidden = false;
}
}
window.onbeforeunload = disconnect;
</script>
</head>
<body>
<h1>WebRTC</h1>
<section id="errorText" hidden="true">
Could not detect WebRTC support.<p>
You must have WebRTC enabled Chrome browser to use this test page. The browser
must be started with the --enable-media-stream command line flag. For more
information, please see
<a href="https://sites.google.com/site/webrtc/blog/webrtcnowavailableinthechromedevchannel">
this blog post</a>.
</section>
<section id="testApp" hidden="true">
<table border="0">
<tr>
<td>Local Preview</td>
<td>Remote Video</td>
</tr>
<tr>
<td>
<video width="320" height="240" id="localView" autoplay="autoplay"></video>
</td>
<td>
<video width="640" height="480" id="remoteView" autoplay="autoplay"></video>
</td>
</tr>
</table>
<table border="0">
<tr>
<td valign="top">
<table border="0" cellpaddning="0" cellspacing="0">
<tr>
<td>Server:</td>
<td>
<input type="text" id="server" size="30" value="http://localhost:8888"/>
</td>
</tr>
<tr>
<td>Name:</td><td><input type="text" id="name" size="30" value="name"/></td>
</tr>
</table>
</td>
<td valign="top">
<button id="connect" onclick="connect();">Connect</button><br>
<button id="disconnect" onclick="disconnect();" disabled="true">Disconnect
</button>
</td>
<td>&nbsp;&nbsp;&nbsp;</td>
<td valign="top">
Connected peers:<br>
<select id="peers" size="5" disabled="true">
<option value="-1">Not connected</option>
</select>
</td>
<td valign="top">
<!--input type="text" id="peer_id" size="3" value="1"/><br-->
<button id="call" onclick="doCall();" disabled="true">Call</button><br>
<button id="hangup" onclick="hangUp();" disabled="true">Hang up</button><br>
</td>
</tr>
</table>
<button onclick="document.getElementById('debug').innerHTML='';">Clear log
</button>
<pre id="debug"></pre>
</section>
</body>
</html>