Permalink
Fetching contributors…
Cannot retrieve contributors at this time
386 lines (318 sloc) 13.5 KB
<!-- Demo version: 2018.10.25 -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>WebRTC Audio Scalable Broadcast using RTCMultiConnection</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="shortcut icon" href="/demos/logo.png">
<link rel="stylesheet" href="/demos/stylesheet.css">
<script src="/demos/menu.js"></script>
</head>
<body>
<header>
<a class="logo" href="/demos/"><img src="/demos/logo.png" alt="RTCMultiConnection"></a>
<a href="/demos/" class="menu-explorer">Menu<img src="/demos/menu-icon.png" alt="Menu"></a>
<nav>
<li>
<a href="/demos/">Home</a>
</li>
<li>
<a href="https://www.rtcmulticonnection.org/docs/getting-started/">Getting Started</a>
</li>
<li>
<a href="https://www.rtcmulticonnection.org/FAQ/">FAQ</a>
</li>
<li>
<a href="https://www.youtube.com/playlist?list=PLPRQUXAnRydKdyun-vjKPMrySoow2N4tl">YouTube</a>
</li>
<li>
<a href="https://rtcmulticonnection.herokuapp.com/demos/">Demos</a>
</li>
<li>
<a href="https://github.com/muaz-khan/RTCMultiConnection/wiki">Wiki</a>
</li>
<li>
<a href="https://github.com/muaz-khan/RTCMultiConnection">Github</a>
</li>
</nav>
</header>
<h1>
WebRTC Audio Scalable Broadcast using RTCMultiConnection
<p class="no-mobile">
Use peer-to-peer protocol to broadcast your audio over 20+ users.
</p>
</h1>
<section class="make-center">
<p style="margin: 0; padding: 0; padding-bottom: 20px;">
<div class="make-center">
<input type="text" id="broadcast-id" value="room-xyz" autocorrect=off autocapitalize=off size=20>
<button id="open-or-join">Open or Join Broadcast</button>
<div class="make-center" id="broadcast-viewers-counter"></div>
</p>
<audio id="audio-preview" controls loop></audio>
</section>
<script src="/dist/RTCMultiConnection.min.js"></script>
<script src="/node_modules/webrtc-adapter/out/adapter.js"></script>
<script src="/socket.io/socket.io.js"></script>
<!-- <script src="https://cdn.webrtc-experiment.com/RecordRTC.js"></script> -->
<script>
// recording is disabled because it is resulting for browser-crash
// if you enable below line, please also uncomment above "RecordRTC.js"
var enableRecordings = false;
var connection = new RTCMultiConnection();
// its mandatory in v3
connection.enableScalableBroadcast = true;
// each relaying-user should serve only 1 users
connection.maxRelayLimitPerUser = 1;
// we don't need to keep room-opened
// scalable-broadcast.js will handle stuff itself.
connection.autoCloseEntireSession = true;
// by default, socket.io server is assumed to be deployed on your own URL
connection.socketURL = '/';
// comment-out below line if you do not have your own socket.io server
// connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/';
connection.socketMessageEvent = 'scalable-audio-broadcast-demo';
// document.getElementById('broadcast-id').value = connection.userid;
// user need to connect server, so that others can reach him.
connection.connectSocket(function(socket) {
socket.on('logs', function(log) {
document.querySelector('h1').innerHTML = log.replace(/</g, '----').replace(/>/g, '___').replace(/----/g, '(<span style="color:red;">').replace(/___/g, '</span>)');
});
// this event is emitted when a broadcast is already created.
socket.on('join-broadcaster', function(hintsToJoinBroadcast) {
console.log('join-broadcaster', hintsToJoinBroadcast);
connection.session = hintsToJoinBroadcast.typeOfStreams;
connection.sdpConstraints.mandatory = {
OfferToReceiveVideo: !!connection.session.video,
OfferToReceiveAudio: !!connection.session.audio
};
connection.broadcastId = hintsToJoinBroadcast.broadcastId;
connection.join(hintsToJoinBroadcast.userid);
});
socket.on('rejoin-broadcast', function(broadcastId) {
console.log('rejoin-broadcast', broadcastId);
connection.attachStreams = [];
socket.emit('check-broadcast-presence', broadcastId, function(isBroadcastExists) {
if (!isBroadcastExists) {
// the first person (i.e. real-broadcaster) MUST set his user-id
connection.userid = broadcastId;
}
socket.emit('join-broadcast', {
broadcastId: broadcastId,
userid: connection.userid,
typeOfStreams: connection.session
});
});
});
socket.on('broadcast-stopped', function(broadcastId) {
// alert('Broadcast has been stopped.');
// location.reload();
console.error('broadcast-stopped', broadcastId);
alert('This broadcast has been stopped.');
});
// this event is emitted when a broadcast is absent.
socket.on('start-broadcasting', function(typeOfStreams) {
console.log('start-broadcasting', typeOfStreams);
// host i.e. sender should always use this!
connection.sdpConstraints.mandatory = {
OfferToReceiveVideo: false,
OfferToReceiveAudio: false
};
connection.session = typeOfStreams;
// "open" method here will capture media-stream
// we can skip this function always; it is totally optional here.
// we can use "connection.getUserMediaHandler" instead
connection.open(connection.userid);
});
});
window.onbeforeunload = function() {
// Firefox is ugly.
document.getElementById('open-or-join').disabled = false;
};
var audioPreview = document.getElementById('audio-preview');
connection.onstream = function(event) {
if (connection.isInitiator && event.type !== 'local') {
return;
}
connection.isUpperUserLeft = false;
audioPreview.srcObject = event.stream;
audioPreview.play();
audioPreview.userid = event.userid;
if (event.type === 'local') {
audioPreview.muted = true;
}
if (connection.isInitiator == false && event.type === 'remote') {
// he is merely relaying the media
connection.dontCaptureUserMedia = true;
connection.attachStreams = [event.stream];
connection.sdpConstraints.mandatory = {
OfferToReceiveAudio: false,
OfferToReceiveVideo: false
};
connection.getSocket(function(socket) {
socket.emit('can-relay-broadcast');
if (connection.DetectRTC.browser.name === 'Chrome') {
connection.getAllParticipants().forEach(function(p) {
if (p + '' != event.userid + '') {
var peer = connection.peers[p].peer;
peer.getLocalStreams().forEach(function(localStream) {
peer.removeStream(localStream);
});
event.stream.getTracks().forEach(function(track) {
peer.addTrack(track, event.stream);
});
connection.dontAttachStream = true;
connection.renegotiate(p);
connection.dontAttachStream = false;
}
});
}
if (connection.DetectRTC.browser.name === 'Firefox') {
// Firefox is NOT supporting removeStream method
// that's why using alternative hack.
// NOTE: Firefox seems unable to replace-tracks of the remote-media-stream
// need to ask all deeper nodes to rejoin
connection.getAllParticipants().forEach(function(p) {
if (p + '' != event.userid + '') {
connection.replaceTrack(event.stream, p);
}
});
}
// Firefox seems UN_ABLE to record remote MediaStream
// WebAudio solution merely records audio
// so recording is skipped for Firefox.
if (connection.DetectRTC.browser.name === 'Chrome') {
repeatedlyRecordStream(event.stream);
}
});
}
// to keep room-id in cache
localStorage.setItem(connection.socketMessageEvent, connection.sessionid);
};
// ask node.js server to look for a broadcast
// if broadcast is available, simply join it. i.e. "join-broadcaster" event should be emitted.
// if broadcast is absent, simply create it. i.e. "start-broadcasting" event should be fired.
document.getElementById('open-or-join').onclick = function() {
var broadcastId = document.getElementById('broadcast-id').value;
if (broadcastId.replace(/^\s+|\s+$/g, '').length <= 0) {
alert('Please enter broadcast-id');
document.getElementById('broadcast-id').focus();
return;
}
document.getElementById('open-or-join').disabled = true;
connection.extra.broadcastId = broadcastId;
connection.session = {
audio: true,
oneway: true
};
connection.getSocket(function(socket) {
socket.emit('check-broadcast-presence', broadcastId, function(isBroadcastExists) {
if (!isBroadcastExists) {
// the first person (i.e. real-broadcaster) MUST set his user-id
connection.userid = broadcastId;
}
console.log('check-broadcast-presence', broadcastId, isBroadcastExists);
socket.emit('join-broadcast', {
broadcastId: broadcastId,
userid: connection.userid,
typeOfStreams: connection.session
});
});
});
};
connection.onstreamended = function() {};
connection.onleave = function(event) {
if (event.userid !== audioPreview.userid) return;
connection.getSocket(function(socket) {
socket.emit('can-not-relay-broadcast');
connection.isUpperUserLeft = true;
if (allRecordedBlobs.length) {
// playing lats recorded blob
var lastBlob = allRecordedBlobs[allRecordedBlobs.length - 1];
audioPreview.src = URL.createObjectURL(lastBlob);
audioPreview.play();
allRecordedBlobs = [];
} else if (connection.currentRecorder) {
var recorder = connection.currentRecorder;
connection.currentRecorder = null;
recorder.stopRecording(function() {
if (!connection.isUpperUserLeft) return;
audioPreview.src = URL.createObjectURL(recorder.getBlob());
audioPreview.play();
});
}
if (connection.currentRecorder) {
connection.currentRecorder.stopRecording();
connection.currentRecorder = null;
}
});
};
var allRecordedBlobs = [];
function repeatedlyRecordStream(stream) {
if (!enableRecordings) {
return;
}
connection.currentRecorder = RecordRTC(stream, {
type: 'audio'
});
connection.currentRecorder.startRecording();
setTimeout(function() {
if (connection.isUpperUserLeft || !connection.currentRecorder) {
return;
}
connection.currentRecorder.stopRecording(function() {
allRecordedBlobs.push(connection.currentRecorder.getBlob());
if (connection.isUpperUserLeft) {
return;
}
connection.currentRecorder = null;
repeatedlyRecordStream(stream);
});
}, 30 * 1000); // 30-seconds
};
function disableInputButtons() {
document.getElementById('open-or-join').disabled = true;
document.getElementById('broadcast-id').disabled = true;
}
// ......................................................
// ......................Handling broadcast-id................
// ......................................................
var broadcastId = '';
if (localStorage.getItem(connection.socketMessageEvent)) {
broadcastId = localStorage.getItem(connection.socketMessageEvent);
} else {
broadcastId = connection.token();
}
var txtBroadcastId = document.getElementById('broadcast-id');
txtBroadcastId.value = broadcastId;
txtBroadcastId.onkeyup = txtBroadcastId.oninput = txtBroadcastId.onpaste = function() {
localStorage.setItem(connection.socketMessageEvent, this.value);
};
// below section detects how many users are viewing your broadcast
connection.onNumberOfBroadcastViewersUpdated = function(event) {
if (!connection.isInitiator) return;
document.getElementById('broadcast-viewers-counter').innerHTML = 'Number of broadcast viewers: <b>' + event.numberOfBroadcastViewers + '</b>';
};
</script>
<section class="no-mobile">
<h2>How this works?</h2>
<p>
This module simply initializes socket.io and configures it in a way that single audio/video/screen stream can be shared/relayed over unlimited users without any <a href="https://www.webrtc-experiment.com/docs/RTP-usage.html">bandwidth/CPU usage issues</a>. Everything happens peer-to-peer!
</p>
<p>
Check <a href="https://github.com/muaz-khan/WebRTC-Experiment/issues/2">this thread</a> or <a href="https://github.com/muaz-khan/WebRTC-Scalable-Broadcast">this github repository</a>.
</p>
</section>
<section class="no-mobile">
<h2>iOS or Android</h2>
<p>
You can write <a href="http://www.rtcmulticonnection.org/docs/Write-iOS-Apps/" target="_blank">iOS</a> or <a href="http://www.rtcmulticonnection.org/docs/Write-Android-Apps/" target="_blank">Android</a> apps for this demo as well.
</p>
</section>
<footer>
<small id="send-message"></small>
</footer>
<script src="https://cdn.webrtc-experiment.com/common.js"></script>
</body>
</html>