undefined

Video Calling

ConnectyCube Video Calling P2P API is built on top of WebRTC protocol and based on top of WebRTC Mesh architecture.

Max people per P2P call is 4.

To get a difference between P2P calling and Conference calling please read our ConnectyCube Calling API comparison blog page.

Get started with SDK

Follow the Getting Started guide on how to connect ConnectyCube SDK and start building your first app.

Code samples

There is ready-to-go FREE code sample to help you better understand how to integrate video calling capabilities in your apps:

  • Video Chat code sample for React Native src code

Preparations

ConnectyCube Chat API is used as a signaling transport for Video Calling API, so in order to start using Video Calling API you need to connect user to Chat.

Connect WebRTC lib

To make calling works under React Native, we use react-native-webrtc lib.

You need to strictly follow the react-native-webrtc Installation guide in order to make it works properly.

RN 0.62

In a case you face any unexpected crashes with React Native 0.62 and WebRTC lib - there is a Flipper related crash and a working solution.

Create video session

In order to use Video Calling API you need to create a session object - choose your opponents with whom you will have a call and a type of session (VIDEO or AUDIO):

const calleesIds = [56, 76, 34]; // User's ids
const sessionType = ConnectyCube.videochat.CallType.VIDEO; // AUDIO is also possible
const additionalOptions = {};
const session = ConnectyCube.videochat.createNewSession(calleesIds, sessionType, additionalOptions);

In a case of low bandwidth network, you can try to limit the call bandwidth cap to get better quality vs stability results. It can be done by passing const additionalOptions = {bandwidth: 256}; or 128 value.

Access local media stream

In order to have a video chat session you need to get an access to the user's devices (webcam / microphone):

const mediaParams = {
  audio: true,
  video: true
};

session
  .getUserMedia(mediaParams)
  .then((localStream) => {})
  .catch((error) => {});

This method lets the browser ask the user for permission to use devices. You should allow this dialog to access the stream. Otherwise, the browser can't obtain access and will throw an error for getUserMedia callback function.

For more information about possible audio/video constraints, here is a good code sample from WebRTC team how to work with getUserMedia constraints: https://webrtc.github.io/samples/src/content/getusermedia/resolution/

Attach local media stream

Then you should attach your local media stream to a video element:

import {RTCView} from 'react-native-connectycube';

// pass a local or remote stream to the RTCView component
...
<RTCView  objectFit="cover" style={styles.rtcView} key={userId} streamURL={localStream.toURL()} />
...

Initiate a call

const extension = {};
session.call(extension, (error) => {});

The extension is used to pass any extra parameters in the request to your opponents.

After this, your opponents will receive a callback call:

ConnectyCube.videochat.onCallListener = function (session, extension) {};

Or if your opponents are offline or did not answer the call request:

ConnectyCube.videochat.onUserNotAnswerListener = function (session, userId) {};

Accept a call

To accept a call the following code snippet is used:

ConnectyCube.videochat.onCallListener = function (session, extension) {
  // Here we need to show a dialog with 2 buttons - Accept & Reject.
  // By accepting -> run the following code:
  //
  // 1. await session.getUserMedia (...)
  //
  // 2. Accept call request:
  const extension = {};
  session.accept(extension);
};

After this, your opponents will get a confirmation in the following callback:

ConnectyCube.videochat.onAcceptCallListener = function (session, userId, extension) {};

Also, both the caller and opponents will get a special callback with the remote stream:

ConnectyCube.videochat.onRemoteStreamListener = function (session, userID, remoteStream) {
  // attach the remote stream to a video element

  // import {RTCView} from 'react-native-connectycube';
  //
  // <RTCView  objectFit="cover" style={styles.rtcView} key={userId} streamURL={remoteStream.toURL()} />
};

From this point, you and your opponents should start seeing each other.

Receive a call in background

For mobile apps, it can be a situation when an opponent's user app is either in closed (killed) or background (inactive) state.

In this case, to be able to still receive a call request, you can use Push Notifications. The flow should be as follows:

  • a call initiator should send a push notification along with a call request
  • when an opponent's app is killed or in background state - an opponent will receive a push notification about an incoming call, and will be able to accept/reject the call. If accepted or pressed on a push notification - an app will be opened, a user should auto login and connect to chat and then will be able to join an incoming call.

Please refer to Push Notifications API guides regarding how to integrate Push Notifications in your app:

For even better integration - CallKit and VoIP push notifications can be used. Please check CallKit and VoIP push notifications section on each platform Push Notifications API guide page.

CallKit

In a case you need to show a native calling interface - you need to integrate a CallKit functionality via CallKeep lib.

For iOS, this will also require to integrate VoIP push notifications along with CallKeep.

For the Push Notifications integration - please refer ConnectyCube Push Notifications API guide

Reject a call

const extension = {};
session.reject(extension);

After this, the caller will get a confirmation in the following callback:

ConnectyCube.videochat.onRejectCallListener = function (session, userId, extension) {};

End a call

const extension = {};
session.stop(extension);

After this, the opponents will get a confirmation in the following callback:

ConnectyCube.videochat.onStopCallListener = function (session, userId, extension) {};

Monitor session connections state

There is a callback function to track the session state:

ConnectyCube.videochat.onSessionConnectionStateChangedListener = function (session, userID, connectionState) {};

The possible values of connectionState are those of an enum of type ConnectyCube.videochat.SessionConnectionState:

  • ConnectyCube.videochat.SessionConnectionState.UNDEFINED
  • ConnectyCube.videochat.SessionConnectionState.CONNECTING
  • ConnectyCube.videochat.SessionConnectionState.CONNECTED
  • ConnectyCube.videochat.SessionConnectionState.FAILED
  • ConnectyCube.videochat.SessionConnectionState.DISCONNECTED
  • ConnectyCube.videochat.SessionConnectionState.CLOSED
  • ConnectyCube.videochat.SessionConnectionState.COMPLETED

Mute audio

session.mute("audio");
session.unmute("audio");

Mute video

session.mute("video");
session.unmute("video");

Switch video cameras

localStream.getVideoTracks().forEach(track => track._switchCamera());

Switch audio output

  1. connect and install https://github.com/react-native-webrtc/react-native-incall-manager lib
  2. Use InCallManager class to switch audio output:
import InCallManager from 'react-native-incall-manager';

...

let isSpeakerOn = true; // false

InCallManager.setSpeakerphoneOn(isSpeakerOn);

Also, pay attention to InCall Manager lib if you need to use one of the options below:

  • Manage devices events like wired-headset plugged-in state, proximity sensors and expose functionalities to javascript.
  • Automatically route audio to proper devices based on events and platform API.
  • Toggle speaker or microphone on/off, toggle flashlight on/off
  • Play ringtone/ringback/dtmftone

Screen Sharing

Not available as for now

Group video calls

Because of Mesh architecture we use for multipoint where every participant sends and receives its media to all other participants, current solution supports group calls with up to 4 people.

Also ConnectyCube provides an alternative solution for up to 12 people - Multiparty Video Conferencing API.

Configuration

There are various calling related configs that can be changed.

alwaysRelayCalls

The alwaysRelayCalls config sets the WebRTC RTCConfiguration.iceTransportPolicy config. Setting it to true means the calling media will be routed through TURN server all the time, and not directly P2P between users even if the network path allows it:

const appConfig = {
  videochat: {
    alwaysRelayCalls: true,
  },
};

Echo cancelation issue on some Android devices

A possible solution can be found here https://github.com/react-native-webrtc/react-native-webrtc/issues/713

Recording

Not available as for now

Continue call in background

If you are developing dedicated apps for iOS and Android - it's required to apply additional configuration for the app to keeping the call alive when it goes into the background.

iOS:

There is no way to continue a video call in background because of some OS restrictions. What is supported there is to continue with voice calling while an app is in background. Basically, the recommended to achieve this is to switch off device camera when an app goes to background and then switch camera on back when an app goes to foreground.

Furthermore, even voice background call are blocked by default on iOS. To unblock - you need to setup proper background mode capabilities in your project. Please find the Enabling Background Audio link with more information how to do it properly.

Android:

For Android, we also recommend to implement the same camera switch flow when go to background and then return to foreground.

To keep the call while in background, we recomment to use the react-native-background-actions library.

The flow is the following:

  • when start a call or go background -> display notification
  • when stop call or return to foreground -> hide notification
// Background Activity
import BackgroundService from 'react-native-background-actions';

startBackgroundMode = () => {
  this.stopBackgroundMode();

  const options = {
    taskName: 'AppName',
    taskTitle: 'You have an active call',
    taskDesc: 'Press to return',
    taskIcon: {
      name: 'ic_notification',
      type: 'mipmap',
    },
    linkingURI: 'appName://foreground',
  };

  BackgroundService.start(async (params) => {
    await new Promise(async () => {});
  }, options);
};

stopBackgroundMode = (force = false) => {
  if (BackgroundService.isRunning() || force) {
    return BackgroundService.stop();
  }
};