Skip to content

Instantly share code, notes, and snippets.

@richtr
Last active August 29, 2015 13:57
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save richtr/9683905 to your computer and use it in GitHub Desktop.
Save richtr/9683905 to your computer and use it in GitHub Desktop.
BroadcastWebSocket: advertising, discovering and communicating between web pages and devices running in the current local network
<!DOCTYPE html>
<html>
<head>
<title>BroadcastWebSocket usage demo</title>
</head>
<body>
<h1>BroadcastWebSocket usage demo</h1>
<script>
// [Constructor(DOMString channel)]
// interface BroadcastWebSocket {
// // events
// attribute EventHandler onconnect; // provides WebSocket objects (one per event)
//
// // methods
// void terminate([Clamp] optional unsigned short code, optional DOMString reason);
// }
//
// Create an object that broadcasts the availability of the provided channel id in the
// local network and connect all existing BroadcastWebSocket services that share the same
// channel id within the local network.
//
// The network advertisement broadcast of this BroadcastWebSocket service could
// potentially be agnostic to the underlying Discovery Protocol used.
// i.e. it could be broadcast over SSDP, Zeroconf and/or DIAL by the UA
// as this mechanism is not exposed in the JavaScript API.
//
// The BroadcastWebSocket service type would be 'web+broadcast:<channel>' where <channel>
// is the channel name chosen by the developer.
//
// Here we build on top of the WebSocket interface rather than MessageChannel or MessagePort
// interfaces which do not have well-defined network communication or serialization
// features.
//
var authSocketHandler = new BroadcastWebSocket('auth' + (window.location.search || '?0')); // key the channel name by its URL parameters
var connectedSockets = [];
authSocketHandler.onconnect = function( webSocket ) {
connectedSockets.push( webSocket );
webSocket.onmessage = function (event) {
// we only want to allow same-origin cross-document communication in our app.
if( event.origin !== location.origin )
return;
if (event.data == 'logout') {
doLogout();
showLogout();
}
}
}
function logoutRequested() {
// called when the user asks us to log them out
doLogout();
showLogout();
for(var i in connectedSockets) {
connectedSockets[i].send('logout');
}
}
function doLogout() {
// actually log the user out (e.g. clearing cookies)
// ...
}
function showLogout() {
// update the UI to indicate we're logged out
// ...
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>DEMO: Pure P2P (signaling + streaming) Screen Casting via WebRTC over BroadcastWebSocket</title>
</head>
<body>
<h1>DEMO: Pure P2P (signaling + streaming) Screen Casting via WebRTC over BroadcastWebSocket</h1>
<select disabled>
<option disabled>Select a second screen to cast this tab to</option>
</select>
<script>
var castMenu = document.querySelector('select');
function generateUUID() {
return Math.floor(Math.random() * 1e16);
}
// Use a BroadcastWebSocket to collect screencast-capable candidate web pages and devices.
// Second screen capable devices advertise 'web+broadcast:screencast_devices'
// services on the local network via e.g. DNS-SD/mDNS.
var displaysBroadcast = new BroadcastWebSocket('screencast_devices');
var availableDisplays = {};
var availableDisplaysCount = 0;
displaysBroadcast.onconnect = function( socket ) {
var displayIndex;
socket.onmessage = function () {
var message = JSON.parse(socket.data);
// Connected service is advertising itself as a second screen
if( message.action == 'isSecondScreen' ) {
displayIndex = generateUUID();
availableDisplays[ displayIndex ] = {
'endpointName' : message.name || 'Second Screen',
'endpointSocket': socket
};
var castOption = document.createElement('option');
castOption.value = castOption.id = displayIndex;
castOption.textContent = availableDisplays[ displayIndex ];
castMenu.appendChild( castOption );
availableDisplaysCount++;
castMenu.disabled = false;
}
}
socket.onclose = function () {
// Remove this previously registered second screen
if( displayIndex ) {
var castOption = document.querySelector('#' + displayIndex);
castOption.parentNode.removeChild( castOption );
delete availableDisplays[ displayIndex ];
availableDisplaysCount--;
}
if(availableDisplaysCount === 0) {
castMenu.disabled = true;
}
};
}
castMenu.onchange = function () {
var selectedDisplayIndex = castMenu.options[castMenu.selectedIndex].value;
var selectedDisplayObject = availableDisplays[ selectedDisplayIndex ];
var selectedDisplaySocket = selectedDisplayObject.endpointSocket;
var streamConfig = {
audio: false,
video: {
mandatory: {
mediaSource: 'screen' // share the current screen
}
}
};
// Start a WebRTC screen sharing session
navigator.getUserMedia(
streamConfig,
function( stream ) { startScreenStreaming( stream, selectedDisplaySocket ) },
logError
);
};
function startScreenStreaming( screenStream, signalingChannel ) {
var pc = new RTCPeerConnection({ "iceServers": [{ "url": "stun:stun.example.org" }] });
// Add screen sharing MediaStream object to the PeerConnection
pc.addStream( screenStream );
pc.onicecandidate = function ( event ) {
if ( event.candidate ) {
signalingChannel.send(JSON.stringify({
"action": "rtc_IceCandidate",
"candidate": event.candidate
}));
}
};
pc.onnegotiationneeded = function () {
// enforce 'sendonly' streaming to second screen
pc.createOffer(localSDPCreated, logError, {
offerToReceiveVideo: false,
offerToReceiveAudio: false
});
};
function localSDPCreated( sdp ) {
pc.setLocalDescription(sdp, function() {
signalingChannel.send(JSON.stringify({
"action": "rtc_SDPDescription",
"sdp": pc.localDescription
}));
});
};
signalingChannel.onmessage = function ( event ) {
var message = JSON.parse(event.data);
if ( message.action == "rtc_SDPDescription" ) {
pc.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
if (pc.remoteDescription.type == "offer") {
pc.createAnswer(localSDPCreated, logError);
}
}, logError);
} else if ( message.action == "rtc_IceCandidate" ) {
pc.addIceCandidate( new RTCIceCandidate( message.candidate ) );
}
};
}
function logError( error ) {
console.log( error.name + ": " + error.message );
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment