2020-08-12 21:47:05 +02:00
|
|
|
# Writing a new frontend
|
|
|
|
|
2020-08-14 15:22:50 +02:00
|
|
|
The frontend is written in JavaScript and is split into two files:
|
2020-08-12 21:47:05 +02:00
|
|
|
|
2020-08-14 15:22:50 +02:00
|
|
|
- `protocol.js` contains the low-level functions that interact with the
|
|
|
|
server;
|
2020-12-06 19:43:17 +01:00
|
|
|
- `galene.js` contains the user interface.
|
2020-08-12 21:47:05 +02:00
|
|
|
|
2020-12-19 02:37:07 +01:00
|
|
|
A new frontend may either implement Galène's client-server protocol from
|
|
|
|
scratch, or it may use the functionality of `protocol.js`. This document
|
|
|
|
documents the latter approach.
|
2020-08-12 21:47:05 +02:00
|
|
|
|
|
|
|
## Data structures
|
|
|
|
|
|
|
|
The class `ServerConnection` encapsulates a connection to the server as
|
2020-12-19 02:37:07 +01:00
|
|
|
well as all the associated streams. Unless your frontend communicates
|
|
|
|
with multiple servers, it will probably create just a single instance of
|
|
|
|
this class.
|
2020-08-12 21:47:05 +02:00
|
|
|
|
2020-12-19 02:37:07 +01:00
|
|
|
The class `Stream` encapsulates a set of related audio and video tracks
|
|
|
|
(for example, an audio track from a microphone and a video track from
|
|
|
|
a webcam). A stream is said to go *up* when it carries data from the
|
|
|
|
client to the server, and *down* otherwise. Streams going up are created
|
|
|
|
by the client (your frontend), streams going down are created by the server.
|
2020-08-12 21:47:05 +02:00
|
|
|
|
|
|
|
## Connecting to the server
|
|
|
|
|
|
|
|
First, create a `ServerConnection` and set up all the callbacks:
|
|
|
|
|
2020-08-14 15:22:50 +02:00
|
|
|
```javascript
|
2020-08-12 21:47:05 +02:00
|
|
|
let sc = new ServerConnection()
|
|
|
|
serverConnection.onconnected = ...;
|
|
|
|
serverConnection.onclose = ...;
|
2020-08-14 15:22:50 +02:00
|
|
|
serverConnection.onusermessage = ...;
|
2020-12-01 22:42:06 +01:00
|
|
|
serverConnection.onjoined = ...;
|
2020-08-14 15:22:50 +02:00
|
|
|
serverConnection.onuser = ...;
|
2020-08-12 21:47:05 +02:00
|
|
|
serverConnection.onchat = ...;
|
|
|
|
serverConnection.onclearchat = ...;
|
2020-08-14 15:22:50 +02:00
|
|
|
serverConnection.ondownstream = ...;
|
2020-08-12 21:47:05 +02:00
|
|
|
```
|
|
|
|
|
2020-08-23 19:07:52 +02:00
|
|
|
The `onconnected` callback is called when we connect to the server. The
|
2021-01-14 14:56:15 +01:00
|
|
|
`onclose` callback is called when the socket is closed; all streams will
|
|
|
|
have been closed by the time it is called. The `onusermessage` callback
|
|
|
|
indicates an application-specific message, either from another user or
|
|
|
|
from the server; the field `kind` indicates the kind of message.
|
2020-12-19 02:37:07 +01:00
|
|
|
|
|
|
|
Once you have joined a group (see below), the remaining callbacks may
|
|
|
|
trigger. The `onuser` callback is used to indicate that a user has joined
|
2021-04-27 18:58:21 +02:00
|
|
|
or left the current group, or that their attributes have changed; the
|
|
|
|
user's state can be found in the `users` dictionary. The `onchat`
|
|
|
|
callback 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.
|
2020-08-14 15:22:50 +02:00
|
|
|
|
2022-01-24 19:59:45 +01:00
|
|
|
You may now connect to the server:
|
2020-08-14 15:22:50 +02:00
|
|
|
|
|
|
|
```javascript
|
2020-08-12 21:47:05 +02:00
|
|
|
serverConnection.connect(`wss://${location.host}/ws`);
|
|
|
|
```
|
2020-08-14 15:22:50 +02:00
|
|
|
|
2022-01-24 19:59:45 +01:00
|
|
|
You typically join a group in the `onconnected` callback:
|
2020-08-14 15:22:50 +02:00
|
|
|
|
|
|
|
```javascript
|
2020-08-12 21:47:05 +02:00
|
|
|
serverConnection.onconnected = function() {
|
2020-12-01 22:42:06 +01:00
|
|
|
this.join(group, 'join', username, password);
|
2020-08-12 21:47:05 +02:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2022-01-24 19:59:45 +01:00
|
|
|
After the server has replied to the join request, the `onjoined` callback
|
|
|
|
will trigger. There, you update your user interface and request incoming
|
|
|
|
streams:
|
|
|
|
|
|
|
|
```javascript
|
2023-04-04 23:09:02 +02:00
|
|
|
serverConnection.onjoined = function(kind, group, perms, status, data, error, message) {
|
2022-01-24 19:59:45 +01:00
|
|
|
switch(kind) {
|
|
|
|
case 'join':
|
|
|
|
this.request({'':['audio','video']});
|
|
|
|
// then update the UI, possibly taking perms.present into account
|
|
|
|
break;
|
|
|
|
case 'change':
|
|
|
|
// update the UI
|
|
|
|
break;
|
|
|
|
case 'redirect':
|
|
|
|
this.close();
|
|
|
|
document.location.href = message;
|
|
|
|
break;
|
|
|
|
case 'fail':
|
2023-04-04 23:09:02 +02:00
|
|
|
if(error === 'need-username') {
|
|
|
|
// the user attempted to login with a token that does not
|
|
|
|
// specify a username. Display a dialog requesting a username,
|
|
|
|
// then join again
|
|
|
|
} else {
|
|
|
|
// display the friendly error message
|
|
|
|
}
|
2022-01-24 19:59:45 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2020-08-14 15:22:50 +02:00
|
|
|
|
2020-08-12 21:47:05 +02:00
|
|
|
## Sending and receiving chat messages
|
|
|
|
|
2020-08-14 15:22:50 +02:00
|
|
|
Once you have joined a group, you send chat messages with the `chat`
|
2020-12-19 02:37:07 +01:00
|
|
|
method of the `ServerConnection` class. No permission is needed to do that.
|
2020-08-14 15:22:50 +02:00
|
|
|
|
|
|
|
```javascript
|
2020-12-19 02:37:07 +01:00
|
|
|
serverConnection.chat(username, '', id, 'Hi!');
|
2020-08-12 21:47:05 +02:00
|
|
|
```
|
2020-08-14 15:22:50 +02:00
|
|
|
|
2020-08-12 21:47:05 +02:00
|
|
|
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.
|
|
|
|
|
2022-01-24 19:59:45 +01:00
|
|
|
|
2020-12-19 02:37:07 +01:00
|
|
|
## Other messages
|
|
|
|
|
|
|
|
The `usermessage` method of the `ServerConnection` is similar to the
|
|
|
|
`chat` method, but it sends an application-specific message. Just like
|
|
|
|
chat messages, application-specific messages are not interpreted by the
|
|
|
|
server; unlike chat messages, they are not kept in the chat history.
|
|
|
|
|
|
|
|
The `useraction` method is used to ask the server to act on a remote user
|
|
|
|
(kick it, change its permissions, etc.); similarly, the `groupaction`
|
|
|
|
class requests an action to be performed on the current group. Most
|
|
|
|
actions require either the `Op` or the `Record` permission.
|
|
|
|
|
2020-08-12 21:47:05 +02:00
|
|
|
## Accepting incoming video streams
|
|
|
|
|
|
|
|
When the server pushes a stream to the client, the `ondownstream` callback
|
2020-08-14 15:22:50 +02:00
|
|
|
will trigger; you should set up the stream's callbacks here.
|
|
|
|
```javascript
|
|
|
|
serverConnection.ondownstream = function(stream) {
|
|
|
|
stream.onclose = ...;
|
|
|
|
stream.onerror = ...;
|
|
|
|
stream.ondowntrack = ...;
|
|
|
|
stream.onstatus = ...;
|
|
|
|
}
|
2020-08-12 21:47:05 +02:00
|
|
|
```
|
2020-08-14 15:22:50 +02:00
|
|
|
|
2021-04-28 17:00:50 +02:00
|
|
|
The `stream.label` field is one of `camera`, `screenshare` or `video`.
|
2020-08-14 15:22:50 +02:00
|
|
|
|
2020-08-12 21:47:05 +02:00
|
|
|
After a new stream is created, `ondowntrack` will be called whenever
|
2021-01-31 19:00:09 +01:00
|
|
|
a track is added.
|
2020-08-12 21:47:05 +02:00
|
|
|
|
2020-08-14 15:22:50 +02:00
|
|
|
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.
|
|
|
|
|
2021-01-14 14:56:15 +01:00
|
|
|
The `onclose` callback is called when the stream is destroyed, either by
|
2021-01-31 19:00:09 +01:00
|
|
|
the server or in response to a call to the `close` method. The optional
|
|
|
|
parameter is true when the stream is being replaced by a new stream; in
|
|
|
|
that case, the call to `onclose` will be followed with a call to
|
|
|
|
`onstream` with the same `localId` value.
|
2020-08-14 15:22:50 +02:00
|
|
|
|
2020-08-12 21:47:05 +02:00
|
|
|
## Pushing outgoing video streams
|
|
|
|
|
|
|
|
If you have the `present` permission, you may use the `newUpStream` method
|
2020-08-14 15:22:50 +02:00
|
|
|
to push a stream to the server. Given a `MediaStream` called `localStream`
|
|
|
|
(as obtained from `getUserMedia` or `getDisplayMedia`).
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
let stream = serverConnection.newUpStream();
|
2021-04-28 17:00:50 +02:00
|
|
|
stream.label = ...;
|
2020-08-12 21:47:05 +02:00
|
|
|
stream.onerror = ...;
|
|
|
|
stream.onstatus = ...;
|
|
|
|
localStream.getTracks().forEach(t => {
|
|
|
|
c.pc.addTrack(t, c.stream);
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
2021-01-31 19:00:09 +01:00
|
|
|
The `newUpStream` method takes an optional parameter. If this is set to
|
|
|
|
the `localId` property of an existing stream, then the existing stream
|
|
|
|
will be closed and the server will be informed that the new stream
|
|
|
|
replaces the existing stream.
|
|
|
|
|
2020-08-14 15:22:50 +02:00
|
|
|
## Stream statistics
|
2020-08-12 21:47:05 +02:00
|
|
|
|
2020-12-19 02:37:07 +01:00
|
|
|
Some statistics about streams are made available by calling the
|
|
|
|
`setStatsInterval` method and setting the `onstats` callback. These
|
|
|
|
include the data rate for streams in the up direction, and the average
|
|
|
|
audio energy (the square of the volume) for streams in the down direction.
|
2020-08-12 21:47:05 +02:00
|
|
|
|
|
|
|
--- Juliusz Chroboczek <https://www.irif.fr/~jch/>
|