undefined

Multiparty Video Conferencing feature overview

ConnectyCube Multiparty Video Conferencing API is built on top of WebRTC protocol and based on top of WebRTC SFU architecture.

Max people per Conference call is 12.

Video Conferencing is available starting from Hobby plan (you still can play with it on a Free plan).

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

Features supported

  • Video/Audio Conference with up to 12 people
  • Join-Rejoin video room functionality (like Skype)
  • Guest rooms (coming soon)
  • Mute/Unmute audio/video streams
  • Display bitrate (coming soon)
  • Switch video input device (camera)
  • Switch audio input device (microphone)

Get started with SDK

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

Code sample

There is ready-to-go FREE Conference Calls Sample to help you better understand how to integrate video calling capabilities in your apps.

Preparations

Required preparations for mobile platforms

iOS

Add the following entry to your Info.plist file, located in <project root>/ios/Runner/Info.plist:

<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) Camera Usage!</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) Microphone Usage!</string>

This entry allows your app to access camera and microphone.

Android

Ensure the following permission is present in your Android Manifest file, located in <project root>/android/app/src/main/AndroidManifest.xml:

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

The Flutter project template adds it, so it may already be there.

Also you will need to set your build settings to Java 8, because official WebRTC jar now uses static methods in EglBase interface. Just add this to your app level build.gradle:

android {
    //...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

If necessary, in the same build.gradle you will need to increase minSdkVersion of defaultConfig up to 18 (currently default Flutter generator set it to 16).

ConferenceClient setup

To manage Conference calls in flutter you should use ConferenceClient. Please see code below to find out possible functionality.

ConferenceClient callClient = ConferenceClient.instance; // returns instance of ConferenceClient

Create call session

In order to use Conference Calling API you need to create a session object - set your current user and a type of session (VIDEO or AUDIO optional). ConferenceSession creates via ConferenceClient:

ConferenceClient callClient = ConferenceClient.instance;

int callType = CallType.VIDEO_CALL; // or CallType.AUDIO_CALL 

ConferenceSession callSession = callClient.createCallSession(currentUserId, callType);

Add listeners

callSession.onLocalStreamReceived = (mediaStream) {
  // called when local media stream completely prepared 
};

callSession.onRemoteStreamReceived = (callSession, opponentId, mediaStream) {
  // called when remote media stream received from opponent
};

callSession.onPublishersReceived = (publishers) {
  // called when received new opponents/publishers
};

callSession.onPublisherLeft= (publisher) {
  // called when opponent/publisher left room
};

callSession.onError= (ex) {
  // called when received some exception from conference
};

callSession.onSessionClosed = (callSession){
  // called when current session was closed
};

Also, there is RTCSessionStateCallback in callSession to manage connection state with user:

//CallClass implements RTCSessionStateCallback<ConferenceSession>
callSession.setSessionCallbacksListener(this);
...

@override
void onConnectedToUser(ConferenceSession session, int userId) {}

@override
void onConnectionClosedForUser(ConferenceSession session, int userId) {}

@override
void onDisconnectedFromUser(ConferenceSession session, int userId) {}

Join video room

Just join the room and for example, send invite to some opponent:

callSession.joinDialog(roomId, ((publishers) {
    startCall(roomId, opponents, callSession.currentUserId);// event by system message e.g.
  }
}));

Subscribe/unsubscribe

When you get onPublishersReceived you can subscribe to the active publisher:

callSession.subscribeToPublisher(publisher)

To unsubscribe from publisher:

callSession.unsubscribeFromPublisher(publisher);

Leave

To leave current room session:

callSession.leave();

Mute audio

bool mute = true; // false - to unmute, default value is false
callSession.setMicrophoneMute(mute);

Switch audio output

bool enabled = false; // true - to switch to sreakerphone, default value is false
callSession.enableSpeakerphone(enable);

Mute video

bool enabled = false; // true - to enable local video track, default value for video calls is true
callSession.setVideoEnabled(enabled);

Switch video cameras

callSession.switchCamera().then((isFrontCameraSelected){
  if(isFrontCameraSelected) {
    // front camera selected
  } else {
    // back camera selected
  }
}).catchError((error) {
  // switching camera failed
});

Configurations

ConnectyCube Flutter SDK provides possibility to change some default parameters for call session.

