Brume provides user-to-user (AKA peer-to-peer) data/voice/video between Brume users. Communication is directly between user devices without traversing a central server. Brume has three components:
- The core which provides user onboarding and management and a peer-to-peer signaling server to connect Brume users.
- The Brume client Javascript library that provides an API for web or platform-specific applications to establish connections to the Brume signaling server and between Brume clients. The client library can be used in browsers, NodeJS and Webview.
- A video chat and file transfer app. Simple example data sender and receiver apps are provided in this repo.
Brume applications use the Brume client to establish signaling connections to the signaling server using the Brume name. An application establishes a peer-to-peer connection to another receiving application using that application's Brume name. Each attempt by a Brume user to connect to another user is a connection attempt.
The Brume Client library is used in an application to create a client instance.
import { Brume } from './Brume.mjs';
const brume = new Brume;
Creates a new Brume instance. Returns a Brume instance.
Connect to the Brume signaling server:
token
is a JWT required to connect to the Brume signaling server.
url
is the Brume signaling server url.
The JWT is either generated by logging into the Brume server with a user's email and password or by downloading a config file from the Brume website.
This method must be called before a peer connection can be made or received.
brume.start( { token, url } );
Creates a new WebRTC peer connection with a data channel to the Brume user specified by brumeName <string>
. Returns a Promise that resolves to a simple-peer instance or is rejected with an error code:
'ENODEST'
brumeName is not connected to the Brume signaling server.
'EBADDEST'
brumeName is not a Brume name.
'EOFFERTIMEOUT'
The attempt to connect to brumeName timed out.
'ESERVER'
An unspecified Brume signaling server error.
try {
const peer = await brume.connect( 'alice' );
...
} catch( e ){
//process error code e
}
Returns the <string> Brume name of this Brume instance.
myBrumeName = brume.thisUser();
Registers a function handler
to be called when a peer connection request is received; this is the counterpart to brume.connect( ... )
. The function is called with an object { peer, accept }
where the peer
property contains a simple-peer instance and the accept
property is an async function that is called to accept the offered connection, allowing the connection receiver to set up the peer instance before the WebRTC data channel is established.
function offerHandler( peer, accept ){
// set up peer.on handlers
await accept();
// peer-to-peer data channel established
};
brume.onconnection( offerHandler );
Disconnect from the Brume signaling server.
brume.stop();
Creates a Uint8Array encoded message of type
containing data
.
type
is a string identifying the format of data
data
is a Uint8Array
peer.send( Brume.encodeMsg( { type: 'action', data} ) );
Decodes an encoded msg returning the obejct { type, data }.
{ type, data } = Brume.decodeMsg( msg );
Received when the Brume server closes the connection to this Brume instance.
Received if the token used in brume.start( ... ) has expired and a refresh token was used to generate a new token. config
is a new config object with a new token. A refresh token is only available i the config file downloaded from the Brume website.
Brume creates a peer instance of the simple-peer Peer class when a connection request is made or received. Brume extends simple-peer in several ways.
As noted, the peer instance created by Brume is already connected to the other peer and a data channel is configured. The offer/answer/candidate signaling has already been done by Brume.
An application can have multiple peer connections, each to a different Brume name.
All media streams are created dynamically, there is no option to do so when the peer is created. A Brume peer creates a media stream by:
function addMedia (stream) {
peer1.addStream(stream) // <- add streams to peer dynamically
}
// then, anytime later...
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(addMedia).catch(() => {})
A Brume peer receives notification of a stream by:
peer2.on('stream', stream => {
// got remote video stream, now let's show it in a video tag
var video = document.querySelector('video')
if ('srcObject' in video) {
video.srcObject = stream
} else {
video.src = window.URL.createObjectURL(stream) // for older browsers
}
video.play()
})
Taken from simple-peer dynamic streams
Brume handles all of the signaling generated by the above; the peers needn't and shouldn't use the simple-peer signal method or listen for 'signal' events.
Brume defines a simple data channel protocol. Each message is an object { type, data }
where type
is a string and data
can be either a string or a Uint8Array. The message is sent as a Uint8Array. The Brume class static methods encode( ... )
and decode( ... )
are used by the sender and receiver.
Brume bypasses the signaling server and sends all signaling messages generated by dynamic media streams over the peer-to-peer data channel once it is created; the signaling server is no longer needed.
Peer instances are created in Brume; there is no publicly available Peer constructor.
Contains the Brume ID of the far peer.
An application must use Brume.encodeMsg
and Brume.decodeMsg
in order to take advantage of data channel multiplexing. This is not required.
The Brume instance acts on these events to manage the peers it creates and for multiplexing signaling on the data channel. The events are then passed on to the application listeners. Brume will not work if the application calls removeAllListeners
for these events.
Install the repo.
git clone [email protected]:boblund/brume-client.git
cd brume-client
npm i
There are three examples showing the client use in NodeJS, browser and webview-nodejs.
NodeJS apps require a config file containing the JWT and server URL to connect to the Brume signaling server. The default location for this file is: ~/Brume/brume.conf
. This can be overidden by setting the environment varailbe BRUME_CONFIG
. The config file can be obtained by signing into brume.occams.solutions, going to the Account
tab and clicking Update configuration
.
The example has a sender and receiver app. These must be started with config files for different users. Assume you have two Brume accounts, 'alice' and 'bob'. The 'alice' config file is in the default location, i.e. ~/Brume/brume.conf
. You've saved the 'bob' config in ~/Brume/bob-brume.conf
. Then, to run the NodeJS example, in the brume-client directory start brumeReceiver.mjs
first, then brumeSender
:
BRUME_CONFIG=~/Brume/joe.brume.conf node brumeReceiver.mjs&
node brumeSender.mjs
You'll need a web server for this example. One is included in this repo. In the brume-client directory do:
node server.js .
A server is started on a random unused port. Then, in two separate browser tabs, go to: localhost:port
. Sign in as 'alice' and 'bob' then click the call button in one of the tabs. In the other tab, click to accept the call.
The webview example runs the same code as the browser example except in webview-nodejs.
cd webview
npm i
node webpackBrume.mjs&
node webpackBrume.mjs
This will start an two webviews. Then follow the same steps as the browser example.
Creative Commons Attribution-NonCommercial 4.0 International
THIS SOFTWARE COMES WITHOUT ANY WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.