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
==============
#### shane tully (shanetully.com)
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.
@ -18,18 +16,17 @@ $ npm install
$ 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.
* 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.
## 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?
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
@ -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,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,32 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFiDCCA3CgAwIBAgIJAMSEu/mdFhkUMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xODAxMDcyMzQz
MzRaFw0yODAxMDUyMzQzMzRaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l
LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
BAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMrq
0a9LjifYP7UEn5znTCatkSMlRERb+D2Nj0JfmGki5z2E/HOS3lpoBfIFqak+/+AZ
8Xpg7EwNRZtrUd1ubanHQocl2vCLPPSNkoUavYojIPKM0OkGaCwt7fu2Igoch080
m1VyOCB1VX1pl/k+0lcNMlE4mBczg3HBUJL+G21zjD9T4TalyGYKrZ1wZMJ/zxT6
4GejjQI8ivLWFRrNqNXUeYDfSDhmrRCvIBjeV3Q+eJB5sb7LH4f3egaDgkaDc5Xs
iRjV8Fwcf4AtFQknAkASKp0gbiIhgleqCEPQRnjXYShRtVGek86lkgvJLbIP+eQK
krMBVZF76tMFuNjkdm2ImklB+1efTHswBwyUT8mb06qQnGRCOZ5EiYeZ+urWobSd
SlKxz3FSS32jn1KkvRyxaXrTdCGrGvdo/qawr/i/i2b03tng7masTomhCrN+YQBo
QjWqS+IborGfLd36Yb4JiQqWhWrlzzFK7NyGEU6ueY1lifSsaqxnyqnjfEIGVhVe
6tv1IHsiqilLoUR9S7zybQW8y0S3gnuyYyf/k4MyF7XhT+Bo0mvOm8HpxR0trtq7
b7tjGvghzA+wzgIAo96srtcwTB+cJGsop9eR3B5bYyYjYSDtNoN7YHyUyuC+HPKR
DE/0EFIpj/W3YQfbScoOGougm6l6lL6qG8vYIj33AgMBAAGjUzBRMB0GA1UdDgQW
BBTDQsMbFk928O798FBfgoi4IyA4ajAfBgNVHSMEGDAWgBTDQsMbFk928O798FBf
goi4IyA4ajAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQANlBsU
gZ/1u5mN1/2QjU0V2tXl1ZvtJOk5TBy1bdNFiCT+mZNVtpAlejl6LEr/2P2XEGTQ
D7NQTrQwCKTRiE3idST2jesYTLxzyIdEDB1hLVFoxsuPXo4+y4M3Qy/BvLFVoDQW
AqEfl9dDPq+urNYvvBDth9zHwyPwvW2gIGQRsIGvZ60waHTWWlFSBB53T+JNQK3k
aNXhpXjyrlWkVfZteplhAg98qSqiYKrlfLbTCFzvF7SrwB81Ox8miYtDNfFO8uL3
Gk0vNCVZjBPl2/VkCanPBjIPKPtfIgVa6ZjXFzW88gqvkfFoSyr7Nqf59jM+4IOY
C4o47jFAPVXeIKGZDXeu5KLlFngdQ3TCKCTT79oJCn1354kOmBMsKKbVG1G2mAy6
ngpkAIQ4Bsul3dHK6kpoAzI8/cRD3p7vIuwq46GTFz47iq9M3FZxWu3F5j5XdLvt
+43iN24xKlHc5AfAtPGdqbozmZ4INhL2sm9HvIMUy1fnL1aYZpnM7KOpzEe+pN39
2B/GemUwVENRoCRv0qLadOQtCO/Nu4QJNV9gHteQ0rVLFAgbp1hqnjyYkLDmZ9WF
nC9GVS1yhVK+Wwgp8Xrcti4O0c0HtCNrfAMECUOL7QW7KV6wzr9ZdBvf0v2WvbbM
KKNHDiqV4JiP4WWFN+pc2lOVaLJwTe4FrbG7AA==
MIIFkzCCA3ugAwIBAgIUD4Vfmfwa3e7F34wp7Wl08mOXqRcwDQYJKoZIhvcNAQEL
BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X
DTIzMDUyNTA0MDAyOVoXDTMzMDUyMjA0MDAyOVowWTELMAkGA1UEBhMCQVUxEzAR
BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEAiKvigFwEG4ahwAUAEoBw3p8ivp5fTYsMBGN6Zw3BjCJNuDB6Dhhv
Opin2G0XjJWsii9RR09MJbdDgT2PBvnvbNkUZwcxcIdXOlIA/krC2NVz0u8VZeMi
z+6tg2sgR7/HqdDoeqw7OTAErgU7lwA28C7tGydgSbOY+8KrPH+OXRoSzZC5pE7I
GLAmuYLDR0xEKjMnOww9py0gmQsT/K4Gj188QfN1Pl7kd4qURSZXbfJ1cdZ9nIIu
DtdBb5gH1AUbM9er9pEC4MJMflp6aDgkpjsTPqI/l9NWuoI/1T5TCYobXl47D0rl
r/ycCAiywx4amO8/2kIgXwBAyAVp/x3KL76YkGvnsrOaG0FnHKnT43DpuU8bwhet
C9c3Y4Xwe4d2ZwypV1FecGm15dO3vs5r81SGjyYJ5JjvfzH2BQUHBzO+l1vQfr99
bM1nLL08Vg0VUtrf1nkYp8etZZ0C6OYBPlH74fG8iuS8Eiu4t6gM6yF+Ar9DmsPF
P+aqKovn5GqVFXmNDo4vxY44GiNiC8pclQVsGiH9GXpSKO1nCpaTtzI1tk1rHQW4
1P0vcEia6Nl0zNpfcdWyjvqEh+b5DalV3V8NxuhH8NKnfXK+9IpKEtCqYNRV1Ehm
9+uFT7b0wK4523spzi+isIG5mH49jD99w2NVAcS+3t+T2W7KSmdGtHUCAwEAAaNT
MFEwHQYDVR0OBBYEFNpwxUVFQcEvwhUpJxHpsJ1gQYV2MB8GA1UdIwQYMBaAFNpw
xUVFQcEvwhUpJxHpsJ1gQYV2MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggIBAFrbXtomKUIIVy3bLenwP6ntKuhYUFtLLl0w7hNzj5sg5UdqHOJXmY30
j7hcyJA+Xgyvyt2DTNMWMjn9nWu8CAn3uczPLDsjgYAq6hz3Z5UU+jhE8R+laJqE
SKRWQKcQgICnyTllGKg7XGx1Cq5Vpg4bZZNwHnn9YQbmOBICyH26wwgcQbi0ny0S
lhw1DytqsuarO1Opgf+521T9KyAlLtlWnhlpDc0axNJzXvLSLpNrYHWS6Bhob/a/
XA678uayXeOYv9D7nfCJYcpG9KHSb/TyCczf3yXR+eZPiITDiLnM41Wnl2TXJYJp
gJ0TBY1Nu6J8MCGE5qX77qMl8rCVrXOLy3X77AYfYJib26KsWmThYsYBwaaovk7R
d28ka4bBizzNCP4ivlcnqGXqwRghddMwtePEoJdA1C0o/qfORWW2RdBsZ5xQdWqu
NxJ4KGMCoPN4Zc2fgoQ7kXOJhpmjp4mIoVg3oXaOCIJsseU4SPvraWR7uS+CMDwf
2+zyEGrljirm0WZ1r+0t09OVLvWBvV9DSL0bR0wwGOS+sr+03GMDXTRlgd7SadAb
+xwK0A1O2byTyA4AulwCLFUI4+NP8RnaF0lw2jTuzqgqKnuHdoGLh7fbCon0Tsmm
2RTfZORnstT4Nuybe6l1O4iECDOgDqIeoWxREZCCMxu2VQnZ1B9l
-----END CERTIFICATE-----

View File

@ -6,12 +6,16 @@
</head>
<body>
<video id="localVideo" autoplay muted style="width:40%;"></video>
<video id="remoteVideo" autoplay style="width:40%;"></video>
<video id="localVideo" autoplay playsinline muted 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">
pageReady();

View File

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

59
package-lock.json generated
View File

@ -1,33 +1,44 @@
{
"name": "WebRTC-Example",
"version": "1.0.0",
"lockfileVersion": 1,
"lockfileVersion": 2,
"requires": true,
"dependencies": {
"async-limiter": {
"packages": {
"": {
"name": "WebRTC-Example",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
},
"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"
"license": "MIT",
"dependencies": {
"ws": "^8.13.0"
}
},
"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": {
"url": "https://github.com/shanet/WebRTC-Example/issues"
},
"homepage": "https://github.com/shanet/WebRTC-Example#readme",
"homepage": "https://github.com/shanet/WebRTC-Example",
"dependencies": {
"ws": "^3.3.2"
"ws": "^8.13.0"
}
}

View File

@ -5,55 +5,65 @@ const https = require('https');
const WebSocket = require('ws');
const WebSocketServer = WebSocket.Server;
// Yes, TLS is required
// Yes, TLS is required for WebRTC
const serverConfig = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
};
// ----------------------------------------------------------------------------------------
function main() {
const httpsServer = startHttpsServer(serverConfig);
startWebSocketServer(httpsServer);
printHelp();
}
// Create a server for the client html page
const handleRequest = function(request, response) {
// Render the single client html file for any request the HTTP server receives
console.log('request received: ' + request.url);
function startHttpsServer(serverConfig) {
// Handle incoming requests from the client
const handleRequest = (request, response) => {
console.log(`request received: ${request.url}`);
if(request.url === '/') {
response.writeHead(200, {'Content-Type': 'text/html'});
response.end(fs.readFileSync('client/index.html'));
} else if(request.url === '/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);
// This server only serves two files: The HTML page and the client JS file
if(request.url === '/') {
response.writeHead(200, {'Content-Type': 'text/html'});
response.end(fs.readFileSync('client/index.html'));
} else if(request.url === '/webrtc.js') {
response.writeHead(200, {'Content-Type': 'application/javascript'});
response.end(fs.readFileSync('client/webrtc.js'));
}
});
};
};
console.log('Server running. Visit https://localhost:' + HTTPS_PORT + ' in Firefox/Chrome.\n\n\
Some important notes:\n\
* Note the HTTPS; there is no HTTP -> HTTPS redirect.\n\
* 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'
);
const httpsServer = https.createServer(serverConfig, handleRequest);
httpsServer.listen(HTTPS_PORT, '0.0.0.0');
return httpsServer;
}
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();