Compare commits

..

10 Commits

Author SHA1 Message Date
shane tully a171e044b1 replace deprecated addStream method with addTrack 2023-06-28 20:13:58 -07:00
shane tully dec89dac53 fix #29: add playsinline for mobile devices 2023-05-24 22:13:40 -07:00
shane tully cd7177d2d6 update syntax to es6 2023-05-24 21:53:45 -07:00
shane tully 93d28dd9c0 add instructions to client html 2023-05-24 21:53:33 -07:00
shane tully f0bcf2615a update packages 2023-05-24 21:53:05 -07:00
shane tully bd7ea806ac update readme documentation 2023-05-24 21:52:47 -07:00
shane tully dbcb4ba82d update cert 2023-05-24 21:52:30 -07:00
Mauricio de Oliveira Pena 5f67119e4e Updated readme. 2018-08-24 00:40:04 -07:00
Yoshihiro Kikuchi 147a02d695 fix stun server url 2018-03-30 18:35:05 +09:00
shane tully a5967c46f5 fix #27: update stun servers 2018-03-12 00:10:48 -07:00
9 changed files with 217 additions and 190 deletions

View File

@ -1,8 +1,6 @@
WebRTC Example WebRTC Example
============== ==============
#### shane tully (shanetully.com)
An 'as simple as it gets' WebRTC example. An 'as simple as it gets' WebRTC example.
See [https://shanetully.com/2014/09/a-dead-simple-webrtc-example/](https://shanetully.com/2014/09/a-dead-simple-webrtc-example/) for a detailed walkthrough of the code. See [https://shanetully.com/2014/09/a-dead-simple-webrtc-example/](https://shanetully.com/2014/09/a-dead-simple-webrtc-example/) for a detailed walkthrough of the code.
@ -18,18 +16,17 @@ $ npm install
$ npm start $ npm start
``` ```
With the server running, open a recent version of Firefox or Chrome and visit `https://localhost:8443`. With the server running, open Firefox/Chrome/Safari and visit `https://localhost:8443`.
Please note the following:
* Note the HTTPS! There is no redirect from HTTP to HTTPS. * Note the HTTPS! There is no redirect from HTTP to HTTPS.
* You\'ll need to accept the invalid TLS certificate as it is self-signed and WebRTC must be run over TLS.
* Some browsers or OSs may not allow the webcam to be used by multiple pages at once. You may need to use two different browsers or machines. * Some browsers or OSs may not allow the webcam to be used by multiple pages at once. You may need to use two different browsers or machines.
## TLS
Recent versions of Chrome require secure websockets for WebRTC. Thus, this example utilizes HTTPS. Included is a self-signed certificate that must be accepted in the browser for the example to work.
## Problems? ## Problems?
WebRTC is a rapidly evolving beast. Being an example that I don't check often, I rely on users for reports if something breaks. Issues and pull requests are greatly appreciated. This is a short example that I don't check often. As such, I rely on users for reports if something breaks. Issues and pull requests are greatly appreciated.
## License ## License
@ -54,4 +51,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.

View File

@ -1,32 +1,32 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIFiDCCA3CgAwIBAgIJAMSEu/mdFhkUMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV MIIFkzCCA3ugAwIBAgIUD4Vfmfwa3e7F34wp7Wl08mOXqRcwDQYJKoZIhvcNAQEL
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xODAxMDcyMzQz GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X
MzRaFw0yODAxMDUyMzQzMzRaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l DTIzMDUyNTA0MDAyOVoXDTMzMDUyMjA0MDAyOVowWTELMAkGA1UEBhMCQVUxEzAR
LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
BAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMrq IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
0a9LjifYP7UEn5znTCatkSMlRERb+D2Nj0JfmGki5z2E/HOS3lpoBfIFqak+/+AZ MIICCgKCAgEAiKvigFwEG4ahwAUAEoBw3p8ivp5fTYsMBGN6Zw3BjCJNuDB6Dhhv
8Xpg7EwNRZtrUd1ubanHQocl2vCLPPSNkoUavYojIPKM0OkGaCwt7fu2Igoch080 Opin2G0XjJWsii9RR09MJbdDgT2PBvnvbNkUZwcxcIdXOlIA/krC2NVz0u8VZeMi
m1VyOCB1VX1pl/k+0lcNMlE4mBczg3HBUJL+G21zjD9T4TalyGYKrZ1wZMJ/zxT6 z+6tg2sgR7/HqdDoeqw7OTAErgU7lwA28C7tGydgSbOY+8KrPH+OXRoSzZC5pE7I
4GejjQI8ivLWFRrNqNXUeYDfSDhmrRCvIBjeV3Q+eJB5sb7LH4f3egaDgkaDc5Xs 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
KKNHDiqV4JiP4WWFN+pc2lOVaLJwTe4FrbG7AA== 2RTfZORnstT4Nuybe6l1O4iECDOgDqIeoWxREZCCMxu2VQnZ1B9l
-----END CERTIFICATE----- -----END CERTIFICATE-----

View File

@ -6,12 +6,16 @@
</head> </head>
<body> <body>
<video id="localVideo" autoplay muted style="width:40%;"></video> <video id="localVideo" autoplay playsinline muted style="width:40%;"></video>
<video id="remoteVideo" autoplay style="width:40%;"></video> <video id="remoteVideo" autoplay playsinline style="width:40%;"></video>
<br /> <div style="margin-top: 10px;">
Open this page in a second browser window then click below to start the WebRTC connection.
</div>
<input type="button" id="start" onclick="start(true)" value="Start Video"></input> <div style="margin-top: 10px;">
<input type="button" id="start" onclick="start(true)" value="Start WebRTC"></input>
</div>
<script type="text/javascript"> <script type="text/javascript">
pageReady(); pageReady();

View File

@ -1,48 +1,54 @@
var localVideo; let localStream;
var localStream; let localVideo;
var remoteVideo; let peerConnection;
var peerConnection; let remoteVideo;
var uuid; let serverConnection;
var serverConnection; let uuid;
var peerConnectionConfig = { const peerConnectionConfig = {
'iceServers': [ 'iceServers': [
{'urls': 'stun:stun.services.mozilla.com'}, {'urls': 'stun:stun.stunprotocol.org:3478'},
{'urls': 'stun:stun.l.google.com:19302'}, {'urls': 'stun:stun.l.google.com:19302'},
] ]
}; };
function pageReady() { async function pageReady() {
uuid = createUUID(); uuid = createUUID();
localVideo = document.getElementById('localVideo'); localVideo = document.getElementById('localVideo');
remoteVideo = document.getElementById('remoteVideo'); remoteVideo = document.getElementById('remoteVideo');
serverConnection = new WebSocket('wss://' + window.location.hostname + ':8443'); serverConnection = new WebSocket(`wss://${window.location.hostname}:8443`);
serverConnection.onmessage = gotMessageFromServer; serverConnection.onmessage = gotMessageFromServer;
var constraints = { const constraints = {
video: true, video: true,
audio: true, audio: true,
}; };
if(navigator.mediaDevices.getUserMedia) { if(!navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(constraints).then(getUserMediaSuccess).catch(errorHandler);
} else {
alert('Your browser does not support getUserMedia API'); alert('Your browser does not support getUserMedia API');
return;
} }
}
function getUserMediaSuccess(stream) { try {
localStream = stream; const stream = await navigator.mediaDevices.getUserMedia(constraints);
localVideo.srcObject = stream;
localStream = stream;
localVideo.srcObject = stream;
} catch(error) {
errorHandler(error);
}
} }
function start(isCaller) { function start(isCaller) {
peerConnection = new RTCPeerConnection(peerConnectionConfig); peerConnection = new RTCPeerConnection(peerConnectionConfig);
peerConnection.onicecandidate = gotIceCandidate; peerConnection.onicecandidate = gotIceCandidate;
peerConnection.ontrack = gotRemoteStream; peerConnection.ontrack = gotRemoteStream;
peerConnection.addStream(localStream);
for(const track of localStream.getTracks()) {
peerConnection.addTrack(track, localStream);
}
if(isCaller) { if(isCaller) {
peerConnection.createOffer().then(createdDescription).catch(errorHandler); peerConnection.createOffer().then(createdDescription).catch(errorHandler);
@ -52,17 +58,17 @@ function start(isCaller) {
function gotMessageFromServer(message) { function gotMessageFromServer(message) {
if(!peerConnection) start(false); if(!peerConnection) start(false);
var signal = JSON.parse(message.data); const signal = JSON.parse(message.data);
// Ignore messages from ourself // Ignore messages from ourself
if(signal.uuid == uuid) return; if(signal.uuid == uuid) return;
if(signal.sdp) { if(signal.sdp) {
peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp)).then(function() { peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp)).then(() => {
// Only create answers in response to offers // Only create answers in response to offers
if(signal.sdp.type == 'offer') { if(signal.sdp.type !== 'offer') return;
peerConnection.createAnswer().then(createdDescription).catch(errorHandler);
} peerConnection.createAnswer().then(createdDescription).catch(errorHandler);
}).catch(errorHandler); }).catch(errorHandler);
} else if(signal.ice) { } else if(signal.ice) {
peerConnection.addIceCandidate(new RTCIceCandidate(signal.ice)).catch(errorHandler); peerConnection.addIceCandidate(new RTCIceCandidate(signal.ice)).catch(errorHandler);
@ -78,7 +84,7 @@ function gotIceCandidate(event) {
function createdDescription(description) { function createdDescription(description) {
console.log('got description'); console.log('got description');
peerConnection.setLocalDescription(description).then(function() { peerConnection.setLocalDescription(description).then(() => {
serverConnection.send(JSON.stringify({'sdp': peerConnection.localDescription, 'uuid': uuid})); serverConnection.send(JSON.stringify({'sdp': peerConnection.localDescription, 'uuid': uuid}));
}).catch(errorHandler); }).catch(errorHandler);
} }
@ -99,5 +105,5 @@ function createUUID() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
} }
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4() + s4() + s4()}`;
} }

View File

@ -1,3 +1,3 @@
#!/bin/bash #!/bin/bash
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 3650 -nodes

100
key.pem
View File

@ -1,52 +1,52 @@
-----BEGIN PRIVATE KEY----- -----BEGIN PRIVATE KEY-----
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
BfXBs2eSpBE70GwrztgDS2fco6tzQQ== ZEbffYj4hKrpVVuPFYwKzuLcXP1vTFQ=
-----END PRIVATE KEY----- -----END PRIVATE KEY-----

59
package-lock.json generated
View File

@ -1,33 +1,44 @@
{ {
"name": "WebRTC-Example", "name": "WebRTC-Example",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 1, "lockfileVersion": 2,
"requires": true, "requires": true,
"dependencies": { "packages": {
"async-limiter": { "": {
"name": "WebRTC-Example",
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", "license": "MIT",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" "dependencies": {
}, "ws": "^8.13.0"
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
},
"ws": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
"requires": {
"async-limiter": "1.0.0",
"safe-buffer": "5.1.1",
"ultron": "1.1.1"
} }
},
"node_modules/ws": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
},
"dependencies": {
"ws": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"requires": {}
} }
} }
} }

View File

@ -17,8 +17,8 @@
"bugs": { "bugs": {
"url": "https://github.com/shanet/WebRTC-Example/issues" "url": "https://github.com/shanet/WebRTC-Example/issues"
}, },
"homepage": "https://github.com/shanet/WebRTC-Example#readme", "homepage": "https://github.com/shanet/WebRTC-Example",
"dependencies": { "dependencies": {
"ws": "^3.3.2" "ws": "^8.13.0"
} }
} }

View File

@ -5,55 +5,65 @@ const https = require('https');
const WebSocket = require('ws'); const WebSocket = require('ws');
const WebSocketServer = WebSocket.Server; const WebSocketServer = WebSocket.Server;
// Yes, TLS is required // Yes, TLS is required for WebRTC
const serverConfig = { const serverConfig = {
key: fs.readFileSync('key.pem'), key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'), cert: fs.readFileSync('cert.pem'),
}; };
// ---------------------------------------------------------------------------------------- function main() {
const httpsServer = startHttpsServer(serverConfig);
startWebSocketServer(httpsServer);
printHelp();
}
// Create a server for the client html page function startHttpsServer(serverConfig) {
const handleRequest = function(request, response) { // Handle incoming requests from the client
// Render the single client html file for any request the HTTP server receives const handleRequest = (request, response) => {
console.log('request received: ' + request.url); console.log(`request received: ${request.url}`);
if(request.url === '/') { // This server only serves two files: The HTML page and the client JS file
response.writeHead(200, {'Content-Type': 'text/html'}); if(request.url === '/') {
response.end(fs.readFileSync('client/index.html')); response.writeHead(200, {'Content-Type': 'text/html'});
} else if(request.url === '/webrtc.js') { response.end(fs.readFileSync('client/index.html'));
response.writeHead(200, {'Content-Type': 'application/javascript'}); } else if(request.url === '/webrtc.js') {
response.end(fs.readFileSync('client/webrtc.js')); response.writeHead(200, {'Content-Type': 'application/javascript'});
} response.end(fs.readFileSync('client/webrtc.js'));
};
const httpsServer = https.createServer(serverConfig, handleRequest);
httpsServer.listen(HTTPS_PORT, '0.0.0.0');
// ----------------------------------------------------------------------------------------
// Create a server for handling websocket calls
const wss = new WebSocketServer({server: httpsServer});
wss.on('connection', function(ws) {
ws.on('message', function(message) {
// Broadcast any received message to all clients
console.log('received: %s', message);
wss.broadcast(message);
});
});
wss.broadcast = function(data) {
this.clients.forEach(function(client) {
if(client.readyState === WebSocket.OPEN) {
client.send(data);
} }
}); };
};
console.log('Server running. Visit https://localhost:' + HTTPS_PORT + ' in Firefox/Chrome.\n\n\ const httpsServer = https.createServer(serverConfig, handleRequest);
Some important notes:\n\ httpsServer.listen(HTTPS_PORT, '0.0.0.0');
* Note the HTTPS; there is no HTTP -> HTTPS redirect.\n\ return httpsServer;
* You\'ll also need to accept the invalid TLS certificate.\n\ }
* Some browsers or OSs may not allow the webcam to be used by multiple pages at once. You may need to use two different browsers or machines.\n'
); function startWebSocketServer(httpsServer) {
// Create a server for handling websocket calls
const wss = new WebSocketServer({server: httpsServer});
wss.on('connection', (ws) => {
ws.on('message', (message) => {
// Broadcast any received message to all clients
console.log(`received: ${message}`);
wss.broadcast(message);
});
});
wss.broadcast = function(data) {
this.clients.forEach((client) => {
if(client.readyState === WebSocket.OPEN) {
client.send(data, {binary: false});
}
});
};
}
function printHelp() {
console.log(`Server running. Visit https://localhost:${HTTPS_PORT} in Firefox/Chrome/Safari.\n`);
console.log('Please note the following:');
console.log(' * Note the HTTPS in the URL; there is no HTTP -> HTTPS redirect.');
console.log(' * You\'ll need to accept the invalid TLS certificate as it is self-signed.');
console.log(' * Some browsers or OSs may not allow the webcam to be used by multiple pages at once. You may need to use two different browsers or machines.');
}
main();