# Writing a new frontend

The frontend is written in JavaScript and is split into two files:

  - `protocol.js` contains the low-level functions that interact with the
    server;
  - `sfu.js` contains the user interface.

If you wish to develop your own frontend, I recommend using `protocol.js`,
which is likely to remain reasonably stable as the protocol evolves.  This
file can be processed with JSDoc or Typescript (a sample `tsconfig.json`
is provided), but is otherwise plain Javascript (ES6).

## Data structures

The class `ServerConnection` encapsulates a connection to the server as
well as all the associated streams.

The class `Stream` encapsulates a set of related audio and video tracks;
your frontend will probably associate each stream with a `video` or
`audio` component.

## Connecting to the server

First, create a `ServerConnection` and set up all the callbacks:

```javascript
let sc = new ServerConnection()
serverConnection.onconnected = ...;
serverConnection.onclose = ...;
serverConnection.onusermessage = ...;
serverConnection.onpermissions = ...;
serverConnection.onuser = ...;
serverConnection.onchat = ...;
serverConnection.onclearchat = ...;
serverConnection.ondownstream = ...;
```

The `onconnected` callback is called when we connect to the server.  The
`onclose` callback is called when the socket is closed; you should use it
to close all your outgoing streams (incoming streams will be closed by the
server).  `onusermessage` indicates a message from the server that should
be displayed to the user.

The other callbacks will only be called after you join a group.  `onuser`
is used to indicate that a user has joined or left the current group.
`onchat` indicates that a chat message has been posted to the group, and
`onclearchat` indicates that the chat history has been cleared.  Finally,
`ondownstream` is called when the server pushes a stream to the client;
see the section below about streams.

You may now connect to the server.

```javascript
serverConnection.connect(`wss://${location.host}/ws`);
```

You log-in, join a group and request media in the `onconnected` callback.

```javascript
serverConnection.onconnected = function() {
    this.login(username, password);
    this.join(group); 
    this.request('everything');
}
```

You should not attempt to push a stream to the server until it has granted
you the `present` permission through the `onpermissions` callback.

## Managing groups and users

The `groupaction` and `useraction` methods perform actions such as kicking
users or locking groups.  Most actions require either the `Op` or the
`Record` permission.

## Sending and receiving chat messages

Once you have joined a group, you send chat messages with the `chat`
method.  No permission is needed to do that.

```javascript
serverConnection.chat(username, '', 'Hi!');
```

You receive chat messages in the `onchat` callback.  The server may
request that you clear your chat window, in that case the `onclearchat`
callback will trigger.

## Accepting incoming video streams

When the server pushes a stream to the client, the `ondownstream` callback
will trigger; you should set up the stream's callbacks here.
```javascript
serverConnection.ondownstream = function(stream) {
    stream.onclose = ...;
    stream.onerror = ...;
    stream.ondowntrack = ...;
    stream.onlabel = ...;
    stream.onstatus = ...;
}
```

The `stream.labels` dictionary maps each track's id to one of `audio`,
`video` or `screenshare`.  Since `stream.labels` is already available at
this point, you may set up an `audio` or `video` component straight away,
or you may choose to wait until the `ondowntrack` callback is called.

The server will usually invoke the `onlabel` callback in order to set
a user-readable label on the stream; this is currently just the
originating client's username.

After a new stream is created, `ondowntrack` will be called whenever
a track is added.  If the `MediaStream` passed to `ondowntrack` differs
from the one previously received, then the stream has been torn down and
recreated, and you must drop all previously received tracks; in practice,
it is enough to set the `srcObject` property of the video component to the
new stream.

The `onstatus` callback is invoked whenever the client library detects
a change in the status of the stream; states `connected` and `complete`
indicate a functioning stream; other states indicate that the stream is
not working right now but might recover in the future.

The `onclose` callback is called when the stream is destroyed by the
server.

## Pushing outgoing video streams

If you have the `present` permission, you may use the `newUpStream` method
to push a stream to the server.  Given a `MediaStream` called `localStream`
(as obtained from `getUserMedia` or `getDisplayMedia`).

```javascript
let stream = serverConnection.newUpStream();
stream.onerror = ...;
stream.onabort = ...;
stream.onstatus = ...;
localStream.getTracks().forEach(t => {
    c.labels[t.id] = t.kind;
    c.pc.addTrack(t, c.stream);
});
```

See above for information about setting up the `labels` dictionary.

## Stream statistics

For outgoing streams only, the `setStatsInterval` and `onstats` callback
can be used to determine the data rate in real time.  This is currently
not implemented for down streams.

--- Juliusz Chroboczek <https://www.irif.fr/~jch/>