calls

intergrating calls to your application is easy and straight forward with the @cyxth/calls npm package.

prerequisites

first make sure you have created a cyxth account and created a cyxth instance, and make sure to check out authorization and concepts.

next download @cyxth/core and @cyxth/calls from packages

# cyxth core
npm install @cyxth/core

# cyxth chat
npm install @cyxth/calls

# optionally add types 
npm install --save-dev @cyxth/types

This guide gives a high level overview and cyxth calls concepts, feel free to check the calls api reference and colab examples after this guide. for a deeper understanding of various topics.

initialization

calls is a cyxth core plugin and is instantiated like the other plugins to get started with calls use calls() function from cyxth core module.

import Cyxth from '@cyxth/core';
import Calls from '@cyxth/calls';

// initialize cyxth
const cyxth = new Cyxth("YOUR_APP_URL");

cyxth.register([
    Calls,
    // ... other modules
]);

// authorize user
await cyxth.connect(USER_TOKEN_SMH);

// get calls
const calls : Calls = cyxth.calls();

the user has to be connected to cyxth or else cyxth.calls() will fail.

call flow

at a glance implementing calls to your application with cyxth calls follows these three simple steps

create or join call

first a user creates a call or joins one

const myCall : Calls = cyxth.calls();
await myCall.create('channelId');

// or join
// cyxth.on("newcall", async ({channelId}) => {
//     await myCall.join('channelId')
// })

listen for call events

next we listen for call and channel events to know when a user joins, adds a video or audio track, removes a track, is muted, is speaking, etc and update the local application state to reflect the same

myCall.on("track:add",(data) => {
    // ... update user tracks and show in ui
})

myCall.on("user:left",({userId}) => {
    // ... remove user from view
})

// .. listen for more event

in calls controls

these are your typical call control buttons at the bottom of the screen mute, video, audio, etc plus if a user is a call admin controls for other users like mute others, disable publishing and receiving certain tracks by various users in call etc.

const enableMic = () => {
    const media = await navigator.mediaDevices.getUserMedia({audio:true})
    const tracks = {
        "mic_0": media.getAudioTracks[0],
    };
    await myCall.addTracks(tracks);
}

//.. more call controls + actions

// add trigger to your call, for example
micOnBtn.addEventListener("click", enableMic);

with these three steps, you are able to integrate the full call experience to your application and customize it to your specific needs or use it in your framework of choice. check out the demos on various implementation examples, PS they all take these three steps

tracks

cyxth calls works primarily on MediaStreamTracks rather than media streams. developers are required to provide labelled tracks sourced from streams i.e camera and mic with getUserMedia, screen capture with the screen capture API or canvas capture , etc. anything that can be turned to a MediaStreamTrack is supported.

for example to get user camera feed and mic

const media = await navigator.mediaDevices.getUserMedia({audio:true, video: true})

const tracks = {
    "mic_0": media.getAudioTracks[0],
    "cam_0": media.getVideoTracks[0],
};

// use to create call
await myCall.create('channelId', tracks)

// or add to existing call
// await myCall.addTracks(tracks);

the track labels have to be unique and are used to control the specific tracks i.e muteTracks, removeTracks, getUsers on the call instance, labelled tracks also used on various call events such as track:add, track:mute.

the choosen media tracks are affected by the application codecs and call quality setting on the cyxth console and the call configuration. i.e if HD video is not enabled adding tracks with high video quality will fail so always match the constraints with what is set in the console.

controls & actions

calls implements the channel interface, the common channel operations such as create, join, leave, e.t.c work with calls and emit the usual channel events. all this is covered in the concepts, we will go through them at high level but be sure to check the excellent calls channel reference, channel events and user events for a deeper understanding on the same. the call channel has various methods to add users, moderate users, mute users etc.

user moderation enables you to build various call presets by limiting what a user can send or receive in a call, i.e a group call (many to many) where all users can send and receive media , a stream(few to many) where a few users are allowed to send media and many users can receive. one to one private calls and anything in between specific to your use case.

for a deeper dive into various controls and actions check this out.

configuration

application wide call configuration is done at the cyxth console. call quality, supported codecs, maximum number of users per call instance, max tracks etc. check the call configuration page for all these settings.

application users can also create local configuration for a specific call instance like the allowed MediaKinds, maximum number of users, default permissions and join requests check this for the full configuration

events

use the on method to listen to various call events, here is an overview of the events, for a great user experience it is recommended that you listen to all this events and update the user view accordingly. for example if a user leaves the call make sure you remove their container or view as they will stay frozen contributing to bad ux, this goes for mute, track:add, track:del, cnl:del(call ended) and all the events

on<K extends keyof CallEventMap>(event: K, handler: (ev: CallEventMap[K]) => any): void;

interface CallEventMap{
    "cnl:config": { config: Config; senderId: string;  };
    "cnl:create": { config: Config; senderId: string;  };
    "cnl:delete": { reason?: string; senerId: string;  };
    "track:add": TrackAddEvent;
    "track:del": CallEvent;
    "track:mute": [TrackMuteEvent];
    "user:add": { users: User[];  };
    "user:del": { senderId: string; users: string[];  };
    "user:join": { userId: string;  };
    "user:left": { userId: string;  };
    "user:mod": { senderId: string; users: User[];  };
}