Media stream configurations

Use instance of RTCMediaConfig class to change some default media stream configs.

RTCMediaConfig mediaConfig = RTCMediaConfig.instance;
mediaConfig.minHeight = 720; // sets preferred minimal height for local video stream, default value is 360 
mediaConfig.minWidth = 1280; // sets preferred minimal width for local video stream, default value is 640 
mediaConfig.minFrameRate = 30; // sets preferred minimal framerate for local video stream, default value is 30 

Call connection configurations

ConferenceConfig.instance.url = SERVER_ENDPOINT // 'wss://...:8989';

Signaling implementation

To implement regular calls with events such as call, reject, hang up there should be used some signaling mechanism.

Signaling

As signaling mechanism there can be used ConnectyCube system-messages with predefined custom properties.

Start Call

Just join the room and send an invitation start call message to opponents:

 var systemMessagesManager = CubeChatConnection.instance.systemMessagesManager;
...

sendCallMessage(String roomId, List<int> participantIds) {
 List<CubeMessage> callMsgList = _buildCallMessages(roomId, participantIds);
 callMsgList.forEach((callMsg) {
   callMsg.properties["callStart"] = '1';
   callMsg.properties["participantIds"] = participantIds.join(',');
 });
 callMsgList.forEach((msg) => systemMessagesManager.sendSystemMessage(msg));
}

List<CubeMessage> buildCallMessages(String roomId, List<int> participantIds) {
  return participantIds.map((userId) {
    var msg = CubeMessage();
    msg.recipientId = userId;
    msg.properties = {"janusRoomId": roomId};
    return msg;
  }).toList();
}

Reject Call

Send reject message when decline/busy call:

sendRejectMessage(String roomId, bool isBusy, int participantId) {
  List<CubeMessage> callMsgList = buildCallMessages(roomId, [participantId]);
  callMsgList.forEach((callMsg) {
    callMsg.properties["callRejected"] = '1';
    callMsg.properties["busy"] = isBusy.toString();
  });
  callMsgList.forEach((msg) => systemMessagesManager.sendSystemMessage(msg));
}

End call

Send end call message when hangup/answer_timeout call:

sendEndCallMessage(String roomId, List<int> participantIds) {
  List<CubeMessage> callMsgList = _buildCallMessages(roomId, participantIds);
  callMsgList.forEach((callMsg) {
    callMsg.properties["callEnd"] = '1';
  });
  callMsgList.forEach((msg) => systemMessagesManager.sendSystemMessage(msg));
}

Get call events

Listen and parse all call events with systemMessagesManager:

systemMessagesManager.systemMessagesStream.listen((cubeMessage) => parseCallMessage(cubeMessage));
...

parseCallMessage(CubeMessage cubeMessage) {
  final properties = cubeMessage.properties;
  if(properties.containsKey("callStart")) {
    String roomId = properties["janusRoomId"];
    List<int> participantIds = properties["participantIds"].split(',').map((id) => int.parse(id)).toList();
    if(this._roomId == null) {
      this._roomId = roomId;
      this._initiatorId = cubeMessage.senderId;
      this._participantIds = participantIds;
//    handleNewCall();
    }
  } else if(properties.containsKey("callRejected")) {
    String roomId = properties["janusRoomId"];
    bool isBusy = properties["busy"] == 'true';
    if(this._roomId == roomId) {
//    handleRejectCall();
    }
  } else if(properties.containsKey("callEnd")) {
    String roomId = properties["janusRoomId"];
    if(this._roomId == roomId) {
//    handleEndCall();
    }
  }
}

Adding user to call

For adding user to current call you can send invite message with current roomId and participantIds:

sendCallMessage(String roomId, List<int> participantIds) {
 List<CubeMessage> callMsgList = _buildCallMessages(roomId, participantIds);
 callMsgList.forEach((callMsg) {
   callMsg.properties["callStart"] = '1';
   callMsg.properties["participantIds"] = participantIds.join(',');
 });
 callMsgList.forEach((msg) => systemMessagesManager.sendSystemMessage(msg));
}

And then on the receiver side when the new user successfully joins the room, he automatically subscribes to all active participants in current call (at the same time, other participants will receive onPublishersReceived and will be able to subscribe to that new user).