6a8147519c
Review URL: https://webrtc-codereview.appspot.com/365001 git-svn-id: http://webrtc.googlecode.com/svn/trunk@1494 4adac7df-926f-26a2-2b94-8c16560cd09d
603 lines
15 KiB
HTML
603 lines
15 KiB
HTML
<!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> </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>
|
|
|