LioWebRTC
A WebRTC library that makes it easy to embed scalable peer to peer communication into UI components.
LioWebRTC works standalone, but it is also compatible with React, Vue, Electron, etc. It can be configured for scalability using partial mesh networks, making it possible to emit data to thousands of peers in a room, while only needing to be connected to at least one other peer in the room.
Click here to see a chatroom demo built with LioWebRTC.
Click here to see a video conferencing demo app built with LioWebRTC.
Using LioWebRTC with React
React developers may want to take a look at react-liowebrtc.
Usage
Installation
yarn add liowebrtc
// Or
npm i liowebrtc
Import LioWebRTC
import LioWebRTC from 'liowebrtc';
Create LioWebRTC instance
By default, this enables video, audio, and data channels.
const webrtc = new LioWebRTC({
localVideoEl: localVideoIdOrRef, // The local video element
autoRequestMedia: true, // Immediately request camera and mic access upon initialization
debug: true, // Displays events emitted by liowebrtc in the console
url: 'https://your-signaling-server.com:443/' // The url for your signaling server. If no url is passed, liowebrtc uses the default demo signaling server. (The default server is for demo purposes only, and is not reliable. Plus, I'm the only one paying for it 🙁. Please use your own in production!)
});
Data channels only
Disable video/audio streaming, and only allow data channels.
const webrtc = new LioWebRTC({
dataOnly: true
});
Audio and data channels only
Great for voice calls.
const webrtc = new LioWebRTC({
autoRequestMedia: true,
media: {
video: false,
audio: true
}
});
Partial mesh network
Peers only form direct connections with a maximum of maxPeers and a minimum of minPeers. shout()ing still works because peers wil re-propagate messages to other peers. Note: partial mesh networks only work if you're only using dataOnly
.
const webrtc = new LioWebRTC({
dataOnly: true,
network: {
maxPeers: 8,
minPeers: 4
}
})
Join a room once it's ready
webrtc.on('ready', () => {
// Joins a room if it exists, creates it if it doesn't
webrtc.joinRoom('your room name');
});
Emitting to the hive
Sometimes a peer wants to let every other peer in the room to know about something. This can be accomplished with
shout(messageType, payload)
webrtc.shout('event-label', { success: true, payload: '137' });
Now for the recipients, handle the peer event with a listener:
webrtc.on('receivedPeerData', (type, data, peer) => {
if (type === 'event-label' && data.success) {
console.log(`Peer ${peer.id} emitted ${data.payload}`);
}
});
Communicating with a single peer
Sometimes a peer only wants to send data directly to another peer. This can be accomplished with
whisper(peer, messageType, payload)
webrtc.whisper(peer, 'directMessage', { msg: 'Hello world!' });
Receiving the message is the same as handling a peer event:
webrtc.on('receivedPeerData', (type, data, peer) => {
if (type === 'directMessage') console.log(`Peer ${peer.id} says: ${data.msg}`);
});
Live-syncing state
componentDidUpdate(prevProps, prevState) {
if (this.state.position !== prevState.position) {
this.webrtc.shout('stateUpdate', this.state);
}
}
});
All communications via shout/whisper are sent over the default data channel and emitted by the LioWebRTC instance as events. You can create your own custom listeners suited for whatever purpose you'd like.
Attaching a peer's media stream to a video element
webrtc.on('peerStreamAdded', (stream, peer) => {
webrtc.attachStream(stream, yourVideoElementOrRef);
});
Example
P2P Video Chat Component
import React, { Component } from 'react';
import LioWebRTC from 'liowebrtc';
class Party extends Component {
constructor(props) {
super(props);
this.state = {
nick: this.props.nick,
roomID: `party-${this.props.roomName}`,
muted: false,
camPaused: false,
peers: []
};
this.remoteVideos = {};
}
componentDidMount() {
this.webrtc = new LioWebRTC({
// The url for your signaling server. Use your own in production!
url: 'https://sm1.lio.app:443/',
// The local video ref set within your render function
localVideoEl: this.localVid,
// Immediately request camera access
autoRequestMedia: true,
// Optional: nickname
nick: this.state.nick,
debug: true
});
this.webrtc.on('peerStreamAdded', this.addVideo);
this.webrtc.on('removedPeer', this.removeVideo);
this.webrtc.on('ready', this.readyToJoin);
this.webrtc.on('iceFailed', this.handleConnectionError);
this.webrtc.on('connectivityError', this.handleConnectionError);
}
addVideo = (stream, peer) => {
this.setState({ peers: [...this.state.peers, peer] }, () => {
this.webrtc.attachStream(stream, this.remoteVideos[peer.id]);
});
}
removeVideo = (peer) => {
this.setState({
peers: this.state.peers.filter(p => !p.closed)
});
}
handleConnectionError = (peer) => {
const pc = peer.pc;
console.log('had local relay candidate', pc.hadLocalRelayCandidate);
console.log('had remote relay candidate', pc.hadRemoteRelayCandidate);
}
readyToJoin = () => {
// Starts the process of joining a room.
this.webrtc.joinRoom(this.state.roomID, (err, desc) => {
});
}
// Show fellow peers in the room
generateRemotes = () => this.state.peers.map((p) => (
<div key={p.id}>
<div id={/* The video container needs a special id */ `${this.webrtc.getContainerId(p)}`}>
<video
// Important: The video element needs both an id and ref
id={this.webrtc.getDomId(p)}
ref={(v) => this.remoteVideos[p.id] = v}
/>
</div>
<p>{p.nick}</p>
</div>
));
disconnect = () => {
this.webrtc.quit();
}
componentWillUnmount() {
this.disconnect();
}
render() {
return (
<div>
<div>
<video
// Important: The local video element needs to have a ref
ref={(vid) => { this.localVid = vid; }}
/>
<p>{this.state.nick}</p>
</div>
{this.generateRemotes()}
</div>
);
}
}
export default Party;
API
Constructor Options
new LioWebRTC(options)
-
object options
-
string url
- url for your socket.io signaling server -
bool debug
- optional logs all webrtc events -
string nick
- optional sets your nickname. Peers' nicknames can be accessed withpeer.nick
-
[string|DomElement|Ref] localVideoEl
- Can be a ref, DOM element, or ID of the local video -
bool autoRequestMedia
- optional(=false) automatically request user media. Usetrue
to request automatically, orfalse
to request media later withstartLocalVideo
-
bool dataOnly
optional(=false) option to ensure that video and audio stream channels are turned off -
bool autoRemoveVideos
- optional(=true) option to automatically remove video elements when streams are stopped. -
bool adjustPeerVolume
- optional(=true) option to reduce peer volume when the local participant is speaking -
number peerVolumeWhenSpeaking
- optional(=.0.25) value used in conjunction withadjustPeerVolume
. Uses values between 0 and 1 -
object media
- media options to be passed togetUserMedia
. Defaults to{ video: true, audio: true }
. Valid configurations described on MDN with official spec at w3c. -
object receiveMedia
- optional RTCPeerConnection options. Defaults to{ offerToReceiveAudio: 1, offerToReceiveVideo: 1 }
. -
object localVideo
- optional options for attaching the local video stream to the page. Defaults to
{ autoplay: true, // automatically play the video stream on the page mirror: true, // flip the local video to mirror mode (for UX) muted: true // mute local video stream to prevent echo }
-
object constraints
- optional options for setting minimum and maximum peers to connect to. Defaults to
{ minPeers: 2, // connect to at least 2 peers maxPeers: 0 // when 0, maxPeers is infinite }
-
bool selfOptimize
- optional(=true) whether or not peers in a partial mesh network should self-optimize their connections. LioWebRTC uses a more object-oriented version of an adjacency list to represent the p2p graph, with the weights of the edges representing roundtrip latency between two nodes. WithselfOptimize
set to true, peers automatically disconnect from neighbors with latencies >=1 std. deviation from the mean, and reconnect to a new random peer.
-