npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@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}`);
      }
    });