@airtame/webrtc
v4.0.0
Published
WebRTC React library.
Downloads
55
Readme
Airtame WebRTC abstraction library
Introduction
This library is intended to be used for Airtame use-cases. Namely standalone streaming and streaming during a video call.
It supports, 1-1, 1-N, N-1, N-N types calls. The library is very opinionated and should only be used as intended.
In the library 2 things have to be understood:
- The streamer connection is always the one to create the data channel and to initiate the media connection. Additionally, it will always be the "impolite" peer (see Perfect Negotiation Pattern).
- The library is not tested to support bi-directional media meaning that we haven't tested if a receiver can easily send data and a streamer receive some.
Usage
Build
To build the library:
# Install the dependencies
npm install
# Run the build script
npm run build
After that you have the artifacts in the dist/
folder.
Run the examples
# Install the dependencies
npm install
# Start the dev server
npm run start
You can now go to http://localhost:8080/public/example/streamer
for the streamer and
http://localhost:8080/public/example/receiver
for the receiver.
To implement
There are only two raw object in this library: WebRTCApplication
and WebRTCConnection
. For
conveniance we also provide a React
wrapper: WebRTCApplicationProvider
.
Here are the outline of both main object:
WebRTCApplication
:
class WebRTCApplication {
/*
* Create a connection that will be used by a receiver.
*
* @returns A tuple containing the newly created connection and the answer to the offer.
*/
public acceptConnection(id: string, offer: string): Promise<[Connection, string]>;
/*
* Create a connection that will be used by a streamer.
*
* @returns A tuple containing the newly created connection and the offer.
*/
public connect(id: string): Promise<[Connection, string]>;
}
WebRTCConnection
:
class WebRTCConnection extends EventEmitter {
/*
* Returns the main RTCPeerConnection used for data communication.
*/
get mainPeerConnection(): RTCPeerConnection;
/*
* Returns the RTCPeerConnection used to transport media. Can be undefined is no streaming
* session is started.
*/
get mediaPeerConnection(): RTCPeerConnection | undefined;
/*
* Returns the current state of the main connection - data channel (see below for the state).
*/
get connectionState(): ConnectionState;
/*
* Returns the current state of the media connection (see below for the state).
*/
get mediaConnectionState(): ConnectionState;
/*
* Initialize the connection to be used as a receiving connection. That is, to be
* used in a receiver.
*
* @returns The answer to the offer provided in argument.
*/
public initReceiver(offer: string): Promise<string>;
/*
* Initialize the connection to be used as a sending connection. That is, to be
* used in a streamer.
*
* @returns The offer.
*/
public initSender(): Promise<string>;
/*
* Disconnect the full application, media stream included. Will emit the `CLOSED` event on
* the CONNECTION_STATE and the MEDIA_STATE.
* The connection is not usable from that point, and you need to call `connect` once again to
* create a new connection.
*/
public disconnect(): void;
/*
* Start the streaming of media data to the receiver.
*
* *This is only used in a streamer otherwise it will throw an error*.
*
* NOTE:
* -----
* This is the responsibility of the caller to stop the mediaStream tracks when
* needed. The WebRTC API is not responsible for it when calling stopStream.
*/
public startStream(ms: MediaStream, codecFilter?: CodecFilterPredicate): void;
/*
* Stop the stream connection. Stop all the tracks on the media stream and close the connection.
* The state closed will be emitted.
*/
public stopStream(): void;
/*
* Set the initial answer provided by a receiver to a streamer.
*
* *This is only used in a streamer otherwise it will throw an error*.
*/
public setAnswer(answer: string): Promise<void>;
/*
* Send a message to the peer connection. This is used in both the receiver and streamer.
*
* Notes: The kind is used to identify the message being sent and the message is the content.
* Notes: The kind shouldn't start with `__`. This is reserved to the internal usage of the
* library.
*/
public sendMessage(kind: string, message: string): void;
}
Additionally, we have a connection state enum
:
enum ConnectionState {
// When nothing started, for example:
// * No receiver nor streamer are event initialized
// * For media state, it would mean that no streaming session is started
NOT_INITIALIZED = 'not-initialized',
// When somethig is initialized like the streamer or receiver, or stream is called
INITIALIZED = 'initialized',
// When the connections are starting to their initialization and connection
LOADING = 'loading',
// When a session is started (main or media)
OK = 'ok',
// When a connection looses contact with the peer, but still trying to connect
DISCONNECTED = 'disconnected',
// When the disconnection passes a timeout, it turns out to this state
FAILED = 'failed',
// When a connection closed properly
CLOSED = 'closed',
// Other more generic errors
ERROR = 'error',
}
For a complete usage example (using the React
wrapper), please refer to the examples.
Filtering codecs
In some specific use cases, you might want to limit the codec used by the streamer. For example this is what happens with our Zoom SIP application.
Here is an example:
connection.startStream(mediaStream, (codecs: RTCRtpCodecCapability[]) =>
codecs.filter(
(codec) =>
(codec.mimeType.toLowerCase() === 'video/h264' &&
codec.sdpFmtpLine?.match(/42e01f/) &&
codec.sdpFmtpLine.match(/packetization-mode=0/)) ||
codec.mimeType.toLowerCase() === 'video/rtx'
)
);
Events
An enum Events is available to be used to subscribe to events:
// See below for specific explanation of how to use those events and when.
export enum Events {
MESSAGE = 'message',
STREAM = 'stream',
CONNECTION_STATE_CHANGE = 'connectionStateChange',
MEDIA_STATE_CHANGE = 'mediaStateChange',
}
The Connection object described above implements EventEmitter
and has some built in events to be
aware of:
message
: Used for the text communication between 2 peers. This is generated by both kind of connections.connection.on(Events.MESSAGE, (data) => { const message = JSON.parse(data); if (message.kind === 'my_great_message') { doSomething(message.message); } });
stream
: Used by the receiver when it receives media data from the streamer.connection.on(Events.STREAM, (stream) => { if (video && video.current) { video.current.srcObject = stream; } });
connectionStateChange
: Emitted when the main RTC session state changed. The emitter callback will have the signature(state: ConnectionState, message?: string): void
.connection.on(Events.CONNECTION_STATE_CHANGE, (state, message) => { if (state === ConnectionState.OK) { console.log('Connected successfully'); } else if (state === ConnectionState.ERROR) { console.log(`Error: ${message}`); } });
mediaStateChange
: Emitted when the media streaming session state changed. The emitter callback will have the signature(state: ConnectionState, message?: string): void
.connection.on(Events.MEDIA_STATE_CHANGE, (state, message) => { if (state === ConnectionState.OK) { console.log('Connected successfully'); } else if (state === ConnectionState.ERROR) { console.log(`Error: ${message}`); } });