v3.0 has its own built-in socket.io server. It has pubnub/firebase demos as well; however reliable-signaler or socketio-over-nodejs or similar codes can NOT be used with v3.0. Please use
Signaling-Server.jsinstead.v3.0 can use XHR/XMPP/etc. signaling implementations as well. Please check PubNubConnection.js to see how to configure it for 3rd party signaling implementations. You simply have to modify top few lines.
Fetch latest code:
Download via Github (as ZIP or .js): https://github.com/muaz-khan/RTCMultiConnection/releases
Or:
sudo npm install rtcmulticonnection-v3
# or MOST preferred one
mkdir RTCMultiConnection-v3.0 && cd RTCMultiConnection-v3.0
wget http://dl.webrtc-experiment.com/rtcmulticonnection-v3.tar.gz
tar -zxvf rtcmulticonnection-v3.tar.gz
ls -a
To TEST:
npm start
# or
node server.js
# if fails,
lsof -n -i4TCP:9001 | grep LISTEN
kill process-ID
# or kill specific port
# it may require "sudo" privileges
fuser -vk 9001/tcp
Now open: https://localhost:9001/
nohup nodejs server.js > /dev/null 2>&1 &
All files from /dist directory are available on CDN: https://cdn.webrtc-experiment.com:443/
<script src="/RTCMultiConnection.min.js"></script>
<!-- or -->
<script src="/dist/rmc3.min.js"></script>
<!-- CDN non-minified or minified -->
<script src="https://cdn.webrtc-experiment.com:443/rmc3.min.js"></script>
<!-- or specific version -->
<script src="https://github.com/muaz-khan/RTCMultiConnection/releases/download/3.2.90/rmc3.min.js"></script>If you're sharing files, you also need to link:
<script src="/dev/FileBufferReader.js"></script>
<!-- or CDN -->
<script src="https://cdn.webrtc-experiment.com:443/rmc3.fbr.min.js"></script>
<!-- or specific version -->
<script src="https://github.com/muaz-khan/RTCMultiConnection/releases/download/3.2.90/rmc3.fbr.min.js"></script>You can link multiple files from
devdirectory. Order doesn't matters.
Either via config.json file:
{
"socketURL": "/",
"socketMessageEvent": "RTCMultiConnection-Message"
}or override in your HTML code:
connection.socketURL = 'http://yourdomain.com:8080/';
// if your server is already having "message" event
// then you can use something else, unique.
connection.socketMessageEvent = 'unique-message';For testing purpose, you can use this as well:
{
"socketURL": "https://rtcmulticonnection.herokuapp.com:443/",
"socketMessageEvent": "RTCMultiConnection-Message"
}or
connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/';Here is a demo explaining how to use above socketURL:
// node.js code
require('./Signaling-Server.js')(httpServerHandlerOrPort);Use streamEvents instead of connection.streams:
var stream = connection.streamEvents['streamid'];
// or use this code:
// backward compatibility
connection.streams = connection.streamEvents;
connection.numberOfConnectedUsers = 0;
if (Object.observe) {
Object.observe(connection.streamEvents, function() {
// for backward compatibility
connection.streams = connection.streamEvents;
});
Object.observe(connection.peers, function() {
// for backward compatibility
connection.numberOfConnectedUsers = connection.getAllParticipants().length;
});
}
# you can even use "getStreamById"
var stream = connection.attachStreams.getStreamById('streamid');
# to get remote stream by id
var allRemoteStreams = connection.getRemoteStreams('remote-user-id');
var stream = allRemoteStreams.getStreamByid('streamid');Wanna check isScreen or isVideo or isAudio?
connection.onstream = function(event) {
if(event.stream.isScreen) {
// screen stream
}
if(event.stream.isVideo) {
// audio+video or video-only stream
}
if(event.stream.isAudio) {
// audio-only stream
}
};Wanna mute/unmute?
var stream = connection.streamEvents['streamid'].stream;
stream.mute('audio'); // audio or video or bothThis method allows you change video resolutions or audio sources without making a new getUserMedia request i.e. it modifies your existing MediaStream:
var width = 1280;
var height = 720;
var supports = navigator.mediaDevices.getSupportedConstraints();
var constraints = {};
if (supports.width && supports.height) {
constraints = {
width: width,
height: height
};
}
connection.applyConstraints({
video: constraints
});applyConstraints access mediaConstraints object, defined here:
This method allows you replace your front-camera video with back-camera video or replace video with screen or replace older low-quality video with new high quality video.
// here is its simpler usage
connection.replaceTrack({
screen: true,
oneway: true
});You can even pass MediaStreamTrack object:
var videoTrack = yourVideoStream.getVideoTracks()[0];
connection.replaceTrack(videoTrack);You can even pass MediaStream object:
connection.replaceTrack(yourVideoStream);You can even force to replace tracks only with a single user:
var remoteUserId = 'single-remote-userid';
var videoTrack = yourVideoStream.getVideoTracks()[0];
connection.replaceTrack(videoTrack, remoteUserId);If you replaced a video or audio track, RTCMultiConnection keeps record of old track, and allows you move-back-to previous track:
connection.resetTrack(null, true);It takes following arguments:
[Array of user-ids]or"single-user-id"ornull- Is video track (boolean): Either
trueorfalse.nullmeans replace all last tracks.
// with single user
connection.resetTrack('specific-userid', true);
// with multiple users
connection.resetTrack(['first-user', 'second-user'], true);
// NULL means all users
connection.resetTrack(null, true);
// reset only audio
connection.resetTrack(null, false);
// to reset all last-tracks (both audio and video)
connection.resetTrack();Means that you can reset all tracks that are replaced recently.
This event allows you show online/offline statuses of the user:
connection.onUserStatusChanged = function(status) {
document.getElementById(event.userid).src = status === 'online' ? 'online.gif' : 'offline.gif';
};You can even manually call above method from onopen, onstream and similar events to get the most accurate result possible:
connection.onopen = connection.stream = function(event) {
connection.onUserStatusChanged({
userid: event.userid,
extra: event.extra,
status: 'online'
});
};
connection.onleave = connection.streamended = connection.onclose = function(event) {
connection.onUserStatusChanged({
userid: event.userid,
extra: event.extra,
status: 'offline'
});
};This method allows you get the socket object used for signaling (handshake/presence-detection/etc.):
var socket = connection.getSocket();
socket.emit('custom-event', 'hi there');
socket.on('custom-event', function(message) {
alert(message);
});If socket isn't connected yet, then above method will auto-connect it. It is using connectSocket to connect socket. See below section.
It is same like old RTCMultiConnection connect method:
connectSocket method simply initializes socket.io server so that you can send custom-messages before creating/joining rooms:
connection.connectSocket(function(socket) {
socket.on('custom-message', function(message) {
alert(message);
// custom message
if(message.joinMyRoom) {
connection.join(message.roomid);
}
});
socket.emit('custom-message', 'hi there');
connection.open('room-id');
});This object allows you capture audio/video stream yourself. RTCMultiConnection will NEVER know about your stream until you add it yourself, manually:
var options = {
localMediaConstraints: {
audio: true,
video: true
},
onGettingLocalMedia: function(stream) {},
onLocalMediaError: function(error) {}
};
connection.getUserMediaHandler(options);Its defined here:
By default: all moderators are private.
This method returns list of all moderators (room owners) who declared themselves as public via becomePublicModerator method:
# to become a public moderator
connection.open('roomid', true); // 2nd argument is "TRUE"
# or call this method later (any time)
connection.becomePublicModerator();You can access list of all the public rooms using this method. This works similar to old RTCMultiConnection method onNewSession.
Here is how to get public moderators:
connection.getPublicModerators(function(array) {
array.forEach(function(moderator) {
// moderator.extra
connection.join(moderator.userid);
});
});You can even search for specific moderators. Moderators whose userid starts with specific string:
var moderatorIdStartsWith = 'public-moderator-';
connection.getPublicModerators(moderatorIdStartsWith, function(array) {
// only those moderators are returned here
// that are having userid similar to this:
// public-moderator-xyz
// public-moderator-abc
// public-moderator-muaz
// public-moderator-conference
array.forEach(function(moderator) {
// moderator.extra
connection.join(moderator.userid);
});
});You can force dontAttachStream and dontGetRemoteStream for any or each user in any situation:
connection.setUserPreferences = function(userPreferences) {
if (connection.dontAttachStream) {
// current user's streams will NEVER be shared with any other user
userPreferences.dontAttachLocalStream = true;
}
if (connection.dontGetRemoteStream) {
// current user will NEVER receive any stream from any other user
userPreferences.dontGetRemoteStream = true;
}
return userPreferences;
};Scenarios:
- All users in the room are having cameras
- All users in the room can see only self video
- All users in the room can text-chat or share files; but can't share videos
- As soon as teacher or moderator or presenter enters in the room; he can ask all the participants or specific participants to share their cameras with single or multiple users.
They can enable cameras as following:
connection.onmessage = function(event) {
var message = event.data;
if(message.shareYourCameraWithMe) {
connection.dontAttachStream = false;
connection.renegotiate(event.userid); // share only with single user
}
if(message.shareYourCameraWithAllUsers) {
connection.dontAttachStream = false;
connection.renegotiate(); // share with all users
}
}
i.e. setUserPreferences allows you enable camera on demand.
This method allows you check presence of the moderators/rooms:
connection.checkPresence('roomid', function(isRoomEists, roomid) {
if(isRoomEists) {
connection.join(roomid);
}
else {
connection.open(roomid);
}
});This event is fired as soon as callee says "I am ready for offer. I enabled camera. Please create offer and share.".
connection.onReadyForOffer = function(remoteUserId, userPreferences) {
// if OfferToReceiveAudio/OfferToReceiveVideo should be enabled for specific users
userPreferences.localPeerSdpConstraints.OfferToReceiveAudio = true;
userPreferences.localPeerSdpConstraints.OfferToReceiveVideo = true;
userPreferences.dontAttachStream = false; // according to situation
userPreferences.dontGetRemoteStream = false; // according to situation
// below line must be included. Above all lines are optional.
connection.multiPeersHandler.createNewPeer(remoteUserId, userPreferences);
};This event is fired as soon as someone tries to join you. You can either reject his request or set preferences.
connection.onNewParticipant = function(participantId, userPreferences) {
// if OfferToReceiveAudio/OfferToReceiveVideo should be enabled for specific users
userPreferences.localPeerSdpConstraints.OfferToReceiveAudio = true;
userPreferences.localPeerSdpConstraints.OfferToReceiveVideo = true;
userPreferences.dontAttachStream = false; // according to situation
userPreferences.dontGetRemoteStream = false; // according to situation
// below line must be included. Above all lines are optional.
// if below line is NOT included; "join-request" will be considered rejected.
connection.acceptParticipationRequest(participantId, userPreferences);
};Or:
var alreadyAllowed = {};
connection.onNewParticipant = function(participantId, userPreferences) {
if(alreadyAllowed[participantId]) {
connection.addParticipationRequest(participantId, userPreferences);
return;
}
var message = participantId + ' is trying to join your room. Confirm to accept his request.';
if( window.confirm(messsage ) ) {
connection.addParticipationRequest(participantId, userPreferences);
}
};Disconnect with single or multiple users. This method allows you keep connected to socket however either leave entire room or remove single or multiple users:
connection.disconnectWith('remoteUserId');
// to leave entire room
connection.getAllParticipants().forEach(function(participantId) {
connection.disconnectWith(participantId);
});Get list of all participants that are connected with current user.
var numberOfUsersInTheRoom = connection.getAllParticipants().length;
var remoteUserId = 'xyz';
var isUserConnectedWithYou = connection.getAllParticipants().indexOf(remoteUserId) !== -1;
connection.getAllParticipants().forEach(function(remoteUserId) {
var user = connection.peers[remoteUserId];
console.log(user.extra);
user.peer.close();
alert(user.peer === webkitRTCPeerConnection);
});Set number of users who can join your room.
// to allow another single person to join your room
// it will become one-to-one (i.e. you+anotherUser)
connection.maxParticipantsAllowed = 1;This method allows you skip Socket.io and force Firebase or PubNub or WebSockets or PHP/ASPNET whatever.
connection.setCustomSocketHandler(FirebaseConnection);Please check FirebaseConnection or PubNubConnection.js to understand how it works.
By default, logs are enabled.
connection.enableLogs = false; // to disable logsYou can force all the extra-data to be synced among all connected users.
connection.extra.fullName = 'New Full Name';
connection.updateExtraData(); // now above value will be auto synced among all connected usersThis event is fired as soon as extra-data from any user is updated:
connection.onExtraDataUpdated = function(event) {
console.log('extra data updated', event.userid, event.extra);
// make sure that <video> header is having latest fullName
document.getElementById('video-header').innerHTML = event.extra.fullName;
};It is similar to this:
If socket.io is listening on a separate port or external URL:
connection.socketURL = 'https://domain:port/';Socket.io options:
connection.socketOptions = {
'force new connection': true, // For SocketIO version < 1.0
'forceNew': true, // For SocketIO version >= 1.0
'transport': 'polling' // fixing transport:unknown issues
};Wanna detect current browser?
if(connection.DetectRTC.browser.isChrome) {
// it is Chrome
}
// you can even set backward compatibility hack
connection.UA = connection.DetectRTC.browser;
if(connection.UA.isChrome) { }Wanna detect if user is having microphone or webcam?
connection.DetectRTC.detectMediaAvailability(function(media){
if(media.hasWebcam) { }
if(media.hasMicrophone) { }
if(media.hasSpeakers) { }
});Get files problematically instead of using input[type=file]:
connection.invokeSelectFileDialog(function(file) {
var file = this.files[0];
if(file){
connection.shareFile(file);
}
});Force bandwidth, bitrates, etc.
var BandwidthHandler = connection.BandwidthHandler;
connection.bandwidth = {
audio: 128,
video: 256,
screen: 300
};
connection.processSdp = function(sdp) {
sdp = BandwidthHandler.setApplicationSpecificBandwidth(sdp, connection.bandwidth, !!connection.session.screen);
sdp = BandwidthHandler.setVideoBitrates(sdp, {
min: connection.bandwidth.video,
max: connection.bandwidth.video
});
sdp = BandwidthHandler.setOpusAttributes(sdp);
sdp = BandwidthHandler.setOpusAttributes(sdp, {
'stereo': 1,
//'sprop-stereo': 1,
'maxaveragebitrate': connection.bandwidth.audio * 1000 * 8,
'maxplaybackrate': connection.bandwidth.audio * 1000 * 8,
//'cbr': 1,
//'useinbandfec': 1,
// 'usedtx': 1,
'maxptime': 3
});
return sdp;
};Moderator can shift moderation control to any other user:
connection.shiftModerationControl('remoteUserId', connection.broadcasters, false);connection.broadcasters is the array of users that builds mesh-networking model i.e. multi-user conference.
Moderator shares connection.broadcasters with each new participant; so that new participants can connect with other members of the room as well.
This event is fired, as soon as moderator of the room shifts moderation control toward you:
connection.onShiftedModerationControl = function(sender, existingBroadcasters) {
connection.acceptModerationControl(sender, existingBroadcasters);
};A DOM-element to show progress-bars and preview files.
connection.filesContainer = document.getElementById('files-container');A DOM-element to append videos or audios or screens:
connection.videosContainer = document.getElementById('videos-container');In a one-way session, you can make multiple broadcasters using this method:
if(connection.isInitiator) {
connection.addNewBroadcaster('remoteUserId');
}Now this user will also share videos/screens.
Remove user from connection.broadcasters list.
connection.removeFromBroadcastersList('remote-userid');If screen or video capturing fails:
connection.onMediaError = function(error) {
alert( 'onMediaError:\n' + JSON.stringify(error) );
};Recreate peers. Capture new video using connection.captureUserMedia and call connection.renegotiate() and that new video will be shared with all connected users.
connection.renegotiate('with-single-userid');
connection.renegotiate(); // with all usersYou can even pass streamCallback:
connection.addStream({
screen: true,
oneway: true,
streamCallback: function(screenStream) {
// this will be fired as soon as stream is captured
screenStream.onended = function() {
document.getElementById('share-screen').disabled = false;
// or show button
$('#share-screen').show();
}
}
});To enable file sharing. By default, it is false:
connection.enableFileSharing = true;Change userid and update userid among all connected peers:
connection.changeUserId('new-userid');It is true by default. If you are handling window.onbeforeunload yourself, then you can set it to false:
connection.closeBeforeUnload = false;
window.onbeforeunlaod = function() {
connection.close();
};Open room:
var isPublicRoom = false;
connection.open('roomid', isPublicRoom);
// or
connection.open('roomid', function() {
// on room created
});Join room:
connection.join('roomid');
// or pass "options"
connection.join('roomid', {
localPeerSdpConstraints: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
},
remotePeerSdpConstraints: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
},
isOneWay: false,
isDataOnly: false
});connection.openOrJoin('roomid');
// or
connection.openOrJoin('roomid', function(isRoomExists, roomid) {
if(isRoomExists) alert('opened the room');
else alert('joined the room');
});By default, it is false. Which means that RTCMultiConnection will always capture video if connection.session.video===true.
If you are attaching external streams, you can ask RTCMultiConnection to DO NOT capture video:
connection.dontCaptureUserMedia = true;By default, it is false. Which means that RTCMultiConnection will always attach local streams.
connection.dontAttachStream = true;By default, it is false. Which means that RTCMultiConnection will always get remote streams.
connection.dontGetRemoteStream = true;This method allows you get full control over screen-parameters:
connection.__getScreenConstraints = connection.getScreenConstraints;
connection.getScreenConstraints = function(callback) {
connection.__getScreenConstraints(function(error, screen_constraints) {
if (connection.DetectRTC.browser.name === 'Chrome') {
delete screen_constraints.mandatory.minAspectRatio;
delete screen_constraints.mandatory.googLeakyBucket;
delete screen_constraints.mandatory.googTemporalLayeredScreencast;
delete screen_constraints.mandatory.maxWidth;
delete screen_constraints.mandatory.maxHeight;
delete screen_constraints.mandatory.minFrameRate;
delete screen_constraints.mandatory.maxFrameRate;
}
callback(error, screen_constraints);
});
};Or to more simplify it:
connection.__getScreenConstraints = connection.getScreenConstraints;
connection.getScreenConstraints = function(callback) {
connection.__getScreenConstraints(function(error, screen_constraints) {
if (connection.DetectRTC.browser.name === 'Chrome') {
screen_constraints.mandatory = {
chromeMediaSource: screen_constraints.mandatory.chromeMediaSource,
chromeMediaSourceId: screen_constraints.mandatory.chromeMediaSourceId
};
}
callback(error, screen_constraints);
});
};You can even delete width/height for Firefox:
connection.__getScreenConstraints = connection.getScreenConstraints;
connection.getScreenConstraints = function(callback) {
connection.__getScreenConstraints(function(error, screen_constraints) {
if (connection.DetectRTC.browser.name === 'Chrome') {
delete screen_constraints.mandatory.minAspectRatio;
}
if (connection.DetectRTC.browser.name === 'Firefox') {
delete screen_constraints.width;
delete screen_constraints.height;
}
callback(error, screen_constraints);
});
};If you are willing to use Firebase instead of Socket.io there, open GruntFile.js and replace SocketConnection.js with FirebaseConnection.js.
Then use grunt to recompile RTCMultiConnection.js.
Otherwise if you don't want to modify RTCMultiConnection:
<script src="/dev/globals.js"></script>
<script src="/dev/FirebaseConnection.js"></script>
<script>
var connection = new RTCMultiConnection();
connection.firebase = 'your-firebase-account';
// below line replaces FirebaseConnection
connection.setCustomSocketHandler(FirebaseConnection);
</script>Demo: https://rtcmulticonnection.herokuapp.com/demos/Firebase-Demo.html
Follow above all "firebase" steps and use PubNubConnection.js instead.
Please don't forget to use your own PubNub keys.
Demo: https://rtcmulticonnection.herokuapp.com/demos/PubNub-Demo.html
v3.0 now supports WebRTC scalable broadcasting. Two new API are introduced: enableScalableBroadcast and singleBroadcastAttendees.
connection.enableScalableBroadcast = true; // by default, it is false.
connection.singleBroadcastAttendees = 3; // how many users are handled by each broadcasterLive Demos:
connection.onstream = function(event) {
if(event.mediaElement) {
event.mediaElement.muted = true;
delete event.mediaElement;
}
var video = document.createElement('video');
if(event.type === 'local') {
video.muted = true;
}
video.src = URL.createObjectURL(event.stream);
connection.videosContainer.appendChild(video);
}connection.multiPeersHandler.onPeerStateChanged = function(state) {
if (state.iceConnectionState.search(/disconnected|closed|failed/gi) === -1 && !connection.isConnected) {
connection.isConnected = true;
var peer = connection.peers[state.userid].peer;
getStats(peer, function(result) {
if (!result || !result.connectionType) return;
// "relay" means TURN server
// "srflx" or "prflx" means STUN server
// "host" means neither STUN, nor TURN
console.debug('Incoming stream is using:', result.connectionType.remote.candidateType);
console.debug('Outgoing stream is using:', result.connectionType.local.candidateType);
// user external ip-addresses
console.debug('Remote user ip-address:', result.connectionType.remote.ipAddress);
console.debug('Local user ip-address:', result.connectionType.local.ipAddress);
// UDP is a real media port; TCP is a fallback.
console.debug('Peers are connected on port:', result.connectionType.transport);
}, 5000);
return;
}
};You can compare muteType for onmute event; and unmuteType for onunmute event.
connection.onmute = function(e) {
if (!e.mediaElement) {
return;
}
if (e.muteType === 'both' || e.muteType === 'video') {
e.mediaElement.src = null;
e.mediaElement.pause();
e.mediaElement.poster = e.snapshot || 'https://cdn.webrtc-experiment.com/images/muted.png';
} else if (e.muteType === 'audio') {
e.mediaElement.muted = true;
}
};
connection.onunmute = function(e) {
if (!e.mediaElement) {
return;
}
if (e.unmuteType === 'both' || e.unmuteType === 'video') {
e.mediaElement.poster = null;
e.mediaElement.src = URL.createObjectURL(e.stream);
e.mediaElement.play();
} else if (e.unmuteType === 'audio') {
e.mediaElement.muted = false;
}
};connection.bandwidth = {
audio: 128,
video: 1024,
screen: 1024
};
var videoConstraints = {
mandatory: {
maxWidth: 1920,
maxHeight: 1080,
minAspectRatio: 1.77,
minFrameRate: 3,
maxFrameRate: 64
},
optional: []
};
connection.mediaConstraints.video = videoConstraints;For low-latency audio:
By default, RTCMultiConnection tries to use last available microphone and camera. However you can disable this behavior and ask to use default devices instead:
// pass second parameter to force options
var connection = new RTCMultiConnection(roomId, {
useDefaultDevices: true
});By default, you always have to call open or join or openOrJoin methods manually. However you can force RTCMultiConnection to auto open/join room as soon as constructor is initialized.
// pass second parameter to force options
var connection = new RTCMultiConnection(roomId, {
autoOpenOrJoin: true
});| Experiment Name | Demo | Source Code |
|---|---|---|
| AppRTC like RTCMultiConnection demo! | Demo | Source |
| MultiRTC! RTCMultiConnection all-in-one demo! | Demo | Source |
| Collaborative Canvas Designer | Demo | Source |
| Conversation.js - Skype like library | Demo | Source |
| All-in-One test | Demo | Source |
| Multi-Broadcasters and Many Viewers | Demo | Source |
| Select Broadcaster at runtime | Demo | Source |
| OneWay Screen & Two-Way Audio | Demo | Source |
| Stream Mp3 Live | Demo | Source |
| Socket.io auto Open/Join rooms | Demo | Source |
| Screen Sharing & Cropping | Demo | Source |
| Share Part of Screen without cropping it | Demo | Source |
| getMediaDevices/enumerateDevices | Demo | Source |
| Renegotiation & Mute/UnMute/Stop | Demo | Source |
| Video-Conferencing | Demo | Source |
| Video Broadcasting | Demo | Source |
| Many-to-One Broadcast | Demo | Source |
| Audio Conferencing | Demo | Source |
| Multi-streams attachment | Demo | Source |
| Admin/Guest audio/video calling | Demo | Source |
| Session Re-initiation Test | Demo | Source |
| Preview Screenshot of the room | Demo | Source |
| RecordRTC & RTCMultiConnection | Demo | Source |
| Explains how to customize ice servers; and resolutions | Demo | Source |
| Mute/Unmute and onmute/onunmute | Demo | Source |
| One-page demo: Explains how to skip external signalling gateways | Demo | Source |
| Password Protect Rooms: Explains how to authenticate users | Demo | Source |
| Session Management: Explains difference between "leave" and "close" methods | Demo | Source |
| Multi-Sessions Management | Demo | Source |
| Customizing Bandwidth | Demo | Source |
| Users ejection and presence detection | Demo | Source |
| Multi-Session Establishment | Demo | Source |
| Group File Sharing + Text Chat | Demo | Source |
| Audio Conferencing + File Sharing + Text Chat | Demo | Source |
| Join with/without camera | Demo | Source |
| Screen Sharing | Demo | Source |
| One-to-One file sharing | Demo | Source |
| Manual session establishment + extra data transmission | Demo | Source |
| Manual session establishment + extra data transmission + video conferencing | Demo | Source |
| takeSnapshot i.e. Take Snapshot of Local/Remote streams | Demo | Source |
| Audio/Video/Screen sharing and recording | Demo | Source |
| Broadcast Multiple-Cameras | Demo | Source |
| Remote Stream Forwarding | Demo | Source |
| WebRTC Scalable Broadcast | Socketio/Nodejs | Source |
v2.2.2 is available here:
- https://twitter.com/WebRTCWeb i.e. @WebRTCWeb
RTCMultiConnection is released under MIT licence . Copyright (c) Muaz Khan.
