> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quickblox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Video Conference

> Learn how to add video conference calls to your app.

<Warning>
  This feature is available for customers on the **Enterprise plan** only. Take advantage of Enterprise features to unlock new value and opportunities for users. For more information and if you want to request a Demo, please contact us by mail: [enterprise@quickblox.com](mailto:enterprise@quickblox.com).
</Warning>

QuickBlox provides a Multiparty Video Conferencing solution allowing to set up a video conference between 10-12 people. It is built on top of [WebRTC SFU](https://webrtcglossary.com/sfu/) technologies.

Features supported:

* Video/Audio Conference with 10-12 people.
* Join/Rejoin video room functionality (like Skype).
* Mute/Unmute audio/video stream (own and opponents).
* Display bitrate.
* Switch video input device (camera).

## Initialize

In order to start working with Multiparty Video Conferencing API, you need to initialize a conference endpoint. To request the conference server URL, please contact us:[enterprise@quickblox.com](mailto:enterprise@quickblox.com.).

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    String conferenceServer = "your_conference_server";
    ConferenceConfig.setUrl(conferenceServer);
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    val conferenceServer = "your_conference_server"
    ConferenceConfig.setUrl(conferenceServer)
    ```
  </Tab>
</Tabs>

| Argument         | Required | Description              |
| ---------------- | -------- | ------------------------ |
| conferenceServer | yes      | A conference server URL. |

## Create session

Create a session using `createSession()` method. `ConferenceClient` instance is a client model responsible for managing the conference session. `ConferenceClient` has a `setAutoSubscribeAfterJoin` option signifying that your client will be subscribed to all online publishers after joining some room. **By default**, `setAutoSubscribeAfterJoin` is `true`. `ConferenceSession` is a session within a certain video room, managing all current processes.

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    ConferenceClient client = ConferenceClient.getInstance(getApplicationContext());

    QBRTCTypes.QBConferenceType conferenceType = isVideoCall
        ? QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_VIDEO
        : QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_AUDIO;

    client.setAutoSubscribeAfterJoin(true);

    client.createSession(userId, conferenceType, new ConferenceEntityCallback<ConferenceSession>() {
        @Override
        public void onSuccess(ConferenceSession conferenceSession) {
            // session Created Successfully
        }

        @Override
        public void onError(WsException exception) {
            // create Session Error
        }
    });
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    val client = ConferenceClient.getInstance(applicationContext)

    val conferenceType = if (isVideoCall) {
        QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_VIDEO
    } else {
        QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_AUDIO
    }

    client.isAutoSubscribeAfterJoin = true

    client.createSession(userId, conferenceType, object : ConferenceEntityCallback<ConferenceSession> {
        override fun onSuccess(conferenceSession: ConferenceSession?) {
            // session Created Successfully
        }

        override fun onError(exception: WsException?) {
            // create Session Error
        }
    })
    ```
  </Tab>
</Tabs>

## Callbacks

To have the ability to receive callbacks about the current `ConferenceSession` instance state and conference events, you should implement appropriate interfaces. Implement the `QBRTCSessionStateCallback` for tracking connection state:

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    QBRTCSessionStateCallback sessionStateCallback = new QBRTCSessionStateCallback<ConferenceSession>() {
        @Override
        public void onStateChanged(ConferenceSession conferenceSession, BaseSession.QBRTCSessionState sessionState) {

        }

        @Override
        public void onConnectedToUser(ConferenceSession conferenceSession, Integer userId) {

        }

        @Override
        public void onDisconnectedFromUser(ConferenceSession conferenceSession, Integer userId) {

        }

        @Override
        public void onConnectionClosedForUser(ConferenceSession conferenceSession, Integer userId) {

        }
    };

    currentConferenceSession.addSessionCallbacksListener(sessionStateCallback);
    // or
    currentConferenceSession.removeSessionCallbacksListener(sessionStateCallback);

    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    val sessionStateCallback = object : QBRTCSessionStateCallback<ConferenceSession> {
        override fun onStateChanged(conferenceSession: ConferenceSession?, sessionState: BaseSession.QBRTCSessionState?) {

        }

        override fun onConnectedToUser(conferenceSession: ConferenceSession?, userId: Int?) {

        }

        override fun onDisconnectedFromUser(conferenceSession: ConferenceSession?, userId: Int?) {

        }

        override fun onConnectionClosedForUser(conferenceSession: ConferenceSession?, userId: Int?) {

        }
    }

    currentConferenceSession.addSessionCallbacksListener(sessionStateCallback)
    // or
    currentConferenceSession.removeSessionCallbacksListener(sessionStateCallback)
    ```
  </Tab>
</Tabs>

Implement `ConferenceSessionCallbacks` for tracking conference events:

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    ConferenceSessionCallbacks conferenceSessionCallbacks = new ConferenceSessionCallbacks() {
        @Override
        public void onPublishersReceived(ArrayList<Integer> publishersList) {
            // publisher or Publishers (users) joined
        }

        @Override
        public void onPublisherLeft(Integer userId) {
            // publisher left
        }

        @Override
        public void onMediaReceived(String type, boolean success) {
            // media received (audio or video type)
        }

        @Override
        public void onSlowLinkReceived(boolean uplink, int nacks) {
            // called when slowLink is received from server.
            // slowLink with uplink = true - several missing packets from server;
            // uplink = false means server is not receiving all your packets.
        }

        @Override
        public void onError(WsException exception) {
            // error from Server Received
        }

        @Override
        public void onSessionClosed(ConferenceSession conferenceSession) {
            // cession Closed
        }
    };

    currentConferenceSession.addConferenceSessionListener(conferenceSessionCallbacks);
    // or
    currentConferenceSession.removeConferenceSessionListener(conferenceSessionCallbacks);
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    val conferenceSessionCallbacks = object : ConferenceSessionCallbacks {
        override fun onPublishersReceived(publishersList: ArrayList<Int>?) {
            // publisher or Publishers (users) joined
        }

        override fun onPublisherLeft(userId: Int?) {
            // publisher left
        }

        override fun onMediaReceived(type: String?, success: Boolean) {
            // media received (audio or video type)
        }

        override fun onSlowLinkReceived(uplink: Boolean, nacks: Int) {
            // called when slowLink is received from server.
            // slowLink with uplink = true - several missing packets from server;
            // uplink = false means server is not receiving all your packets.
        }

        override fun onError(exception: WsException?) {
            // error from Server Received
        }

        override fun onSessionClosed(conferenceSession: ConferenceSession?) {
            // cession Closed
        }
    }

    currentConferenceSession.addConferenceSessionListener(conferenceSessionCallbacks)
    // or
    currentConferenceSession.removeConferenceSessionListener(conferenceSessionCallbacks)
    ```
  </Tab>
</Tabs>

## Video and audio tracks

For obtaining video and audio tracks implement interface `RTCClientVideoTracksCallbacks` and `RTCClientAudioTracksCallback`.

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    QBRTCClientVideoTracksCallbacks videoTracksCallbacks = new QBRTCClientVideoTracksCallbacks<ConferenceSession>() {
        @Override
        public void onLocalVideoTrackReceive(ConferenceSession conferenceSession, QBRTCVideoTrack videoTrack) {

        }

        @Override
        public void onRemoteVideoTrackReceive(ConferenceSession conferenceSession, QBRTCVideoTrack videoTrack, Integer userId) {

        }
    };

    // add callback
    currentConferenceSession.addVideoTrackCallbacksListener(videoTracksCallbacks);
    // or remove
    currentConferenceSession.removeVideoTrackCallbacksListener(videoTracksCallbacks);

    QBRTCClientAudioTracksCallback audioTracksCallback = new QBRTCClientAudioTracksCallback<ConferenceSession>() {
        @Override
        public void onLocalAudioTrackReceive(ConferenceSession conferenceSession, QBRTCAudioTrack audioTrack) {

        }

        @Override
        public void onRemoteAudioTrackReceive(ConferenceSession conferenceSession, QBRTCAudioTrack audioTrack, Integer userId) {

        }
    };

    // add callback
    currentConferenceSession.addAudioTrackCallbacksListener(audioTracksCallback);
    // or remove
    currentConferenceSession.removeAudioTrackCallbacksListener(audioTracksCallback);
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    val videoTracksCallbacks = object : QBRTCClientVideoTracksCallbacks<ConferenceSession> {
        override fun onLocalVideoTrackReceive(conferenceSession: ConferenceSession?, videoTrack: QBRTCVideoTrack?) {

        }

        override fun onRemoteVideoTrackReceive(conferenceSession: ConferenceSession?, videoTrack: QBRTCVideoTrack?, userId: Int?) {

        }
    }

    // add callback
    currentConferenceSession.addVideoTrackCallbacksListener(videoTracksCallbacks)
    // or remove
    currentConferenceSession.removeVideoTrackCallbacksListener(videoTracksCallbacks)

    val audioTracksCallback = object : QBRTCClientAudioTracksCallback<ConferenceSession> {
        override fun onLocalAudioTrackReceive(conferenceSession: ConferenceSession?, audioTrack: QBRTCAudioTrack?) {

        }

        override fun onRemoteAudioTrackReceive(conferenceSession: ConferenceSession?, audioTrack: QBRTCAudioTrack?, userId: Int?) {

        }
    }

    // Add callback
    currentConferenceSession.addAudioTrackCallbacksListener(audioTracksCallback)
    // Or remove
    currentConferenceSession.removeAudioTrackCallbacksListener(audioTracksCallback)
    ```
  </Tab>
</Tabs>

To render the video track, use the `QBConferenceSurfaceView` as renderer:

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    QBConferenceSurfaceView videoView = findViewById(R.id.opponentView);

    videoTrack.removeRenderer(videoTrack.getRenderer());
    videoTrack.addRenderer(videoView);
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    val videoView = findViewById<QBConferenceSurfaceView>(R.id.opponentView)

    qbrtcVideoTrack.removeRenderer(qbrtcVideoTrack.renderer)
    qbrtcVideoTrack.addRenderer(videoView)
    ```
  </Tab>
</Tabs>

## Join video room

You can join a room as a listener or as a publisher. As a listener, you subscribe only to the publishers, without giving your own video and audio streams.

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    boolean asPublisher = true;
    int userId = currentConferenceSession.getCurrentUserID();

    QBConferenceRole conferenceRole = asPublisher ? QBConferenceRole.PUBLISHER : QBConferenceRole.LISTENER;

    currentConferenceSession.joinDialog(dialogId, conferenceRole, new ConferenceEntityCallback<ArrayList<Integer>>() {
        @Override
        public void onSuccess(ArrayList<Integer> publishers) {

        }

        @Override
        public void onError(WsException exception) {

        }
    });
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    val asPublisher = true
    val userId = currentConferenceSession.currentUserID

    val conferenceRole = if (asPublisher) {
        QBConferenceRole.PUBLISHER
    } else {
        QBConferenceRole.LISTENER
    }

    currentConferenceSession.joinDialog(userId, conferenceRole, object : ConferenceEntityCallback<ArrayList<Int>> {
        override fun onSuccess(publishers: ArrayList<Int>?) {

        }

        override fun onError(exception: WsException?) {

        }
    })
    ```
  </Tab>
</Tabs>

To subscribe to the active publisher, you should use the snippet below:

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    Set<Integer> publishersSet = currentConferenceSession.getActivePublishers();
    for (Integer publisher : publishersSet) {
        currentConferenceSession.subscribeToPublisher(publisher);
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    val publishersSet = currentConferenceSession.activePublishers
    for (publisher in publishersSet) {
        currentConferenceSession.subscribeToPublisher(publisher)
    }
    ```
  </Tab>
</Tabs>

<Note>
  You should subscribe to publishers only when the session state becomes connected. Use `onStateChanged()` callback method to track session states.
</Note>

If you are subscribing as a listener, then you can subscribe to publishers right after the successful `joinDialog()`. To unsubscribe from the publisher, use the `unSubscribeFromPublisher()` method.

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    currentConferenceSession.unsubscribeFromPublisher(publisher);
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    currentConferenceSession.unsubscribeFromPublisher(publisher)
    ```
  </Tab>
</Tabs>

## Mute local audio

Mute the audio by calling `getLocalAudioTrack().setEnabled()` method. Using this method, we can tell SDK to send/not send audio data from a local peer in the specified WebRTC session.

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    currentConferenceSession.getMediaStreamManager().getLocalAudioTrack().setEnabled(isAudioEnabled);
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    currentConferenceSession.mediaStreamManager.localAudioTrack.setEnabled(isAudioEnabled)
    ```
  </Tab>
</Tabs>

## Mute remote audio

You can always get remote audio tracks for a specific user ID in the call using the above-specified `QBRTCSession` methods (assuming that they are existent). You can also mute remote media tracks on your side by changing the value of the enabled property for a specific remote media track.

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    boolean isAudioEnabled = false;

    currentConferenceSession.getMediaStreamManager().getAudioTrack(opponentId).setEnabled(isAudioEnabled);
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    val isAudioEnabled = false

    currentConferenceSession.mediaStreamManager.getAudioTrack(opponentId).setEnabled(isAudioEnabled)
    ```
  </Tab>
</Tabs>

## Disable local video

Turn off the video by calling `getLocalVideoTrack().setEnabled()`. Using this method, we can tell SDK not to send video data from a local peer in the specified session.

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    currentConferenceSession.getMediaStreamManager().getLocalVideoTrack().setEnabled(isVideoEnabled);
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    currentConferenceSession.mediaStreamManager.localVideoTrack.setEnabled(isVideoEnabled)
    ```
  </Tab>
</Tabs>

## Disable remote video

Turn off the video by calling `getVideoTrack(opponentID).setEnabled()`. Using this method, we can tell SDK not to send video data from a remote peer in the specified session.

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    boolean isVideoEnabled = false;

    currentConferenceSession.getMediaStreamManager().getVideoTrack(opponentId).setEnabled(isVideoEnabled);
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    val isVideoEnabled = false

    currentConferenceSession.mediaStreamManager.getVideoTrack(opponentId).setEnabled(isVideoEnabled)
    ```
  </Tab>
</Tabs>

## Leave video room

To leave the current joined video room, use the `leave()` method.

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    currentConferenceSession.leave();
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    currentConferenceSession.leave()
    ```
  </Tab>
</Tabs>

## Camera resolution

The camera resolution is a video stream encoding parameter. It's possible to set the custom video resolution to provide guarantees for the predictable behavior of the video stream.

<Tabs>
  <Tab title="Java">
    ```Java theme={null}
    int videoWidth = QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.width;
    int videoHeight = QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.height;

    // VGA Resolution
    //videoWidth = QBRTCMediaConfig.VideoQuality.VGA_VIDEO.width;
    //videoHeight = QBRTCMediaConfig.VideoQuality.VGA_VIDEO.height;

    // HD Resolution
    //videoWidth = QBRTCMediaConfig.VideoQuality.HD_VIDEO.width;
    //videoHeight = QBRTCMediaConfig.VideoQuality.HD_VIDEO.height;

    // custom Resolution (for example FullHD)
    //videoWidth = 1920;
    //videoHeight = 1080;

    QBRTCMediaConfig.setVideoWidth(videoWidth);
    QBRTCMediaConfig.setVideoHeight(videoHeight);
    ```
  </Tab>

  <Tab title="Kotlin">
    ```Kotlin theme={null}
    var videoWidth = QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.width
    var videoHeight = QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.height

    // VGA Resolution
    //videoWidth = QBRTCMediaConfig.VideoQuality.VGA_VIDEO.width
    //videoHeight = QBRTCMediaConfig.VideoQuality.VGA_VIDEO.height

    // HD Resolution
    //videoWidth = QBRTCMediaConfig.VideoQuality.HD_VIDEO.width
    //videoHeight = QBRTCMediaConfig.VideoQuality.HD_VIDEO.height

    // Custom Resolution (for example FullHD)
    //videoWidth = 1920
    //videoHeight = 1080

    QBRTCMediaConfig.setVideoWidth(videoWidth)
    QBRTCMediaConfig.setVideoHeight(videoHeight)
    ```
  </Tab>
</Tabs>
