# Watch Together and Synchronization SDKs

## Watch Together

Watch Together video-chat is a general-purpose video chatting infrastructure that is aimed to provide high-quality Audio and Video functionality that will allow you to create and provide engaging experiences in your application for your customers.

## Synchronization

The Synch SDK will allow you to create groups of people that are watching a video stream and synchronize the content the group is watching across all platforms.

**The result** is a synchronized watch-together experience!

## The Watch Together and synchronization SDKs work together<br>

![](/files/-MXcbxC9DFjMCxadO-9S)

## The result

![](/files/-MXcSaKfwIsqeeqpxQl5)


# Watch Together SDK overview

## Overview <a href="#watchtogetheroverview-overview" id="watchtogetheroverview-overview"></a>

Watch Together video-chat is a general-purpose video chatting infrastructure that is aimed to provide high-quality Audio and Video functionality that will allow you to create and provide engaging experiences in your application for your customers.

To register for our service please contact us at <Support@sceenic.co>.

If you already have credentials to the private area, login and retrieve your API\_KEY and API\_SECRET - [Private area](https://media.sceenic.co/)

### Resources

| Resources                                                                                           | SDKs                                                                                                                                |
| --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| [Android Java](https://bitbucket.org/svmt/samplewatchtogether-java-2.0/src/master/)                 | [Web SDK](https://media.sceenic.co/) \*                                                                                             |
| [Android Kotlin](https://bitbucket.org/svmt/samplewatchtogether-kotlin-2.0/src/master/)             | [Android SDK](https://media.sceenic.co/) \*                                                                                         |
| [iOS Objective-c adapter](https://bitbucket.org/svmt/watchtogether-ios-objc/src/master/)            | [i](https://svmtse.atlassian.net/wiki/spaces/SC/pages/75595781/iOS+SDK+-+Swift+-+Garbage+bin)[OS SDK](https://media.sceenic.co/) \* |
| [iOS Swift](https://bitbucket.org/svmt/watchtogether-ios-swift/src/master/)                         |                                                                                                                                     |
| [Web JS sample application code](https://bitbucket.org/svmt/watchtogethersampleappjs/src/master/)   |                                                                                                                                     |
| [React sample application code](https://bitbucket.org/svmt/watchtogethersampleappreact/src/master/) |                                                                                                                                     |

( \* ) SDKs are accessible only from the private area

## Watch Together platform <a href="#watchtogetheroverview-watchtogetherplatform" id="watchtogetheroverview-watchtogetherplatform"></a>

Watch together video-chat application will allow you to create and manage interactive Audio and Video sessions for multiple participants and embed it in your web or mobile applications.\
We will provide you with SDKs for iOS, Android and Web to create and manage your experience.

Watch Together architecture is a client-server architecture whereby:

* Clients - Web, iOS, or Android SDKs which will allow you to manage all the video chat logic (Web {[JS](https://bitbucket.org/svmt/watchtogethersampleappjs/src/master/)
* , [React](https://bitbucket.org/svmt/watchtogethersampleappreact/src/master/)}, iOS {[Swift](https://bitbucket.org/svmt/watchtogether-ios-swift/src/master/), [Obj-c-adpater](https://bitbucket.org/svmt/watchtogether-ios-objc/src/master/)} and Android {[Kotlin](https://bitbucket.org/svmt/samplewatchtogether-kotlin-2.0/src/master/), [Java](https://bitbucket.org/svmt/samplewatchtogether-java-2.0/src/master/)}
* Server - which exposes authentication functionality and responsible for providing a valid token for the various clients ([Authentication overview](/watch-together-sdk/sscale-confluence-overview))

**System flow:**

![](/files/-Lrnwj4Cat1anmT6DPvq)

You can expect the following functionality:

### Client SDKs <a href="#watchtogetheroverview-clients" id="watchtogetheroverview-clients"></a>

* Session management functionality
* Connecting and disconnecting from a Session
* Managing Video and Audio streams
  * Enable/Disable video
  * Enable/Disable audio
* Audio routing functionality  -out of the box - managed by the SDK
* Session management functionality
* Connecting and disconnecting from a Session
* Managing Video and Audio streams
* Enable/Disable video Enable/Disable audio
* Active speaker detection
* Quality of Service monitoring
* Resolution and bitrate control
* Screen sharing Camera preview
* Synchronization logic Reconnect functionality (will be deployed soon)
* Big screen flow support (Android and AppleTV) Audio routing functionality

### Server <a href="#watchtogetheroverview-server" id="watchtogetheroverview-server"></a>

* Authentication and client tokens
* Archiving and server-side configuration (Coming soon)

## Sessions <a href="#watchtogetheroverview-sessions" id="watchtogetheroverview-sessions"></a>

When watching together we bring together a few participants that stream Video and Audio. The participants are bound together in a Session.

The Session is the place where participants meet and interact. A Session is a unique entity, it exists once created, and when all participants leave it is erased and cannot be reused.

Once a Session is created it is possible to connect and manage local and remote streams (Audio and video).

Every Session has a unique `sessionId` to identify it which will allow various participants to join the specific Session.

## Connecting and managing streams <a href="#watchtogetheroverview-connectingandmanagingstreams" id="watchtogetheroverview-connectingandmanagingstreams"></a>

Managing streams to provide a good user experience is always a complex task.

To do that we are providing you with the tools to manage many aspects of the Session functionality and life-cycle.

The first step in creating a Watch Together experience is setting your own [Authentication Server](/watch-together-sdk/sscale-confluence-overview). The Authentication Server should be hosted in your domain (Customer enclave) for several reasons:

* To provide server-side access for configuration (The available functionality will increase with time)
* Security - you will be responsible to integrate the token distribution to Web and Mobile clients and make sure it is done in a correct and secure way

## Credentials

Setting up the Authentication server requires you to [login](https://media.sceenic.co/) to your private area and retrieve your API\_KEY and API\_SECRET

**Authentication flow:**

![](/files/-Lrnwj4D2sQuDgc3DofJ)

Once Authentication is set up you can retrieve a valid token to allow Web and Mobile clients to use the system.

**Acquiring a valid token:**

* `getToken`

![](/files/-Lrnwj4EDoiz-w-JB5KB)

When the token is acquired, you will be able to connect to a Session by either creating it or joining an existing Session.

* `connect`

**Join session" flow:**

![](/files/-Lrnwj4FuXPaPkyjvXeQ)

Managing the Watch Together experience will be done using the callbacks and methods below:

* `onError`
* `onConnected`
* `onDisconnected`
* `onParticipantJoined`
* `onParticipantLeft`
* `enableAudio`
* `enableVideo`
* `disableVideo`
* `disableAudio`

## Key terms <a href="#watchtogetheroverview-keyterms" id="watchtogetheroverview-keyterms"></a>

* Session- A one-time entity that binds together participants and allows management of participant behavior and experience
* Participant - a local or remote user that streams either Video, Audio or both
* Access token - a token that is made available for clients (Web, Mobile) so that they can authenticate against the service
* Streams - Audio or Video that every participant in the Session can share - at the moment, this object is not exposed
* Client SDKs - iOS, Android or Web SDKs which allows you to communicate with the Media Server Cluster (MSC) and create your custom experiences
* Authentication server - A server, that is hosted in your domain (Customer enclave), whose purpose is to request valid token from the MSC and distribute it to clients

## Resources <a href="#watchtogetheroverview-resources" id="watchtogetheroverview-resources"></a>

* Code samples
  * [Android Kotlin](https://bitbucket.org/svmt/samplewatchtogether-kotlin-2.0/src/master/)
  * [Android Java](https://bitbucket.org/svmt/samplewatchtogether-java-2.0/src/master/)
  * [iOS Swift](https://bitbucket.org/svmt/watchtogether-ios-swift/src/master/)
  * [iOS Objective-c-adapter](https://bitbucket.org/svmt/watchtogether-ios-objc/src/master/)
  * [Web JS sample application code](https://bitbucket.org/svmt/watchtogethersampleappjs/src/master/)
  * [React sample application code](https://bitbucket.org/svmt/watchtogethersampleappreact/src/master/)
* Tutorials
  * [Android tutorial](/watch-together-sdk/sscale-confluence-tutorials/sscale-confluence-android/android-java-kotlin-new)
  * [iOS Swift tutorial](/watch-together-sdk/sscale-confluence-tutorials/sscale-confluence-ios/ios-swift-objective-c-adapter-new)
  * [iOS Objective-c-adapter](broken://pages/-MX0xIHMVyBdarXcyh5S)
  * [Web (Re](broken://pages/-MNJHvBNmvrN0HRTYGVg)[ct/JS) tutorial](broken://pages/-MNJHvBNmvrN0HRTYGVg)

## Support

Need technical support? contact us at <Support@sceenic.co>.


# Authentication overview

## Authentication server <a href="#overview-watchtogetherauthenticationserver" id="overview-watchtogetherauthenticationserver"></a>

The Watch Together Authentication server has two parts, the first one is hosted in the Media Server Cluster (MSC), the customer enclave, and the second is hosted by you in your data center/cloud.

![](/files/-Lrnwj48IBFpe9CAnGNv)

This architecture will allow you to apply and integrate your own logic for supplying valid token to the clients and apply your own security standards.

### Authentication flow <a href="#overview-authenticationflow" id="overview-authenticationflow"></a>

In order to make requests to the MSC Authentication service, you require API \_KEY and API\_SECRET, both of which you get by logging in to your private area - [Private area](https://media.sceenic.co/).

![Authentication flow](/files/-MK9vJKmQkFOzI-d9dA_)

{% hint style="info" %}
**Please note**:&#x20;

* Tokens are only valid for a specific session and the JWT token describes the connection details
* Tokens are generated only once and therefore need to be persisted on the **CustomerAuthServer**
* For two clients to be able to connect to the same session the clients must have exactly the same JWT token
  {% endhint %}

## Customer authentication server <a href="#customer-authentication-server" id="customer-authentication-server"></a>

As we require that Clients will not directly request tokens from our Authentication service (CAS) we would like to suggest an authentication flow for your backend.

In this flow, we suggest a way to create a backend service that will be hosted on your side and will allow different clients to connect by sharing a `TokenID`.

![Customer authentication server - suggested flow](/files/-MO0wQRISKeEbRbcxJA1)

## Authentication API reference <a href="#overview-authenticationapireference" id="overview-authenticationapireference"></a>

Follow this [link](/watch-together-sdk/sscale-confluence-api-references/cluster-authentication-service-reference-cas-2) to see the Authentication API reference.

## Support <a href="#overview-support" id="overview-support"></a>

Need technical support? contact us at <Support@sceenic.co>.


# Tutorials


# Android


# Android - Java/Kotlin

## Overview <a href="#android-javaandkotlin-overview" id="android-javaandkotlin-overview"></a>

Follow this step-by-step tutorial to implement the Watch Together video chat sample application.

While the client-side application will take care of most of the functionality, in order to make this sample application work, you will need to get an access token from the [Cluster Authentication Server](/watch-together-sdk/sscale-confluence-api-references/cluster-authentication-service-reference-cas-2) (CAS).

* To better understand the Watch Together architecture have a look at this guide - [Watch Together overview](/watch-together-sdk/sscale-confluence-watch-together-overview)
* Have a look at the [Authentication overview](/watch-together-sdk/sscale-confluence-overview)
* The full code sample can be found here - Android full code sample [Java ](https://bitbucket.org/svmt/samplewatchtogether-java-2.0/src/master/)or [Kotlin](https://bitbucket.org/svmt/samplewatchtogether-kotlin-2.0/src/master/)

## Requirements <a href="#android-javaandkotlin-requirements" id="android-javaandkotlin-requirements"></a>

To complete this guide successfully the following prerequisites are required:

* [Android Studio](https://developer.android.com/studio)

## Authentication

An Access Token is needed in order to allow a client to connect to a Session.

{% hint style="danger" %}
**Note:** It is important that the client application does not request an Access Token directly from the backend. By doing that you risk exposing the API\_TOKEN and API\_SECRET.
{% endhint %}

* To learn how to acquire an Access Token please look at the [Cluster Authentication Server](/watch-together-sdk/sscale-confluence-api-references/cluster-authentication-service-reference-cas-2) (CAS) reference
* To simplify the tutorial, in the section below you can see an example of getting an Access Token

### Acquiring an Access Token

{% tabs %}
{% tab title="cUrl(Bash)" %}

```bash
curl -iL --request GET --url https://YOUR_CAS_URL/stream/token/v2/ --header 'auth-api-key: API_KEY' --header 'auth-api-secret: API_SECRET'
```

{% endtab %}
{% endtabs %}

The`Access Token`is a JWT token - more about JWT you can read - [here](https://en.wikipedia.org/wiki/JSON_Web_Token).

A successful response will look like that:

```
{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...."
}
```

{% hint style="info" %}
You can get your API\_KEY and API\_SECRET in your private area, [here](https://media.sceenic.co/).
{% endhint %}

{% hint style="warning" %}
**Note:** Every Streaming Token corresponds to one specific Session only. To allow two different clients to connect to the same Session, the clients need to use the same `Access Token`.
{% endhint %}

### Going to production <a href="#going-to-production" id="going-to-production"></a>

To go to production you will need to implement your own authentication server. Using the server the Access Token will be shared to your various clients (Web, Android, and iOS). With this valid Access Token you will be able to use the service.

For that you will need:

* API\_KEY, and API\_SECRET - can be retrieved in your private area once you [login](https://media.sceenic.co/)
* Your own working authentication server - [Authentication overview](/watch-together-sdk/sscale-confluence-overview)

## Creating a new project <a href="#android-javaandkotlin-creatinganewproject" id="android-javaandkotlin-creatinganewproject"></a>

* Open Android Studio and select **New Project** from the **File** menu
* Select **Empty Activity** and click next
* Choose Java or Kotlin as your programming language for the project
* Configure your project's location, application, and package names
* Set minimum **SDK** version of the application to **23** or higher

## Adding Watch Together Android SDK library to the project <a href="#android-javaandkotlin-addingwatchtogetherandroidsdklibrarytotheproject" id="android-javaandkotlin-addingwatchtogetherandroidsdklibrarytotheproject"></a>

* Create (if it does not exist) in the project **libs** directory under the **app** folder
* Take the downloaded file **wtsdk\_v....aar** and put it into the **libs** folder
* Edit application's **build.gradle** file (in the app folder) with SDK

{% tabs %}
{% tab title="Build.gradle" %}

```
dependencies {	
    implementation files('libs/wtsdk_v....aar')	
}
```

{% endtab %}
{% endtabs %}

If you are using `android.enableJetifier=true` to automatically convert third-party libraries to use AndroidX, please add this line `android.jetifier.blacklist=wsdk_v2.0.4.aar` to your `gradle.properties` file.

{% hint style="warning" %}
If this line is not there the WT library will not compile
{% endhint %}

{% tabs %}
{% tab title="gradle.properties file" %}

```kotlin
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true

# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
android.jetifier.blacklist=sdk-release.aar

```

{% endtab %}
{% endtabs %}

### Adding dependencies <a href="#android-javaandkotlin-addingdependencies" id="android-javaandkotlin-addingdependencies"></a>

* Modify the **build.gradle** file (in the app folder) with SDK dependencies, once modified it should look as follows:

{% tabs %}
{% tab title="Java" %}

```java
android {
...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version'
    ...

	//Watch Together SDK library
    implementation files('libs/wtsdk_v....aar')	
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
android {
...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version'
    implementation 'androidx.core:core-ktx:1.2.0' 
...
    
	//Watch Together SDK library
    implementation files('libs/wtsdk_v....aar')	
}
```

{% endtab %}
{% endtabs %}

* Sync and Rebuild the project
  * If the Sync and Rebuild was executed successfully, the Watch Together classes and functions should be available in the project

### Granting access to camera and microphone <a href="#android-javaandkotlin-grantingaccesstocameraandmicrophone" id="android-javaandkotlin-grantingaccesstocameraandmicrophone"></a>

Using the device Audio and Video requires permissions to be granted from the user and some code that will allow the application to work well under different scenarios.

* Add the following permissions to the **AndroidManifest.xml** file under the **app/src/main** folder in the project above the application tag.

{% tabs %}
{% tab title="" %}

```markup
<uses-feature android:name = "android.hardware.camera" />
<uses-feature android:name = "android.hardware.camera.autofocus" />
<uses-feature
    android:glEsVersion = "0x00020000"
    android:required = "true" />
<uses-permission android:name = "android.permission.CAMERA" />
<uses-permission android:name = "android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name = "android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name = "android.permission.RECORD_AUDIO" />
<uses-permission android:name = "android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name = "android.permission.INTERNET" />
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
The permissions should be validated before every connect of a client to a session.
{% endhint %}

## Sample application UI <a href="#android-javaandkotlin-sampleapplicationui" id="android-javaandkotlin-sampleapplicationui"></a>

The sample application has a UI element that needs to be handled to allow the proper presentation of the streams and stream properties.

* Use a view container in the application’s resource layout file to display videos. The sample application demonstrates how to manage videos with `RecyclerView` container
* For rendering videos, the SDK provides the `VideoRenderer` class for use in the application’s layout

  ```
  <?xml version="1.0" encoding="utf-8"?>
  <android.support.constraint.ConstraintLayout
  ...

  <com.wt.sdk.VideoRenderer
      android:id="@+id/video_renderer"
      android:layout_width="120dp"
      android:layout_height="160dp" />
  ```
* Initialize Session object with `SessionBuilder` class

{% tabs %}
{% tab title="Java" %}

```java
// Host class of Session object should implement or have interface SessionListener
mSession = new Session.SessionBuilder(this)           // this - SessionListener object   
                    .setReconnectListener(this)       // this - ReconnectListener object
                    .setConnectionListener(this)      // this - ConnectionListener object
                    .build(this);                     // this - Android context
                    
mSession.setDisplayName(mDisplayName);                // optional 
mSession.setEnableStats(isEnableStats);               // isEnableStats - enable stats received                           
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// Host class of Session object should implement or have interface SessionListener
mSession = Session.SessionBuilder(this)             // this - SessionListener object
                    .setReconnectListener(this)     // this - ReconnectListener object
                    .setConnectionListener(this)    // this - ConnectionListener object
                    .build(this)                    // this - Android context
                    
                    
mSession?.setDisplayName(mDisplayName)              // optional            
mSession?.isEnableStats = isEnableStats             // isEnableStats - enable stats received
```

{% endtab %}
{% endtabs %}

* Start camera preview - can be called without the Session’s url and the `Access Token`

{% tabs %}
{% tab title="Java" %}

```java
mSession.startCameraPreview();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSession?.startCameraPreview()
```

{% endtab %}
{% endtabs %}

* Connecting to the Session using the Session object requires a Session’s URL and a valid Streaming Token to be available before connecting. The `connect()` function establishes a connection to the Session with the audio and video tracks inside `MediaStream` object

{% tabs %}
{% tab title="Java" %}

```java
// String parameter sessionId should be passed in the function connect.
mSession.connect(mToken);               // mToken - authorization token
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// String parameter sessionId should be passed in the function connect.
mSession?.connect(mToken)               // mToken - authorization token
```

{% endtab %}
{% endtabs %}

* It is possible connect as different types of participants:
  * FULL\_PARTICIPANT - publishes Audio and Video to the session and Subscribes to all other participants Audio and Video in the Session
  * VIEWER - Subscribes to all other participants Audio and Video in the Session
  * AV\_BROADCASTER - Publishes Audio and Video to the Session
  * A\_BROADCASTER - Publishes only Audio to the Session
* In the case you would like to connect as a Viewer do the following:

{% tabs %}
{% tab title="Java" %}

```java
// String parameter sessionId should be passed in the function connectAsViewer.
mSession.connect(mToken, mParticipantType);  // mParticipantType - participant's connection type
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// String parameter sessionId should be passed in the function connectAsViewer.
mSession?.connect(mToken, mParticipantType)   // mParticipantType - participant's connection type
```

{% endtab %}
{% endtabs %}

## Managing Session logic <a href="#android-javaandkotlin-managingsessionlogic" id="android-javaandkotlin-managingsessionlogic"></a>

To manage the session's logic we provided several callbacks that will allow you to customize the interactions as you need.

* The **SessionListener** interface, which the **MainActivity** implements, will allow you to control the flow of logic of the **Session** you are managing

{% tabs %}
{% tab title="Java" %}

```java
public class MainActivity extends AppCompatActivity implements SessionListener {
   ...
   
    @Override
    public void onConnected(@NonNull List<? extends Participant> list) {
        // client has been connected to the session with the unique sessionId and participants already in the Session
    }

    @Override
    public void onDisconnected() { 
        // client has been disconnected from the session. All connections will be closed.
        // clear ui and data  
        finish() // Close MainActivity page      
    }
    
    @Override
    public void onError(SessionError error) {
        // You can implement error handling here. Check API rferences for possible errors.
    }
    
    @Override
    public void onLocalParticipantJoined(@Nullable Participant participant) {
        // Camera preview started, local stream created, update UI  
        if (mParticipantAdapter != null) {
            mParticipantAdapter.addLocalParticipant(participant);
        }
    }

    @Override
    public void onRemoteParticipantJoined(@Nullable Participant participant) {
        // remote participant joined to the session, update ui.
        // create addParticipant method in the ParticipantsAdapter as in the sample code
        if (mParticipantAdapter != null) {
            mParticipantAdapter.addRemoteParticipant(participant);
        }
    }

    @Override
    public void onUpdateParticipant(@Nullable String participantId, @Nullable Participant participant) {
        // participant update the session, update ui
        if (mParticipantAdapter != null) {
            mParticipantAdapter.updateParticipant(participantId, participant);
        }
    }

    @Override
    public void onRemoteParticipantLeft(@Nullable String participantId) {
        // remote participant left the session, update ui
        if (mParticipantAdapter != null) {
            mParticipantAdapter.removeParticipant(participantId);
        }
    }

    @Override
    public void onParticipantMediaStateChanged(@Nullable String participantId, MediaType mediaType,
        MediaState mediaState) {
        // Method is triggered when remote participant disabled/enabled (mediastate) it's audio/video (mediaType).
        // On UI it can be reflected with this callback
        if (mParticipantAdapter != null) {
            mParticipantAdapter.updateParticipantMedia(participantId, mediaType, mediaState);
        }
    }

    @Override
    public void onMessageReceived(@Nullable String participantId, @Nullable String message) {
        // The method is receiving messages from remote participants into the current room
        Toast.makeText(this, "participant=$participant, message=$message", Toast.LENGTH_LONG).show()
    }
   
    @Override
    protected void onPause() {
        // Disconnect from the session
        mSession.disconnect();
    }

    @Override
    public void onBackPressed() {
        // Disconnect from the session
        mSession.disconnect();
    }
    
    @Override
    protected void onDestroy() {
        // Clear participants and publishers
        mParticipantAdapter.clearParticipants();
        super.onDestroy();
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
class MainActivity : AppCompatActivity(), SessionListener {
    ...
    
    override fun onConnected(participants: List<Participant>) {
        // client has been connected to the session with the unique sessionId and publishers already in the Session
    }
        
    override fun onDisconnected() {
        // client has been disconnected from the session. All connections will be closed.
        // clear ui and data  
        finish() // Close MainActivity page
    }
    
    override fun onError(error: SessionError) {
        // You can implement error handling here. Check API rferences for possible errors.
    }
    
    override fun onLocalParticipantJoined(participant: Participant) {
        // Camera preview started, local stream created, update UI  
        mParticipantAdapter?.addLocalParticipant(participant)
    }

    override fun onRemoteParticipantJoined(participant: Participant) {
        // remote participant joined to the session, update ui.
        // create addParticipant method in the ParticipantsAdapter as in the sample code
        mParticipantAdapter?.addRemoteParticipant(participant)
    }

    override fun onUpdateParticipant(participantId: String, participant: Participant) {
        // participant update the session, update ui
        mParticipantAdapter?.updateParticipant(participantId, participant)
    }

    override fun onRemoteParticipantLeft(participantId: String) {
        // remote participant left the session, update ui
        mParticipantAdapter?.removeParticipant(participantId)
    }
    
    override fun onParticipantMediaStateChanged(
        participantId: String,
        mediaType: MediaConfiguration.MediaType?,
        mediaState: MediaConfiguration.MediaState?
    ) {
        // Method is triggered when remote participant disabled/enabled (mediastate) it's audio/video (mediaType).
        // On UI it can be reflected with this callback
        mParticipantAdapter?.updateParticipantMedia(participantId, mediaType, mediaState)
    }
    
    override fun onMessageReceived(participantId: String, message: String) {
        // The method is receiving messages from remote participants into the current room
        Toast.makeText(applicationContext, "participant=$participant, message=$message", Toast.LENGTH_LONG).show()
    }

    override fun onPause() {
        // Disconnect from the session
        mSession?.disconnect()
    }
    
    override fun onBackPressed() {
        // Disconnect from the session
        mSession?.disconnect()
    }

    override fun onDestroy() {
        // Clear participants and publishers
        mParticipantAdapter?.clearParticipants()
        super.onDestroy()
    }

}
```

{% endtab %}
{% endtabs %}

## Managing Reconnection logic

To manage the reconnect session's logic we provide several callbacks and will allow you to customize the interactions you need.

* The SessionReconnectListener interface, which the MainActivity implements in the sample application, will allow you to control the flow of logic of the **Reconnect** you are managing

{% tabs %}
{% tab title="Java" %}

```java
public class MainActivity extends AppCompatActivity implements SessionReconnectListener {
   ...
    
    @Override
    public void onParticipantReconnecting(final String participantId) {
        // callback which triggers when remote participant loses network 
        // participantId - participant's identificator for updating
        mParticipantAdapter.progressConnection(participantId, true);
    }

    @Override
    public void onParticipantReconnected(String participantId, String oldParticipantId) { 
        // callback with triggers when remote participant network resumes
        // oldParticipantId -  identificator old participant for updating
        // participantId - participant's identificator for updating
        mParticipantAdapter.progressConnection(oldParticipantId, false);     
    }
}

```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
class MainActivity : AppCompatActivity(), SessionReconnectListener {
    ...
    
    override fun onParticipantReconnecting(
        participantId: String
    ) {
        // callback which triggers when remote participant loses network 
        // participantId - participant's identificator for updating
        mParticipantAdapter?.progressConnection(participantId, true)
    }
        
    override fun onParticipantReconnected(
        participantId: String,
        oldParticipantId: String
    ) {
        // callback with triggers when remote participant network resumes
        // oldPublisherId -  identificator old participant for updating
        // participantId - participant's identificator for updating
        mParticipantAdapter?.progressConnection(oldParticipantId, false)
    }
}
```

{% endtab %}
{% endtabs %}

## Managing Connection states logic

To manage the connection states session's logic we provided several callbacks and will allow you to customize the interactions you need.

* The SessionConnectionListener interface, which the MainActivity implements in the sample application, will allow you to control the flow of logic of the **Connection states** you are managing

{% tabs %}
{% tab title="Java" %}

```java
public class MainActivity extends AppCompatActivity implements SessionConnectionListener {
    ...
    
    @Override
    public void onLocalConnectionLost() {
        // callback with triggers when local participant connection lost
    }
    
    @Override
    public void onLocalConnectionResumed() {
        // callback with triggers when local participant connection resumed
    }
    
    @Override
    public void onRemoteConnectionLost(String participantId) {
        // callback with triggers when remote participant connection lost
        // participantId - identificator that participant by id losing connection
        if (mParticipantAdapter != null) {
            mParticipantAdapter.remoteParticipantConnectionLost(participantId);
        }
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
class MainActivity : AppCompatActivity(), SessionConnectionListener {
    ...
    
    override fun onLocalConnectionLost() {
        // callback with triggers when local participant connection lost
    }
    
    override fun onLocalConnectionResumed() {
        // callback with triggers when local participant connection resumed
    }
    
    override fun onRemoteConnectionLost(participantId: String?) {
        // callback with triggers when remote participant connection lost
        // participantId - identificator that participant by id losing connection
        mParticipantAdapter?.remoteParticipantConnectionLost(participantId)
    }
}
```

{% endtab %}
{% endtabs %}

## Enable/Disable Audio or Video

* Bind the Participant view to the Participant data in the **ParticipantsAdapter** class

{% tabs %}
{% tab title="Java" %}

```java
    @Override
    public void onBindViewHolder(@NonNull final ViewHolder viewHolder, final int position) {
        final Participant participant = mParticipants.get(viewHolder.getAdapterPosition());
        if (participant != null) {
            participant.setRenderer(viewHolder.videoRenderer);
            participant.enableStats();
            participant.setParticipantStatsListener(new OnParticipantStatsListener() {
                @Override
                public void onStats(@Nullable JSONObject jsonStats,
                    @NotNull Participant participant) {
                    // The callback with json data about connection quality for audio and video
                    try {
                        if (jsonStats != null) {
                            jsonStats.get("audioAvg")          // 0 - 4.5 value
                            jsonStats.get("audioQosMos")       // Value of quality {BAD, GOOD, EXCELLENT}
                            jsonStats.get("videoAvg")          // 0 - 4.5 value
                            jsonStats.get("videoQosMos")       // Value of quality {BAD, GOOD, EXCELLENT}
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            });
            viewHolder.mic.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(final View v) {
                    if (participant.isAudioEnabled()) {
                        participant.disableAudio();
                    } else {
                        participant.enableAudio();
                    }
                    notifyItemChanged(position, participant);
                }
            });
            viewHolder.cam.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(final View v) {
                    if (participant.isVideoEnabled()) {
                        participant.disableVideo();
                    } else {
                        participant.enableVideo();
                    }
                    notifyItemChanged(position, participant);
                }
            });
            // Update microphone and camera UI icons according to Participant's Audio and Video track state
            viewHolder.connectionState.setVisibility(participant.isConnectionLost() ? View.VISIBLE : View.GONE);
            viewHolder.layoutProgress.setVisibility(participant.isProgressReconnection() ? View.VISIBLE : View.GONE);
            viewHolder.mic.setBackgroundResource(participant.isAudioEnabled() ? R.drawable.ic_mic_on : R.drawable.ic_mic_off);
            viewHolder.cam.setBackgroundResource(participant.isVideoEnabled() ? R.drawable.ic_video_on : R.drawable.ic_video_off);

            viewHolder.name.setText(participant.getName());
      }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
        val participant = mParticipants[viewHolder.adapterPosition]

        participant.setRenderer(viewHolder.videoRenderer)
        participant.enableStats()
        participant.setParticipantStatsListener(object : OnParticipantStatsListener {
            override fun onStats(jsonStats: JSONObject?, participant: Participant) {
                // The callback with json data about connection quality for audio and video
                jsonStats?.get("audioAvg")          // 0 - 4.5 value
                jsonStats?.get("audioQosMos")       // Value of quality {BAD, GOOD, EXCELLENT}
                jsonStats?.get("videoAvg")          // 0 - 4.5 value
                jsonStats?.get("videoQosMos")       // Value of quality {BAD, GOOD, EXCELLENT}
            }
        })
        
        viewHolder.mic.setOnClickListener {
            if (participant.isAudioEnabled) {
                participant.disableAudio()
            } else {
                participant.enableAudio()
            }
            notifyItemChanged(position, participant)
        }
        viewHolder.cam.setOnClickListener {
            if (participant.isVideoEnabled) {
                participant.disableVideo()
            } else {
                participant.enableVideo()
            }
            notifyItemChanged(position, participant)
        }
        // Update microphone and camera UI icons according to Participant's Audio and Video track state
        viewHolder.connectionState.visibility = if (participant.isConnectionLost) View.VISIBLE else View.GONE
        viewHolder.layoutProgress.visibility = if (participant.isProgressReconnection) View.VISIBLE else View.GONE
        viewHolder.mic.setBackgroundResource(if (participant.isAudioEnabled) R.drawable.ic_mic_on else R.drawable.ic_mic_off)
        viewHolder.cam.setBackgroundResource(if (participant.isVideoEnabled) R.drawable.ic_video_on else R.drawable.ic_video_off)

        viewHolder.name.text = participant.name
}
```

{% endtab %}
{% endtabs %}

## Switching camera <a href="#android-javaandkotlin-switchingcamera" id="android-javaandkotlin-switchingcamera"></a>

During streaming, you can switch from front to back camera by a simple call to the Session’s function `switchCamera`

## Running the application <a href="#android-javaandkotlin-runningtheapplication" id="android-javaandkotlin-runningtheapplication"></a>

Once coding is finished, you should be able to run the application in the Android Studio emulator.

You can view the complete Watch Together sample application here

* [Java sample application](https://bitbucket.org/svmt/samplewatchtogether-java-2.0/src/master/)
* [Kotlin sample application](https://bitbucket.org/svmt/samplewatchtogether-kotlin-2.0/src/master/)

## Release notes <a href="#android-javaandkotlin-releasenotes" id="android-javaandkotlin-releasenotes"></a>

* The Android SDK’s support Android 6.0 version and higher

## Next steps <a href="#android-javaandkotlin-nextsteps" id="android-javaandkotlin-nextsteps"></a>

* Learn more about Watch Together architecture - [Watch Together overview](http://svmtse.atlassian.net/wiki/spaces/SC/pages/80052361/Watch+Together+overview)
* To understand better how to set up the Authentication please have a look at the [Authentication overview](/watch-together-sdk/sscale-confluence-overview).

## Support <a href="#android-javaandkotlin-support" id="android-javaandkotlin-support"></a>

Need technical support? contact us at <Support@sceenic.co>.


# iOS


# iOS Swift/Objective-c adapter

## Overview <a href="#ios-swiftandobjectivec-overview" id="ios-swiftandobjectivec-overview"></a>

Follow this step-by-step tutorial to implement the Watch Together video chat sample application.

While the client-side application will take care of most of the functionality, in order to make this sample application work, you will need to get an access token from the [Cluster Authentication Server](/watch-together-sdk/sscale-confluence-api-references/cluster-authentication-service-reference-cas-2) (CAS).

* To better understand the Watch Together architecture have a look at this guide - [Watch Together overview](/watch-together-sdk/sscale-confluence-watch-together-overview)
* Have a look at the [Authentication overview](/watch-together-sdk/sscale-confluence-overview)
* The full code samples can be found here
  * &#x20;[Swift](https://bitbucket.org/svmt/watchtogether-ios-swift/src/master/)
  * [Obj-c adapter](https://bitbucket.org/svmt/watchtogether-ios-objc/src/master/)

## Requirements <a href="#ios-swiftandobjectivec-requirements" id="ios-swiftandobjectivec-requirements"></a>

To complete this guide successfully the following prerequisites are required:

* A Sceenic account
* [Xcode](https://itunes.apple.com/ua/app/xcode/id497799835?mt=12)

## Authentication

An Access Token is needed in order to allow a client to connect to a Session.

{% hint style="danger" %}
**Note:** It is important that the client application does not request an Access Token directly from the backend. By doing that you risk exposing the API\_TOKEN and API\_SECRET.
{% endhint %}

* To learn how to acquire an Access Token please look at the [Cluster Authentication Server](/watch-together-sdk/sscale-confluence-api-references/cluster-authentication-service-reference-cas-2) (CAS) reference
* To simplify the tutorial, in the section below you can see an example of getting an Access Token

### Acquiring an Access Token

{% tabs %}
{% tab title="cUrl(Bash)" %}

```bash
curl -iL --request GET --url https://YOUR_CAS_URL/stream/token/v2/ --header 'auth-api-key: API_KEY' --header 'auth-api-secret: API_SECRET'
```

{% endtab %}
{% endtabs %}

The`Access Token`is a JWT token - more about JWT you can read - [here](https://en.wikipedia.org/wiki/JSON_Web_Token).

A successful response will look like that:

```
{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...."
}
```

{% hint style="info" %}
You can get your API\_KEY and API\_SECRET in your private area, [here](https://media.sceenic.co/).
{% endhint %}

{% hint style="warning" %}
**Note:** Every Streaming Token corresponds to one specific Session only. To allow two different clients to connect to the same Session, the clients need to use the same `Access Token`.
{% endhint %}

### Going to production <a href="#going-to-production" id="going-to-production"></a>

‌To go to production you will need to implement your own authentication server. Using the server the Access Token will be shared to your various clients (Web, Android, and iOS). With this valid Access Token you will be able to use the service.

For that you will need:

* API\_KEY, and API\_SECRET - can be retrieved in your private area once you [login](https://media.sceenic.co/)
* Your own working authentication server - [Authentication overview](/watch-together-sdk/sscale-confluence-overview)

## Creating a new project <a href="#ios-swiftandobjectivec-creatinganewproject" id="ios-swiftandobjectivec-creatinganewproject"></a>

* Open Xcode and create a new project
* Choose a Single View application
* Configure your product organization, bundle, and team names
* Set the application location on your computer and press "create"

## Adding Watch Together iOS SDK library to the project <a href="#ios-swiftandobjectivec-addingwatchtogetheriossdklibrarytotheproject" id="ios-swiftandobjectivec-addingwatchtogetheriossdklibrarytotheproject"></a>

{% hint style="info" %}
In case you require to use an Objective-c project, please add the  WatchTogetherAdapter to your project
{% endhint %}

* Download the library ([Private area](https://media.sceenic.co/)) package and unpack it
* Create a folder with the name "WatchTogether" in the root of the project
* Copy all files ("WatchTogether.framework","WatchTogether.podspec") to created folder
* \[When using the Objective-c adapater] - do the following extra steps
  * Create a folder named "WatchTogetherAdapter" in the root of the project
  * Copy all the files unpacked from the SDK ("WatchTogetherAdapter.framework","WatchTogetherAdapter.podspec") to "WatchTogetherAdapter" folder
* Open folder with your project in Terminal
  * Print the command "pod init"
  * Print the command "open Podfile"
  * In the opened file paste this string `pod 'WatchTogether', :path => './WatchTogether'` \
    after "target 'Your project name' **do**" and save file
  * \[When using the Objective-c adapater] - do the following extra steps
    * In the opened file paste the following strings\
      `pod 'WatchTogetherAdapter', :path => './WatchTogetherAdapter'`\
      `pod 'WatchTogether', :path => './WatchTogether'`
  * In the terminal write the command "pod install"
  * To get more information about Cocoapod go to this site <https://cocoapods.org>
* Disable application Bitcode.
  * Build Settings → Build Options → Enable Bitcodes → Set to "NO"
* To mock a video for the simulator you will need to add an mp4 video file to your main bundle and rename it to simulator\_mock.mp4

![](/files/-MXY8bneQC1MbtaFbPTR)

### Granting access to camera and microphone <a href="#ios-swiftandobjectivec-grantingaccesstocameraandmicrophone" id="ios-swiftandobjectivec-grantingaccesstocameraandmicrophone"></a>

* Go to the **info.plist** file, add the following properties
  * Add the property "`Privacy - Camera Usage Description`" and the value "$(PRODUCT\_NAME) uses the camera"
  * Add the property "`Privacy - Microphone Usage Description`" and the value "$(PRODUCT\_NAME) uses the microphone"

## **Initialize** a Session

* Import the library to the view controller

{% tabs %}
{% tab title="Swift" %}

```swift
import WatchTogether
```

{% endtab %}

{% tab title="Objective-c adapter" %}

```objectivec
@import WatchTogetherAdapter;
```

{% endtab %}
{% endtabs %}

* Set the minimal log level

{% tabs %}
{% tab title="Swift" %}

```swift
Session.setMinLogLevel(.debug)
```

{% endtab %}

{% tab title="Objective-c adapter" %}

```objectivec
[SessionAdapter setMinLogLevel:WTALogLevelDebug];
```

{% endtab %}
{% endtabs %}

* Create a session instance
  * `username` - a display name
  * `delegate` - an object conforming to SessionDelegate protocol

{% tabs %}
{% tab title="Swift" %}

```swift
let session = SessionBuilder()
            .withUsername(username)
            .withVideoCodec(.H264)
            .withVideoRenderer(.Metal)
            .withDelegate(self)
            .build()
```

{% endtab %}

{% tab title="Objective-c adapter" %}

```objectivec
SessionAdapter* session =  [[SessionAdapter alloc] initWith:username];
[session buildSession];
session.delegate = self;
```

{% endtab %}
{% endtabs %}

* Get the local participant instance

{% tabs %}
{% tab title="Swift" %}

```swift
let localParticipant = session.localParticipant
```

{% endtab %}

{% tab title="Objective-c adapter" %}

```objectivec
ParticipantAdapter* localParticipant = session.localParticipant;
```

{% endtab %}
{% endtabs %}

* Connect to the session
  * `Access token` - an authentication token received from the Authentication Server

{% tabs %}
{% tab title="Swift" %}

```swift
do {
    try session.connect(with: token)// method can throw
} catch {
    print(error)//error is WTError
    //fallback logic
}    
```

{% endtab %}

{% tab title="Objective-c adapter" %}

```
NSError* err = nil;
[_session connectWith:_token error:&err];
if (err != nil) {
        //fallback logic
}
```

{% endtab %}
{% endtabs %}

* Disconnect from the session

{% tabs %}
{% tab title="Swift" %}

```swift
session.disconnect()
```

{% endtab %}

{% tab title="Objective-c adapter" %}

```
[session disconnect];
```

{% endtab %}
{% endtabs %}

## Using the SessionDelegate

{% tabs %}
{% tab title="Swift" %}

```swift
extension YourClass: SessionDelegate {
    func onSessionConnected(sessionId: String, participants: [Participant]) {
        print("Connected to \(sessionId)")
    }
    
    func onSessionDisconnect() {
        print("Disconnect from session")
    }
    
    func onRemoteParticipantJoined(participant: Participant) {
        print("New participant with id: \(participant.getId())")
    }
     
    func onRemoteParticipantStartMedia(participant: Participant) {
        print("New media from id: \(participant.getId())")
    }
    
    func onRemoteParticipantStopMedia(participant: Participant) {
        print("Stoped media from id: \(participant.getId())")
    }
    
    func onRemoteParticipantLeft(participant: Participant) {
        print("Participant left with id: \(participant.getId())")
    }
    
    func onSessionError(error: Error) {
        print(error.localizedDescription)
    } 
    
    func onRemoteParticipantNotification(message: String, participantId: String) {
        print("message: \(message) from participant \(participantId)"
    }
}
```

{% endtab %}

{% tab title="Objective-c adapter" %}

```objectivec
- (void)onSessionConnectedWithSessionId:(NSString *)sessionId participants:(NSArray<ParticipantAdapter *> *)participants{
    NSLog(@"Connected to session");
}

- (void)onSessionDisconnect{
    NSLog(@"Disconnect from session");
}

- (void)onRemoteParticipantJoinedWithParticipant:(ParticipantAdapter *)participant{
    NSLog(@"New participant");
}

- (void)onRemoteParticipantStartMediaWithParticipant:(ParticipantAdapter *)participant{
    NSLog(@"New media");
}

- (void)onRemoteParticipantStopMediaWithParticipant:(ParticipantAdapter *)participant{
    NSLog(@"Stoped media");
}

- (void)onRemoteParticipantLeftWithParticipant:(ParticipantAdapter *)participant{
    NSLog(@"Participant left");
}

- (void)onSessionErrorWithError:(NSError *)error{
    NSLog(@"Session failed with error");
}

-(void)onRemoteParticipantNotificationWithMessage:(NSString *)message participantId:(NSString *)participantId { 
    NSLog(@"message: %@ from participant %@", message, participantId);
}

```

{% endtab %}
{% endtabs %}

## Using the Participant object

* To get Participant video use the method `getVideo()`
* To get Participant display name use the method `getDisplayName()`
* To get Participant id use the method `getId()`

{% tabs %}
{% tab title="Swift" %}

```swift
class ParticipantView: UIView {
    var participant: Participant?
    
     func setParticipant(_ participant: Participant) {
        self.participant = participant
        let participantVideo = participant.getVideo() // View with video from participant
        let displayName = participant.getDisplayName() // Participant  display name
        participant?.delegate = self
        let participantId:String = participant.getId()// Participant id

        self.addSubview(participantVideo)
     }
     //MARK: Actions to change participant properties
     @IBAction func onVolumeChanged(_ sender: UISlider) {
        participant.volume = sender.value// values from 0.0 to 1.0
     }
     @IBAction func muteAudioAction(_ sender: UIButton) {
        participant.isAudioEnabled = !participant.isAudioEnabled //true/false enable disable audio
     }
    
     @IBAction func muteVideoAction(_ sender: UIButton) {
        participant.isVideoEnabled = !participant.isVideoEnabled //true/false
     }
}
```

{% endtab %}

{% tab title="Objective-c adapter" %}

```objectivec
@property (weak, nonatomic) ParticipantAdapter* participant;

- (void) setParticipant: (ParticipantAdapter *)participant {
    UIView* view = [participant getVideo];
    NSString *participantId = [_participant getId]
    _participantNameLabel.text = [_participant getDisplayName];
    _participant.delegate = self
    [self addSubview: view];
 } 
 //MARK: Actions in participant view
 - (IBAction)changeVolume:(UISlider *)sender {
    _participant.volume = sender.value; //from 0.0 to 1.10
}
 - (IBAction)enableAudio:(UIButton *)sender {
    _participant.isAudioEnabled = !_participant.isAudioEnabled//YES/NO turn on/off
 }
 - (IBAction)enableAudio:(UIButton *)sender {
    !_participant.isVideoEnabled = !_participant.isVideoEnabled
 }
```

{% endtab %}
{% endtabs %}

## Using the ParticipantDelegate object

{% tabs %}
{% tab title="Swift" %}

```swift
extension ParticipantView: ParticipantDelegate{
    func onReconnecting() {
        print("Participant reconnecting")
    }
    
    func onReconnected() {
        print("Participant reconnected")
    }
    
    func onChangeMediaSettings() {
        print("Participant has changed media settings")
    }
    
    func onStreamUpdate(qualityInfo: StreamQualityInfo) {
        print("Participant has new quality info \(qualityInfo.quality)")
    }
}
```

{% endtab %}

{% tab title="Objective-c adapter" %}

```objectivec
- (void)onChangeMediaSettings{
    NSLog(@"On participant cahnge media settings");
}

- (void)onStreamUpdateWithQualityInfo:(StreamQualityInfoAdapter *)qualityInfo{
    NSLog(@"On participant update quality");
}

- (void)onReconnecting{
    NSLog(@"On participant reconnecting");
}

- (void)onReconnected{
    NSLog(@"On participant reconnected");
}
```

{% endtab %}
{% endtabs %}

## Working with the LocalParticipant object <a href="#how-to-work-with-the-localparticipant-object" id="how-to-work-with-the-localparticipant-object"></a>

{% hint style="info" %}
LocalParticipant class implements the Participant protocol
{% endhint %}

* Camera preview

{% tabs %}
{% tab title="Swift" %}

```swift
localParticipant.startCameraPreview()

//MARK: switch camera action
@IBAction func onSwitchCamera(_ sender: UIButton) {
    localParticipant.cameraPosition == .front ? AVCaptureDevice.Position.back
            : AVCaptureDevice.Position.front
}
func onResolutionChanged(to: VideoQuality, fps: Int32) {
    localParticipant.videoQuality = VideoQuality.Low// VideoQuality.Hight, VideoQuality.Default
    localParticipant.fps = fps // normal values in range from 15 to 45
}
```

{% endtab %}

{% tab title="Objective-c adapter" %}

```objectivec
[_localParticipant startCameraPreview];

//MARK: Actions
//@property (nonatomic) BOOL isFront;
- (IBAction)switchCamera:(id)sender {
    _isFront = !_isFront;
    if (_isFront){
        [_localParticipant setCameraPosition: AVCaptureDevicePositionFront];
    } else {
        [_localParticipant setCameraPosition: AVCaptureDevicePositionBack];
    }
}
- (IBAction)changeVideoQuality:(UISegmentedControl *)sender {
        if(sender.selectedSegmentIndex == 0){
            _localParticipant.videoQuality = VideoQualityAdapterLow;
        } else if (sender.selectedSegmentIndex == 1){
            _localParticipant.videoQuality = VideoQualityAdapterDefault;
        } else {
            _localParticipant.videoQuality = VideoQualityAdapterHigh;
        }
    }
}

- (IBAction)changeFrameRate:(UISlider *)sender {
        _localParticipant.frameRate = sender.value; //normal values in range 15 to 45
    }
}

```

{% endtab %}
{% endtabs %}

* Video orientation set method using the [AVCaptureVideoOrientation](https://developer.apple.com/documentation/avfoundation/avcapturevideoorientation?language=objc) parameter&#x20;

{% tabs %}
{% tab title="Swift" %}

```swift
localParticipant.set(orientation: .landscapeRight) 
// localParticipant.set(orientation: nil) //use to disable orientation lock
```

{% endtab %}

{% tab title="Objective-c adapter" %}

```objectivec
[_localParticipant setWithOrientation: AVCaptureVideoOrientationLandscapeLeft];
// [_localParticipant setWithOrientation: 0]; //use to disable orientation lock
```

{% endtab %}
{% endtabs %}

## Running the application <a href="#ios-swiftandobjectivec-runningtheapplication" id="ios-swiftandobjectivec-runningtheapplication"></a>

Once coding is finished, you should be able to run the application.

You can view the complete Watch Together sample application here - [Swift](https://bitbucket.org/svmt/watchtogether-ios-swift/src/master/).

## Next steps <a href="#ios-swiftandobjectivec-nextsteps" id="ios-swiftandobjectivec-nextsteps"></a>

* Learn more about Watch Together architecture - [Watch Together overview](http://svmtse.atlassian.net/wiki/spaces/SC/pages/80052361/Watch+Together+overview)
* To understand better how to set up the Authentication please have a look at the [Authentication overview](/watch-together-sdk/sscale-confluence-overview).

## Support <a href="#ios-swiftandobjectivec-support" id="ios-swiftandobjectivec-support"></a>

Need technical support? contact us at <Support@sceenic.co>.


# Web

## Overview <a href="#web-javascriptandreact-overview" id="web-javascriptandreact-overview"></a>

Follow this step by step tutorial to implement the Watch Together video chat sample application.

While the client-side application will take care of most of the functionality, in order to make this sample application work, you will need to get an access token from the [Cluster Authentication Server](/watch-together-sdk/sscale-confluence-api-references/cluster-authentication-service-reference-cas-2) (CAS).

* To better understand the Watch Together architecture have a look at this guide - [Watch Together overview](/watch-together-sdk/sscale-confluence-watch-together-overview)
* Have a look at the [Authentication overview](/watch-together-sdk/sscale-confluence-overview)
* &#x20;The full code sample can be found here - [JS](https://bitbucket.org/svmt/watchtogethersampleappjs/src/master/) and [React](https://bitbucket.org/svmt/watchtogethersampleappreact/src/master/)

## Requirements <a href="#web-javascriptandreact-requirements" id="web-javascriptandreact-requirements"></a>

To complete this guide successfully the following prerequisites are required:

* A Sceenic account
* Code Editor( [WebStorm](https://www.jetbrains.com/webstorm/download/), [VS Code](https://code.visualstudio.com/download))
* Additional step needed only for React Project
  * [Node >= 8.10 and npm >= 5.6](https://nodejs.org/en/)


# Authentication

Have a look at the [Authentication overview](/watch-together-sdk/sscale-confluence-overview) and [Watch Together overview](/watch-together-sdk/sscale-confluence-watch-together-overview).

An `Access Token` is needed in order to allow a client to connect to a Session.

{% hint style="danger" %}
**Note:** It is important that the client application does not request an Access Token directly from the frontend. By doing that you risk exposing the API\_TOKEN and API\_SECRET.
{% endhint %}

* To learn how to acquire an Access Token please look at the [Cluster Authentication Server](/watch-together-sdk/sscale-confluence-api-references/cluster-authentication-service-reference-cas-2) (CAS) reference
* To simplify the tutorial, in the section below you can see an example of getting an Access Token.

### Acquiring an Access Token <a href="#acquiring-an-access-token" id="acquiring-an-access-token"></a>

{% tabs %}
{% tab title="cUrl(Bash)" %}

```bash
curl -iL --request GET --url https://YOUR_CAS_URL/stream/token/v2/ --header 'auth-api-key: API_KEY'   --header 'auth-api-secret: API_SECRET'
```

{% endtab %}
{% endtabs %}

The `Access Token` is a JWT token - more about jwt you can read - [here](https://en.wikipedia.org/wiki/JSON_Web_Token).

A successful response will look like that:

```
{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...."
}
```

{% hint style="info" %}
You can get your API\_KEY and API\_SECRET in your private area, [here](https://media.sceenic.co/).
{% endhint %}

{% hint style="warning" %}
**Note:** Every Streaming Token corresponds to one specific Session only. To allow two different clients to connect to the same Session, the clients need to use the same `Access Token`.
{% endhint %}

### Going to production

When moving from the Sandbox environment to production you will need to implement your own authentication server. \
This server will supply the various clients (Web, Android, and iOS) with a valid Access Token so that they can use the service.

For that you will need:

* API\_KEY, and API\_SECRET - can be retrieved in your private area once you [login ](https://media.sceenic.co/)
* Your own working authentication server - [Authentication overview](/watch-together-sdk/sscale-confluence-overview)


# Create a New Project

Our SDK can be used either by creating a new project or by integrating it into an existing one.

## Create a New Project <a href="#web-javascriptandreact-createnewproject" id="web-javascriptandreact-createnewproject"></a>

You can use any suitable method for you to create the project. Here is a few examples:

**JavaScript**

* Open WebStorm and create a new *Empty Project*
* Create two files *index.js* and *index.html* in your project folder.

**React**

{% tabs %}
{% tab title="React JSX" %}

```jsx
npx create-react-app websdksample
cd websdksample
npm start
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
If you've previously installed `create-react-app` globally via `npm install -g create-react-app`, we recommend you uninstall the package using `npm uninstall -g create-react-app` to ensure that `npx` always uses the latest version.
{% endhint %}

{% hint style="warning" %}
**Note**: [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) comes with npm 5.2+ and higher
{% endhint %}

* Then open your project through code editor.


# Adding WT SDK library to the project

Follow the instructions to add the WebSDK into your project.

* In the project’s root folder create *.npmrc* file
* Put your `Access Token` inside `//registry.npmjs.org/:_authToken=YOUR_ACCESS_TOKEN`
* Install WebSDK via`npm i @sscale/wtsdk`

<figure><img src="/files/l0S9hMI5HZMUToQnsVBI" alt=""><figcaption></figcaption></figure>

For more information on NPM installation look [here](/watch-together-sdk/sscale-confluence-tutorials/web/installing-the-npm-package).


# Installing the NPM package

## Requirements

To use the WT SDK npm package, an Access Token will be needed. If you dont have a token, please contact us at <Support@sceenic.co>.

## What is the Access Token? Why do you need it?

WT SDK is a private package that is hosted on the npm registry. Only authorized users can access it. In order to access the SDK we will generate an authentication token for you. The authentication token is a hexadecimal string that gives you access to SDK.

## Before installation

In your project root folder create *.npmrc* file. This file will set the specific npm config values for this project. Insert the code snippet bellow to the .npmrc file. Replace YOUR\_ACCESS\_TOKEN with the token that was provided.`1` `//registry.npmjs.org/:_authToken=YOUR_ACCESS_TOKEN`

## Installation

`1` `npm i @sscale/wtsdk`

## Documentation

Code samples and full API description can be found [here](https://documentation.sceenic.co/sscale-confluence-watch-together-overview).

## Using the SDK

In your *.js* module add the following line of code`1` `import WT from '@sscale/wtsdk'`

WT - contains all the methods described in the documentation. For example, to create a new session, you should do the following:`1` `WT.Session.connect(settings, user)`

## Support

Need technical support? contact us at <Support@sceenic.co>


# Sample application

As a reference, you can use the [JS](https://bitbucket.org/svmt/watchtogethersampleappjs/src/master/) and [React](https://bitbucket.org/svmt/watchtogethersampleappreact/src/master/) sample projects.

They are designed as interactive tutorials and you can switch to this mode at any time by clicking the tips button in the upper right corner.

<figure><img src="/files/jpn96jSC0T2ydIHI24uo" alt=""><figcaption></figcaption></figure>

## Sample application UI <a href="#web-javascriptandreact-sampleapplicationui" id="web-javascriptandreact-sampleapplicationui"></a>

To connect two or more users to the same Session follow the steps below:

1. Make sure you have acquired a valid `Access Token` (as explained in the [Authentication ](/watch-together-sdk/sscale-confluence-overview)section)
2. Share Streaming token between clients that need to join the same Session
3. Each client should invoke Session.connect(streamingToken, participantName)

In next steps as an example, we will be creating our own conference project that allows users to log in and join an ongoing conference. While in the conference, users will have the ability to exit and control camera and sound settings.


# The conference skeleton

**HTML sample**

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>WT Demo</title>
    <!-- Connect Watch Together SDK -->
    <script src="./WTSDK.js"></script>
</head>
<body>
  <button onclick="connect()">Start</button>
  <button onclick="disconnect()">Disconnect</button>
  <button onclick="videoOn()">Video ON</button>
  <button onclick="videoOff()">Video OFF</button>
  <button onclick="voiceOn()">Voice ON</button>
  <button onclick="voiceOff()">Voice Off</button>
  <button onclick="screenSharing()">Screen Share</button>
  <button onclick="stopScreenSharing()">Stop Screen Share</button>
    <div id="video">
      <!-- Container for video streams -->  
    </div>
    <script src="./index.js"></script>
</body>
</html>
```


# Connecting to a Session

The `connect` function is responsible for creating a connection between the clients.

```javascript
function connect() {
	let displayed_name = document.getElementById('display_name').value
	let streamingToken = document.getElementById('session_token').value

	if(!displayed_name || !streamingToken)
		alert('Display name and token is empty or invalid! Please check and try again!')

	WTSDK.Session.connect(
		streamingToken,
		displayed_name
	);
}
```

The `onConnected` function is used to define a callback that will be called when the local user's media stream is created. This callback can be used to perform various operations on the stream, such as displaying it in a video element.

```javascript
WTSDK.SessionListeners.onConnected(() => {
	...
	document.getElementById('config_dialog').style.display = 'none';

	const videos = document.getElementsByTagName('video')

	//On participant connect to the Session
	WTSDK.ParticipantListeners.onParticipantSpeaking(() => {
		// On participant speaking event
	});
	WTSDK.ParticipantListeners.onParticipantStopSpeaking(() => {
		// On participant stop speaking event
	});

	WTSDK.ReconnectListeners.onLocalConnectionResumed(() => {
		Array.from(videos).forEach(video => {
			video.parentElement.remove();
		})
	})

	WTSDK.SessionListeners.onStreamCreated(participant => {
		const { local, stream, participantId } = participant;

		const node = document.getElementById(participantId);

		if(node) {
			node.srcObject = participant.stream;
		} else {
			const div = document.createElement("div")
			div.setAttribute("class", "video-container");

			const video = document.createElement("video");
			video.id = participantId;
			video.className = local ? "local" : "";
			video.autoplay = true;
			video.muted = local;
			video.srcObject = stream;
			video.playsInline = true;
			video.disablePictureInPicture = true;

			div.appendChild(video)

			const container = document.querySelector("#gallery");
			container.appendChild(div);
			recalculateLayout();
		}
	});

	WTSDK.ParticipantListeners.onParticipantLeft(({participantId}) => {
		const el = document.getElementById(participantId);
		el.parentElement.remove();
		recalculateLayout();
	});
});
```

The `onParticipantSpeaking` and `onParticipantStopSpeaking` functions can be used to visually highlight the current speaker in a call.


# How to turn on and off video and audio

```javascript
let cameraState = true;
let micState = true;
```

To turn on and off video, use the `toggleVideo` function.

```javascript
function toggleVideo(){
	if(cameraState){
		WTSDK.Participant.disableVideo();
		cameraState = false;
		const video = document.getElementById('vide_control');
		video.classList.remove('vcam_on');
		video.classList.add('vcam_off');
	}else {
		WTSDK.Participant.enableVideo();
		cameraState = true;
		const video = document.getElementById('vide_control');
		video.classList.remove('vcam_off');
		video.classList.add('vcam_on');
	}
}
```

There is a similar function available for working with audio (`toggleAudio`).

```javascript
function toggleAudio() {
	if(micState){
		WTSDK.Participant.disableAudio();
		micState = false;
		const video = document.getElementById('audio_control');
		video.classList.remove('mic_on');
		video.classList.add('mic_off');
	}else {
		WTSDK.Participant.enableAudio();
		micState = true;
		const video = document.getElementById('audio_control');
		video.classList.remove('mic_off');
		video.classList.add('mic_on');
	}
}
```


# How to change video quality

You can adjust the video quality parameters by creating controls that allow you to set specific values for the resolution and frame rate.

```javascript
let videoWidth = 320;
let videoHeight = 240;
let frameRate = 12;
...
```

```javascript
function setVideoQuality(width, height, frameRate ) {
	let constraints = {};
	if(width)
		constraints.videoWidth = width
	if(height)
		constraints.videoHeight = height
	if(frameRate)
		constraints.frameRate = frameRate
	WTSDK.Participant.setMediaQuality(constraints)
}
...
```

```javascript
function changeWidth(width){
	videoWidth = width;
	document.getElementById('width-label').innerHTML = width;
}
function changeHeight(height){
	videoHeight = height;
	document.getElementById('height-label').innerHTML = height;
}
function applyQuality () {
	WTSDK.Participant.setMediaQuality({videoWidth: videoWidth, videoHeight: videoHeight, frameRate: 12})
}
```


# Errors handling

For any application, you'll need to add error handling. For logging errors, you can use tools such as Sentry.

[WT Error codes](https://documentation.sceenic.co/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-errorlisteners-websdk#session-error-codes)

```javascript
WTSDK.ErrorsListeners.onSessionError((e) => {
	...
	// Sending errors to your errors logger
	...
	// console.log('handling errors');
	// console.log(e.code);
	// console.log(e.message);
});
```


# Leave the call

The `disconnect` function is used to disconnect a participant from a session.

```javascript
function disconnect() {
	WTSDK.Session.disconnect();
	document.getElementById("gallery").innerHTML = "";

	document.getElementById('session_container').style.display = 'none';
	document.getElementById('config_dialog').style = `display: flex; flex-direction: column;`;
}
```


# Support

## Known Issues <a href="#web-javascriptandreact-knownissues" id="web-javascriptandreact-knownissues"></a>

* IE, and old Edge (A new one, chromium-based was released on January 2020) are not supported
* Screen sharing is not supported in Safari
* Screen sharing is flickering on Firefox 72 version - browser issue

## Supported browsers <a href="#web-javascriptandreact-supportedbrowsers" id="web-javascriptandreact-supportedbrowsers"></a>

* Google Chrome browser (Screen sharing from 72+ version)
* Firefox browser (latest release version) from v.66
* Opera browser (latest release version) from 63+
* Safari browser (latest release version)
* Mobile Google Chrome browser
* Mobile Firefox browser
* Mobile Opera
* Mobile Safari

## Next steps <a href="#web-javascriptandreact-nextsteps" id="web-javascriptandreact-nextsteps"></a>

* Learn more about Watch Together architecture - [Watch Together overview](/watch-together-sdk/sscale-confluence-watch-together-overview)
* Authentication overview [here](/watch-together-sdk/sscale-confluence-overview)
* The full code sample can be found here - [React full code sample](https://bitbucket.org/svmt/watchtogethersampleappreact/src/master/)

## Support <a href="#web-javascriptandreact-support" id="web-javascriptandreact-support"></a>

Need technical support? contact us at <Support@sceenic.co>.


# API references


# Android reference


# Session

## Nested classes <a href="#nested-classes" id="nested-classes"></a>

| Classes                                             |
| --------------------------------------------------- |
| SessionBuilder - Creates an instance of the Session |

## Fields <a href="#fields" id="fields"></a>

| Access and type                     | Field name                                                                                                               |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| *private final SessionListener*     | <ul><li><em><strong>mSessionListener</strong></em> - Interface for the Session's callbacks</li></ul>                     |
| *private SessionReconnectListener*  | <ul><li><em><strong>mSessionReconnectListener</strong></em> - Interface for the SessionReconnect's callbacks</li></ul>   |
| *private SessionConnectionListener* | <ul><li><em><strong>mSessionConnectionListener</strong></em> - Interface for the SessionConnection's callbacks</li></ul> |
| *private String*                    | <ul><li><em><strong>mUserName</strong></em> - The unique user's name for the session</li></ul>                           |
| *private AudioManager*              | <ul><li><strong>mAudioManager</strong> - Instance of AudioManager to work with audio routing</li></ul>                   |

## Public Constructor Summary <a href="#hardbreak-public-constructor-summary" id="hardbreak-public-constructor-summary"></a>

| Type           | Methods                                                        | Parameters                                                                                                        |
| -------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| SessionBuilder | Session(final SessionListener listener)                        | <ul><li><strong>listener</strong> - interface to session's callbacks;</li></ul>                                   |
| SessionBuilder | setReconnectListener(final SessionReconnectListener listener)  | <ul><li><strong>listener</strong> - interface to session’s reconnect callbacks;</li></ul>                         |
| SessionBuilder | setConnectionListener(final SessionConnectinListener listener) | <ul><li><strong>listener</strong> - interface to session's connection callbacks;</li></ul>                        |
| Session        | build(final Context context)                                   | <ul><li><strong>context</strong> - an interface to global information about an application environment;</li></ul> |

\
Public Method Summary <a href="#hardbreak-public-method-summary" id="hardbreak-public-method-summary"></a>
----------------------------------------------------------------------------------------------------------

| Type | Methods                                                                                                                                      | Parameters                                                                                                                                           |
| ---- | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| void | startCameraPreview() - creates local Media stream, which can be added on UI before Session ic connected.                                     |                                                                                                                                                      |
| void | <p>connect(String token )  -<br>This method will allow connecting to the Session</p>                                                         | <ul><li><strong>token</strong> - String value for authentication;</li></ul>                                                                          |
| void | <p>connect(String token, ParticipantType participantType)  -<br>This method will allow connecting to the Session with participant’s type</p> | <ul><li><strong>token</strong> - String value for authentication;</li><li><strong>participantType</strong> - participant’s connection type</li></ul> |
| void | <p>disconnect()<br>Disconnects from the Session</p>                                                                                          |                                                                                                                                                      |
| void | <p>switchCamera() - <br>The method switches camera capturer  for local stream from front camera to back</p>                                  |                                                                                                                                                      |
| void | sendMessage(String message) - The method sends messages to the Session all participants in the room                                          | <ul><li><strong>message</strong> - string messages</li></ul>                                                                                         |


# SessionListener

* The Interface SessionListener monitors all the events from the Session.
* The SessionListener is created together with the Session instance.

## Public Method Summary

Public Method Summary

| Type | Methods                                                                                                                                                                                                               | Parameters                                                                                                                                                                                                                                 |
| ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| void | <p>onConnected(List\<Participant> participants)</p><p>Connect to session</p>                                                                                                                                          | <ul><li><strong>participants</strong> - List of participants in the Session</li></ul>                                                                                                                                                      |
| void | <p>onDisconnected() -<br>Session is disconnected</p>                                                                                                                                                                  | <p><br></p>                                                                                                                                                                                                                                |
| void | <p>onError(SessionError error)  - <br>Receive error message</p>                                                                                                                                                       | <ul><li><strong>error</strong> -Detailed error information</li></ul>                                                                                                                                                                       |
| void | <p>onLocalParticipantJoined(final Participant participant) -</p><p>local participant is connected to session</p>                                                                                                      | <ul><li><strong>participant</strong> - Participant object representing the local device and media stream with audio and video tracks inside</li></ul>                                                                                      |
| void | onRemoteParticipantJoined(final Participant participant) -                                                                                                                                                            | <ul><li><strong>participant</strong> - Participant object representing a remote device and media stream with audio and video tracks inside</li></ul>                                                                                       |
| void | onUpdateParticipant(String participantId, Participant participant)                                                                                                                                                    | <ul><li><strong>participantId</strong> - the unique String value of the participant</li><li>participant - Participant object representing a remote device</li></ul>                                                                        |
| void | onRemoteParticipantLeft(final Participant participant)                                                                                                                                                                | <ul><li><strong>participantId</strong> - the unique String value of the participant</li></ul>                                                                                                                                              |
| void | <p>onParticipantMediaStateChanged(final String participantId, final MediaConfiguration.MediaType mediaType, final MediaConfiguration.MediaState mediaState) -</p><p>This method handle participant's media states</p> | <ul><li><strong>participantId</strong> - the unique String value of the participant</li><li><strong>mediaType</strong> - Enum of AUDIO, VIDEO, ALL values</li><li><strong>mediaState</strong> - Enum of ENABLED, DISABLED values</li></ul> |
| void | <p>onMessageReceived(final String participantId, final String message)</p><p>The method is receiving messages from remote participants into the current room</p>                                                      | <ul><li><strong>participantId</strong> - the unique String value of the participant</li><li><strong>message</strong> - receiving string messages</li></ul>                                                                                 |


# SessionReconnectListener

* The Interface SessionReconnectListener monitors the reconnect events from the Session.
* The SessionReconnectListener is created together with the Session instance.

| Type | Methods                                                                                                                                             | Parameters                                                                                                                                     |
| ---- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| void | <p>onParticipantReconnecting(String participantId)</p><p><em>callback for starting to reconnect remote participant</em></p>                         | <ul><li><strong>participantId</strong> - participan’s the unique</li></ul>                                                                     |
| void | <p>onParticipantReconnected(String participantId, String oldParticipantId)</p><p><em>callback for ending to reconnect remote participant</em>  </p> | <ul><li><strong>participantId</strong> - participan’s the unique</li><li><strong>oldParticipantId</strong> - participan’s the unique</li></ul> |


# SessionConnectionListener

* Interface SessionConnectionListener monitors connection states events from the Session.
* The SessionConnectionListener is created together with the Session instance.

| Type | Methods                                      | Parameters                                                                  |
| ---- | -------------------------------------------- | --------------------------------------------------------------------------- |
| void | onLocalConnectionLost()                      |                                                                             |
| void | onLocalConnectionResumed()                   |                                                                             |
| void | onRemoteConnectionLost(String participantId) | <ul><li><strong>participantId</strong> - participant’s the unique</li></ul> |


# Participant

## Field Summary <a href="#field-summary" id="field-summary"></a>

| String          | **name** - *Unique user name*                                  |
| --------------- | -------------------------------------------------------------- |
| ParticipantType | **participantType** - participant’s type                       |
| Boolean         | **isProgressReconnection** - state of participant reconnecting |
| Boolean         | **isConnectionLost** - state of participant connection lost    |
| Boolean         | **isVideoEnabled** - Holds the actual state of Video track     |
| Boolean         | **isAudioEnabled** - Holds the actual state of Audio track     |

## Public Method Summary <a href="#public-method-summary" id="public-method-summary"></a>

|         |                                                                  |                                                                                                                                               |
| ------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Type    | Methods                                                          | Parameters                                                                                                                                    |
| String  | name                                                             |                                                                                                                                               |
| void    | setName(final String name)                                       | <ul><li><strong>name</strong> - username</li></ul>                                                                                            |
| void    | setRenderer(final VideoRenderer videoRenderer)                   | <ul><li><strong>videoRenderer</strong> - <em>View in which video will be rendered</em><br></li></ul>                                          |
| void    | update(final Participant participant)                            | <ul><li><strong>participant</strong> - participant</li></ul>                                                                                  |
| void    | setParticipantStatsListener(OnParticipantStatsListener listener) | <ul><li><strong>listener</strong> - callback with json data about connection quality for audio and video participant</li></ul>                |
| void    | enableStats()                                                    |                                                                                                                                               |
| void    | enableAudio()                                                    | <p><br></p>                                                                                                                                   |
| void    | disableAudio()                                                   | <p><br></p>                                                                                                                                   |
| void    | enableVideo()                                                    | <p><br></p>                                                                                                                                   |
| void    | disableVideo()                                                   | <p><br></p>                                                                                                                                   |
| boolean | isAudioEnabled()                                                 | <p><br></p>                                                                                                                                   |
| boolean | isVideoEnabled()                                                 | <p><br></p>                                                                                                                                   |
| void    | setVolumeLevel(int level)                                        | <ul><li><strong>level</strong> - volume level</li></ul>                                                                                       |
| void    | attachStream(Media stream, Boolean mirror)                       | <ul><li><strong>stream</strong> - <em>media stream with audio and video tracks inside</em></li><li><strong>mirror</strong> - mirror</li></ul> |
| void    | dispose()                                                        | <p><br></p>                                                                                                                                   |
| String  | toString()                                                       | <p><br></p>                                                                                                                                   |


# ParticipantType

* ParticipantType can be change by the Session's function setLocalParticipantType(type: ParticipantType)

| Codes             | Remarks                                                                                                          |
| ----------------- | ---------------------------------------------------------------------------------------------------------------- |
| FULL\_PARTICIPANT | Publishes Audio and Video to the session and Subscribes to all other participants Audio and Video in the Session |
| VIEWER            | Subscribes to all other participants Audio and Video in the Session                                              |
| AV\_BROADCASTER   | Publishes Audio and Video to the Session                                                                         |
| A\_BROADCASTER    | Publishes only Audio to the Session                                                                              |


# SessionError

This class represents the errors available for the WatchTogether session.

## Field Summary <a href="#field-summary" id="field-summary"></a>

| Type   | Field                     |
| ------ | ------------------------- |
| Int    | code - *Error code*       |
| String | name - *Error name*       |
| String | message - *Error message* |

## Public Constructor Summary <a href="#public-constructor-summary" id="public-constructor-summary"></a>

| Type | Methods                                                               | Parameters                                                                                            |
| ---- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| void | SessionError(final int code, final String name, final String message) | <ul><li>code - Error number code</li><li> name - Error name</li><li>message - Error message</li></ul> |

## Error codes <a href="#error-codes" id="error-codes"></a>

| Code | Error                            | Description                                                                                    |
| ---- | -------------------------------- | ---------------------------------------------------------------------------------------------- |
| -1   | UNKNOWN\_ERROR                   | Internal server error                                                                          |
| 999  | GENERIC\_ERROR\_CODE             | The request is not supported                                                                   |
| 901  | GENERIC\_CONNECTION\_ERROR       | The client closed the connection in the middle of the process of joining the room              |
| 801  | TRANSPORT\_REQUEST\_ERROR\_CODE  | Failed to make a request to the media node                                                     |
| 802  | TRANSPORT\_RESPONSE\_ERROR\_CODE | Invalid response from media node                                                               |
| 803  | TRANSPORT\_ERROR\_CODE           | <ul><li>The request method is not supported</li><li>Invalid or missing request field</li></ul> |

## Media Error codes <a href="#media-error-codes" id="media-error-codes"></a>

| Code | Error                                 | Description                                                                                                                                                                   |
| ---- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 303  | MEDIA\_ENDPOINT\_ERROR\_CODE          | <ul><li>Already subscribed to media stream from target client</li><li>SDP exchange for this media stream is already in progress</li><li>Timeout during SDP exchange</li></ul> |
| 308  | MEDIA\_AUDIO\_PERMISSION\_ERROR\_CODE | <ul><li>Microphone permission is not granted</li></ul>                                                                                                                        |
| 309  | MEDIA\_VIDEO\_PERMISSION\_ERROR\_CODE | <ul><li>Camera permission is not granted</li></ul>                                                                                                                            |

## Session Error codes <a href="#session-error-codes" id="session-error-codes"></a>

| Code | Error                          | Description                                                                                                                                                                             |
| ---- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 206  | SESSION\_IS\_FULL\_ERROR\_CODE | Exceeded limit of streaming participants in the room                                                                                                                                    |
| 203  | SESSION\_CLOSED\_ERROR\_CODE   |                                                                                                                                                                                         |
| 201  | SESSION\_GENERIC\_ERROR\_CODE  | <p></p><ul><li>Unable to handle the request as the Session is locked </li><li>Active requests counter underflow (Internal server logic)</li><li>The Session is already locked</li></ul> |

## User Error codes <a href="#user-error-codes" id="user-error-codes"></a>

| Code | Error                                    | Description                                                                                                                                                                                                          |
| ---- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 106  | USER\_NOT\_AUTHORIZED\_ERROR\_CODE       | <ul><li>Attempting to send synchronization data while not having a synchronizer role</li><li>Invalid oldParticipantSecret during reconnection</li></ul>                                                              |
| 105  | USER\_NOT\_STREAMING\_ERROR\_CODE        | <ul><li>Attempting to start a stream with no video and audio</li><li>Attempting to send SDP response when there was no offer</li><li>Attempting to subscribe to a client that did not publish anything yet</li></ul> |
| 104  | EXISTING\_USER\_IN\_SESSION\_ERROR\_CODE | A Participant tries to join a Session with a name that already exists                                                                                                                                                |
| 103  | USER\_CLOSED\_ERROR\_CODE                | <ul><li>Source or target user has left the Session during the media subscription process</li><li>Target user has left the Session during SDP exchange</li></ul>                                                      |
| 102  | USER\_NOT\_FOUND\_ERROR\_CODE            | <ul><li>Attempting to execute request before joining the Session</li><li>Addressing client that is not in the Session</li></ul>                                                                                      |
| 101  | USER\_GENERIC\_ERROR\_CODE               | <ul><li>Invalid participant type</li><li>Target client is not is not available while in the process of media (un)subscription</li><li>ICE candidates queue overflow</li></ul>                                        |


# iOS Swift reference


# Session

## SessionBuilder <a href="#session-swifty-sessionbuilder" id="session-swifty-sessionbuilder"></a>

| Return type    | Method                                                          | Description                                  | Params                                                                              |
| -------------- | --------------------------------------------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------- |
|                | init()                                                          | The default initializer                      |                                                                                     |
| SessionBuilder | withUsername(\_ username: String) -> SessionBuilder             | Specify the username                         | <ul><li><strong>username</strong> - a display name</li></ul>                        |
| SessionBuilder | withDelegate(\_ delegate: SessionDelegate) -> SessionBuilder    | Subscribe a delegate for receiving callbacks | <ul><li><strong>delegate</strong> - an instance for reporting callbacks</li></ul>   |
| SessionBuilder | withCallbackQueue(\_ queue: DispatchQueue) -> SessionBuilder    | Register a queue for receiving callbacks     | <ul><li><strong>queue</strong> - a dispatch queue for reporting callbacks</li></ul> |
| SessionBuilder | withVideoCodec(\_ codec: VideoCodec) -> SessionBuilder          | Specify a video codec type                   | <ul><li><strong>codec</strong> - a video codec type</li></ul>                       |
| SessionBuilder | withVideoRenderer(\_ renderer: VideoRenderer) -> SessionBuilder | Specify a video renderer type                | <ul><li><strong>renderer</strong> - a video renderer type</li></ul>                 |
| Session        | build() -> Session                                              | Construct a session instance                 |                                                                                     |

## Field Summary <a href="#session-swifty-fieldsummary" id="session-swifty-fieldsummary"></a>

| LocalParticipant | localParticipant | Represents a local user                                                       |
| ---------------- | ---------------- | ----------------------------------------------------------------------------- |
| SessionDelegate? | delegate         | An optional delegate for receiving callbacks                                  |
| DispatchQueue    | callbackQueue    | A dispatch queue for receiving callbacks. The default queue is the main queue |
| SessionState     | sessionState     | Enum with current session state                                               |

## Method Summary <a href="#session-swifty-methodsummary" id="session-swifty-methodsummary"></a>

<br>

| Return type      | Methods                                                  | Description                                  | Parameters                                                                                                                                                      |
| ---------------- | -------------------------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| void             | connect(with token: String)                              | Connect a session                            | <ul><li><strong>token</strong> - authentication token </li></ul>                                                                                                |
| void             | disconnect()                                             | Leave a room and teardown WebRTC session     | <p><br></p>                                                                                                                                                     |
| UIViewController | createLogsViewController()                               | Return view controller with all Session logs | <p><br></p>                                                                                                                                                     |
| void             | sendMessage(message: String)                             | sends message to all conference participants | <ul><li><strong>message</strong> - text to send</li></ul>                                                                                                       |
| void             | forceDisconnect(participantId: String, message: String?) | kick partitipant by id from session          | <p></p><ul><li><strong>participantId</strong> -selected participant id</li><li> <strong>message -</strong> optional supplemental message(kick reason)</li></ul> |

## Static Method Summary <a href="#session-swifty-staticmethodsummary" id="session-swifty-staticmethodsummary"></a>

| Return type | Methods                                 | Description         | Parameters                                                              |
| ----------- | --------------------------------------- | ------------------- | ----------------------------------------------------------------------- |
| void        | setMinLogLevel(\_ logLevel: WTLogLevel) | Specify a log level | <ul><li><strong>logLevel</strong> - a level for logs produced</li></ul> |


# SessionDelegate

## Public Method Summary

| Type | Methods                                                                 | Description                                                                                                 | Parameters                                                                                                                                                                 |
| ---- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| void | onSessionConnected(sessionId: String, participants: \[Participant])     | This event handler is invoked when a session is connected                                                   | <ul><li><strong>sessionId</strong> - a unique identifier of a session</li><li><strong>participants</strong> - an array of users currently available in a session</li></ul> |
| void | onSessionDisconnect()                                                   | This event handler is invoked when a session finished                                                       | <p><br></p>                                                                                                                                                                |
| void | onSessionError(error: Error)                                            | This event handler is invoked when a session reported an error                                              | <ul><li><strong>error</strong> - an error produced</li></ul>                                                                                                               |
| void | onRemoteParticipantJoined(participant: Participant)                     | This event handler is invoked when a remote participant joined a session                                    | <ul><li><strong>participant</strong> - an object representing a participant</li></ul>                                                                                      |
| void | onRemoteParticipantLeft(participant: Participant)                       | This event handler is invoked when a participant left a session                                             | <ul><li><strong>participant</strong> - an object representing a participant</li></ul>                                                                                      |
| void | onRemoteParticipantStartMedia(participant: Participant)                 | This event handler is invoked when a remote participant started publishing media                            | <ul><li><strong>participant</strong> - an object representing a participant</li></ul>                                                                                      |
| void | onRemoteParticipantStopMedia(participant: Participant)                  | This event handler is invoked when a remote participant stopped publishing media                            | <ul><li><strong>participant</strong> - an object representing a participant</li></ul>                                                                                      |
| void | onRemoteParticipantNotification(message: String, participantId: String) | This event handler is invoked when a remote participant send message                                        | <ul><li><strong>message</strong> - text sent by participant</li><li><strong>participantId -</strong> sender's id</li></ul>                                                 |
| void | onDisconnected(reason: String?, initiator: String)                      | This event handler is invoked when a remote participant kick you from session, with optional message/reason | <p></p><ul><li><strong>reason</strong> - text sent by participant as kick reason</li><li><strong>initiator -</strong> sender's id</li></ul>                                |


# Participant

## Field Summary <a href="#participant-swifty-fieldsummary" id="participant-swifty-fieldsummary"></a>

| Type                              | Field                                                                                                                                                                     |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Bool                              | <p>isBroadcaster: Bool { get }</p><p>Indicates whether a user broadcasts any media or not.</p>                                                                            |
| Bool                              | <p>isAudioEnabled: Bool { get set }</p><p>A Bool which is <code>true</code> if the audio is currently muted, or <code>false</code> if the audio is currently unmuted.</p> |
| Bool                              | <p>isVideoEnabled: Bool { get set }</p><p>A Bool which is <code>true</code> if the video is currently muted, or <code>false</code> if the video is currently unmuted.</p> |
| Float                             | <p>volume: Float { get set }</p><p>Allows controlling a user's audio volume.</p>                                                                                          |
| ParticipantDelegate?              | <p>delegate: ParticipantDelegate? { get set }</p><p>An optional instance for receiving callbacks.</p>                                                                     |
| ParticipantActiveSpeakerDelegate? | speakerDelegate: ParticipantActiveSpeakerDelegate? { get set } An optional instance for receiving callbacks.                                                              |

## Public Method Summary <a href="#participant-swifty-publicmethodsummary" id="participant-swifty-publicmethodsummary"></a>

| Type   | Methods                                                                   | Parameters |
| ------ | ------------------------------------------------------------------------- | ---------- |
| String | <p>getId() -> String</p><p>Returns a unique identifier of a user.</p>     |            |
| String | <p>getDisplayName() -> String</p><p>Returns a display name of a user.</p> |            |
| UIView | <p>getVideo() -> UIView</p><p>Returns a video view of a user.</p>         |            |


# ParticipantActiveSpeakerDelegate

Public Method Summary\ <a href="#public-method-summary" id="public-method-summary"></a>
---------------------------------------------------------------------------------------

| Method                             | Description                               |
| ---------------------------------- | ----------------------------------------- |
| onRemoteParticipantStartSpeaking() | Triggered when participant start speaking |
| onRemoteParticipantStopSpeaking()  | Triggered when participant start speaking |


# ParticipantDelegate

## Public Method Summary

| Type | Methods                                                                                                 | Parameters                                                                             |
| ---- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| void | <p>onChangeMediaSettings()</p><p>An event indicating changed media settings.</p>                        |                                                                                        |
| void | <p>onStreamUpdate(qualityInfo: StreamQualityInfo)</p><p>An event indicating updated stream quality.</p> | <ul><li><strong>qualityInfo</strong> - Stream quality (bad, good, excellent)</li></ul> |
| void | <p>onReconnecting()</p><p>An event indicating a start of a reconnect process.</p>                       |                                                                                        |
| void | <p>onReconnected()</p><p>An event indicating a finish of a reconnect process.</p>                       |                                                                                        |


# LocalParticipant

* The LocalParticipant object is responsible for all the properties and methods from Participant object

## Field Summary

| Type                     | Field                                                                                          |
| ------------------------ | ---------------------------------------------------------------------------------------------- |
| ParticipantType          | The participantType represents a type of local user - full participant, viewer or broadcaster. |
| AVCaptureDevice.Position | The cameraPosition is a value indicating whether the back or front camera is currently active. |
| VideoQuality             | The videoQuality represents a video quality - low, default or high.                            |
| frameRate                | private variable with range of 10-60 to set                                                    |

## Public Method Summary

| Return type                             | Methods                                     | Description                                                                                                                    |
| --------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| void                                    | startCameraPreview() -> UIView              | Start capturing video from the camera.                                                                                         |
| LocalParticipantSnapshot                | takeSnapshot()                              | <p>return snapshot of local participant settings.</p><p><em>Note</em>: You can't read and edit snapshot object</p><p><br></p>  |
| You can't read and edit snapshot object |                                             |                                                                                                                                |
| void                                    | set(snapshot: LocalParticipantSnapshot)     | snapshot - is parameter with participant settings                                                                              |
| void                                    | set(orientation: AVCaptureVideoOrientation) | orientation - is [AVCaptureVideoOrientation](https://developer.apple.com/documentation/avfoundation/avcapturevideoorientation) |
| Int32                                   | getFrameRate()                              | fps ot translated video                                                                                                        |
| void                                    | setFrameRate(fps: Int)                      | frames to set(limited in range of 10-60)                                                                                       |


# WTError

## Field Summary <a href="#field-summary" id="field-summary"></a>

| Type   | Field                                  |
| ------ | -------------------------------------- |
| Int    | code - *Error code*                    |
| String | localizedDescription - *Error message* |

## User Error codes <a href="#user-error-codes" id="user-error-codes"></a>

| Codes | Errors                | Description                                                                                                                                                                                                          |
| ----- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 101   | userGeneric           | <ul><li>Invalid participant type</li><li>Target client is not is not available while in the process of media (un)subscription</li><li>ICE candidates queue overflow</li></ul>                                        |
| 102   | userNotFound          | <ul><li>Attempting to execute request before joining the Session</li><li>Addressing client that is not in the Session</li></ul>                                                                                      |
| 103   | userClosedError       | <ul><li>Source or target user has left the Session during the media subscription process</li><li>Target user has left the Session during SDP exchange</li></ul>                                                      |
| 104   | existingUserInSession | A Participant tries to join a Session with a name that already exists                                                                                                                                                |
| 105   | userNotStreaming      | <ul><li>Attempting to start a stream with no video and audio</li><li>Attempting to send SDP response when there was no offer</li><li>Attempting to subscribe to a client that did not publish anything yet</li></ul> |
| 106   | userNotAuthorized     | <ul><li>Attempting to send synchronization data while not having a synchronizer role</li><li>Invalid oldParticipantSecret during reconnection</li></ul>                                                              |

## Session Error codes <a href="#session-error-codes" id="session-error-codes"></a>

| Codes | Errors              | Description                                                                                                                                                                      |
| ----- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 201   | sessionGenericError | <ul><li>Unable to handle the request as the Session is locked </li><li>Active requests counter underflow (Internal server logic)</li><li>The Session is already locked</li></ul> |
| 203   | sessionClosed       |                                                                                                                                                                                  |
| 206   | sessionIsFull       | Exceeded limit of streaming participants in the room                                                                                                                             |

## Media Error codes <a href="#media-error-codes" id="media-error-codes"></a>

| Codes             | Errors                                | Description                                                                                                                                                                   |
| ----------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 303               | mediaEndpoint                         | <ul><li>Already subscribed to media stream from target client</li><li>SDP exchange for this media stream is already in progress</li><li>Timeout during SDP exchange</li></ul> |
| <p></p><p>308</p> | MEDIA\_AUDIO\_PERMISSION\_ERROR\_CODE | Microphone permission is not granted                                                                                                                                          |
| <p></p><p>309</p> | MEDIA\_VIDEO\_PERMISSION\_ERROR\_CODE | Camera permission is not granted                                                                                                                                              |

## Transport Error codes <a href="#transport-error-codes" id="transport-error-codes"></a>

| Codes | Errors            | Description                                                                                |
| ----- | ----------------- | ------------------------------------------------------------------------------------------ |
| 801   | transportRequest  | Failed to make request to media node                                                       |
| 802   | transportResponse | Invalid response from media node                                                           |
| 803   | transport         | <ul><li>Request method is not supported</li><li>Invalid or missing request field</li></ul> |

## Generic Error codes <a href="#generic-error-codes" id="generic-error-codes"></a>

| Codes | Errors                 | Description                                                                       |
| ----- | ---------------------- | --------------------------------------------------------------------------------- |
| -1    | unknownError           | Internal server error                                                             |
| 901   | genericConnectionError | The Client closed the connection in the middle of the process of joining the room |
| 999   | genericError           | The Request is not supported                                                      |

## Token Errors <a href="#token-errors" id="token-errors"></a>

| Codes | Errors               | Description                                 |
| ----- | -------------------- | ------------------------------------------- |
| 1100  | invalidTokenExpired  | Token is expired                            |
| 1101  | invalidToken         | Token string is not full or incorect input  |
| 1102  | invalidTokenData     | Token string is not full or incorect format |
| 1103  | invalidTokenJsonData | Token not contains all required data        |

## Socket Erros <a href="#socket-erros" id="socket-erros"></a>

| Codes | Errors            | Description                      |
| ----- | ----------------- | -------------------------------- |
| 1104  | invalidSocketURL  | Url to socket in invalid         |
| 1105  | invalidStunServer | Stun server field is not correct |


# DataTypes

## VideoCodec <a href="#videocodec" id="videocodec"></a>

| Case | Description                                     |
| ---- | ----------------------------------------------- |
| H264 | Hardware H.264 encode & decode via VideoToolbox |
| VP8  | Stable, software VP8 encode & decode via libvpx |

## VideoRenderer <a href="#videorenderer" id="videorenderer"></a>

| Case   | Description                                         |
| ------ | --------------------------------------------------- |
| Metal  | Modern advanced rendering using graphics processors |
| OpenGL | Hardware-accelerated 2D and 3D graphics rendering   |

## VideoQuality <a href="#videoquality" id="videoquality"></a>

<table data-header-hidden><thead><tr><th width="427.2212389380531">Case</th><th>Description</th></tr></thead><tbody><tr><td>Case</td><td>Description</td></tr><tr><td>Low</td><td>144x81</td></tr><tr><td>Default</td><td>480x270</td></tr><tr><td>High</td><td>1200x675</td></tr><tr><td>w144</td><td>144x81</td></tr><tr><td>w160</td><td>160x90</td></tr><tr><td>w176</td><td>176x99</td></tr><tr><td>w240</td><td>240x135</td></tr><tr><td>w288</td><td>288x162</td></tr><tr><td>w320</td><td>320x180</td></tr><tr><td>w352</td><td>352x198</td></tr><tr><td>w360</td><td>368x207</td></tr><tr><td>w480</td><td>480x270</td></tr><tr><td>w608</td><td>608x342</td></tr><tr><td>w640</td><td>640x360</td></tr><tr><td>w720</td><td>720x405</td></tr><tr><td>w768</td><td>768x432</td></tr><tr><td>w800</td><td>800x450</td></tr><tr><td>w960</td><td>960x540</td></tr><tr><td>w1024</td><td>1024x576</td></tr><tr><td>w1088</td><td>1088x612</td></tr><tr><td>w1200</td><td>1200x675</td></tr></tbody></table>

## ParticipantType <a href="#participanttype" id="participanttype"></a>

| Case            | Description               |
| --------------- | ------------------------- |
| fullParticipant | Publish and receive media |
| viewer          | Receive media             |
| broadcaster     | Publish media             |

## WTLogLevel <a href="#wtloglevel" id="wtloglevel"></a>

| Case    | Description |
| ------- | ----------- |
| debug   |             |
| info    |             |
| warning |             |
| error   |             |
| none    |             |

## StreamQuality <a href="#streamquality" id="streamquality"></a>

| Case      | Description              |
| --------- | ------------------------ |
| bad       | Bad stream quality       |
| good      | Good stream quality      |
| excellent | Excellent stream quality |

## SessionState <a href="#sessionstate" id="sessionstate"></a>

| Case          | Description                             |
| ------------- | --------------------------------------- |
| created       | Session is created                      |
| connected     | Session is connected                    |
| reconnecting  | Session is reconnecting                 |
| switchingMode | Session is switch mode from MESH to SFU |
| disconnecting | Session is disconnecting                |
| disconnected  | Session is disconnected                 |


# iOS Objective-c adapter reference


# SessionAdapter

## SessionAdapter <a href="#sessionadapter" id="sessionadapter"></a>

| Method                                              | Description                   | Params                                                               |
| --------------------------------------------------- | ----------------------------- | -------------------------------------------------------------------- |
| init(with username: String)                         | Specify the username          | <ul><li><strong>username</strong> - a display name<br><br></li></ul> |
| changeVideoCodec(codec: VideoCodecAdapter)          | Specify a video codec type    | <ul><li><strong>codec</strong> - a video codec type</li></ul>        |
| changeVideoRenderer(renderer: VideoRendererAdapter) | Specify a video renderer type | <ul><li><strong>renderer</strong> - a video renderer type</li></ul>  |
| build()                                             | Construct a session instance  |                                                                      |

## Field Summary <a href="#field-summary" id="field-summary"></a>

| Type                    | Field            | Description                                                                              |
| ----------------------- | ---------------- | ---------------------------------------------------------------------------------------- |
| LocalParticipantAdapter | localParticipant | Represents a local user                                                                  |
| SessionAdapterDelegate? | delegate         | An optional delegate registered for receiving callbacks                                  |
| DispatchQueue           | callbackQueue    | A dispatch queue registered for receiving callbacks. The default queue is the main queue |
| SessionStateAdapter     | sessionState     | Enum with current session state                                                          |

## Method Summary <a href="#method-summary" id="method-summary"></a>

| Return type      | Methods                                                  | Description                                  | Parameters                                                                                                                                                      |
| ---------------- | -------------------------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| void             | connect(with token: String)                              | Connect a session                            | <ul><li><strong>token</strong> - Authentication token </li></ul>                                                                                                |
| void             | disconnect()                                             | Leave a room and teardown WebRTC session     |                                                                                                                                                                 |
| UIViewController | createLogsViewController()                               | Return view controller with all Session logs |                                                                                                                                                                 |
| void             | sendMessage(message: String)                             | sends message to all conference participants | **message** - text to send                                                                                                                                      |
| void             | forceDisconnect(participantId: String, message: String?) | kick partitipant by id from session          | <p></p><ul><li><strong>participantId</strong> -selected participant id</li><li> <strong>message -</strong> optional supplemental message(kick reason)</li></ul> |

## Static Method Summary <a href="#static-method-summary" id="static-method-summary"></a>

| Return type | Methods                                  | Description         | Parameters                                                              |
| ----------- | ---------------------------------------- | ------------------- | ----------------------------------------------------------------------- |
| void        | setMinLogLevel(\_ logLevel: WTALogLevel) | Specify a log level | <ul><li><strong>logLevel</strong> - a level for logs produced</li></ul> |


# SessionAdapterDelegate

## Public Method Summary

| Type | Methods                                                                    | Description                                                                                                 | Parameters                                                                                                                                                                 |
| ---- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| void | onSessionConnected(sessionId: String, participants: \[ParticipantAdapter]) | This event handler is invoked when a session is connected                                                   | <ul><li><strong>sessionId</strong> - a unique identifier of a session</li><li><strong>participants</strong> - an array of users currently available in a session</li></ul> |
| void | onSessionDisconnect()                                                      | This event handler is invoked when a session finished                                                       | <p><br></p>                                                                                                                                                                |
| void | onSessionError(error: Error)                                               | This event handler is invoked when a session reported an error                                              | <ul><li><strong>error</strong> - an error produced</li></ul>                                                                                                               |
| void | onRemoteParticipantJoined(participant:ParticipantAdapter)                  | This event handler is invoked when a remote participant joined a session                                    | <ul><li><strong>participant</strong> - an object representing a participant</li></ul>                                                                                      |
| void | onRemoteParticipantLeft(participant:ParticipantAdapter)                    | This event handler is invoked when a participant left a session                                             | <ul><li><strong>participant</strong> - an object representing a participant</li></ul>                                                                                      |
| void | onRemoteParticipantStartMedia(participant:ParticipantAdapter)              | This event handler is invoked when a remote participant started publishing media                            | <ul><li><strong>participant</strong> - an object representing a participant</li></ul>                                                                                      |
| void | onRemoteParticipantStopMedia(participant:ParticipantAdapter)               | This event handler is invoked when a remote participant stopped publishing media                            | <ul><li><strong>participant</strong> - an object representing a participant</li></ul>                                                                                      |
| void | onRemoteParticipantNotification(message: String, participantId: String)    | This event handler is invoked when a remote participant send message                                        | <ul><li><strong>message -</strong> text sent by participant</li><li><strong>participantId</strong> -sender's id</li></ul>                                                  |
| void | onDisconnected(reason: String?, initiator: String)                         | This event handler is invoked when a remote participant kick you from session, with optional message/reason | <p></p><ul><li><strong>reason</strong> - text sent by participant as kick reason</li><li><strong>initiator -</strong> sender's id</li></ul>                                |


# ParticipantAdapter

## Field Summary <a href="#participantadapter-objective-c-fieldsummary" id="participantadapter-objective-c-fieldsummary"></a>

| Type                                     | Field                                                                                                                                                                     |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Bool                                     | <p>isBroadcaster: Bool { get }</p><p>Indicates whether a user broadcasts any media or not.</p>                                                                            |
| Bool                                     | <p>isAudioEnabled: Bool { get set }</p><p>A Bool which is <code>true</code> if the audio is currently muted, or <code>false</code> if the audio is currently unmuted.</p> |
| Bool                                     | <p>isVideoEnabled: Bool { get set }</p><p>A Bool which is <code>true</code> if the video is currently muted, or <code>false</code> if the video is currently unmuted.</p> |
| Float                                    | <p>volume: Float { get set }</p><p>Allows controlling a user's audio volume.</p>                                                                                          |
| ParticipantAdapterDelegate?              | <p>delegate: ParticipantAdapterDelegate? { get set }</p><p>An optional instance for receiving callbacks.</p>                                                              |
| ParticipantAdapterActiveSpeakerDelegate? | <p>speakerDelegate: ParticipantAdapterActiveSpeakerDelegate? { get set } An optional instance for receiving callbacks.</p><p></p>                                         |

## Public Method Summary <a href="#participantadapter-objective-c-publicmethodsummary" id="participantadapter-objective-c-publicmethodsummary"></a>

| Type   | Methods                                                                   | Parameters |
| ------ | ------------------------------------------------------------------------- | ---------- |
| String | <p>getId() -> String</p><p>Returns a unique identifier of a user.</p>     |            |
| String | <p>getDisplayName() -> String</p><p>Returns a display name of a user.</p> |            |
| UIView | <p>getVideo() -> UIView</p><p>Returns a video view of a user.</p>         |            |


# LocalParticipantAdapter

## Field Summary

| Type                     | Field                                                                                          |
| ------------------------ | ---------------------------------------------------------------------------------------------- |
| ParticipantTypeAdapter   | The participantType represents a type of local user - full participant, viewer or broadcaster. |
| AVCaptureDevice.Position | The cameraPosition is a value indicating whether the back or front camera is currently active. |
| VideoQualityAdapter      | The videoQuality represents a video quality - low, default or high.                            |
| frameRate                | The frameRate represents a video frame rate value with range 10-60                             |

## Public Method Summary

| Return type                     | Methods                                        | Description                                                                                              | Parameters                                                                                                                      |
| ------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| void                            | startCameraPreview() -> UIView                 | Start capturing video from the camera.                                                                   |                                                                                                                                 |
| LocalParticipantSnapshotAdapter | takeSnapshot()                                 | <p>Returns the snapshot of local participant settings.</p><p>You can't read and edit snapshot object</p> |                                                                                                                                 |
| void                            | set(snapshot: LocalParticipantSnapshotAdapter) |                                                                                                          | **snapshot** - is parameter with participant settings                                                                           |
| void                            | set(orientation: AVCaptureVideoOrientation)    |                                                                                                          | **orientation** - [AVCaptureVideoOrientation](https://developer.apple.com/documentation/avfoundation/avcapturevideoorientation) |


# ParticipantAdpaterDelegate

## Public Method Summary

| Type | Methods                                                                                                       | Parameters                                                                             |
| ---- | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| void | <p>onChangeMediaSettings()</p><p>An event indicating changed media settings.</p>                              |                                                                                        |
| void | <p>onStreamUpdate(qualityInfo:StreamQualityInfoAdapter)</p><p>An event indicating updated stream quality.</p> | <ul><li><strong>qualityInfo</strong> - Stream quality (bad, good, excellent)</li></ul> |
| void | <p>onReconnecting()</p><p>An event indicating a start of a reconnect process.</p>                             |                                                                                        |
| void | <p>onReconnected()</p><p>An event indicating a finish of a reconnect process.</p>                             |                                                                                        |


# ParticipantAdapterActiveSpeakerDelegate

## Public Method Summary

| Type | Methods                                        | Description                                      |
| ---- | ---------------------------------------------- | ------------------------------------------------ |
| void | <p>onRemoteParticipantStartSpeaking</p><p></p> | An event indicating participant is speaking      |
| void | <p>onRemoteParticipantStopSpeaking</p><p></p>  | An event indicating participant stopped speaking |


# NSError

## Field Summary <a href="#field-summary" id="field-summary"></a>

| Type   | Field                                  |
| ------ | -------------------------------------- |
| Int    | code - *Error code*                    |
| String | localizedDescription - *Error message* |

## User Error codes <a href="#user-error-codes" id="user-error-codes"></a>

| Codes | Errors                | Description                                                                                                                                                                                                          |
| ----- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 101   | userGeneric           | <ul><li>Invalid participant type</li><li>Target client is not is not available while in the process of media (un)subscription</li><li>ICE candidates queue overflow</li></ul>                                        |
| 102   | userNotFound          | <ul><li>Attempting to execute request before joining the Session</li><li>Addressing client that is not in the Session</li></ul>                                                                                      |
| 103   | userClosedError       | <ul><li>Source or target user has left the Session during the media subscription process</li><li>Target user has left the Session during SDP exchange</li></ul>                                                      |
| 104   | existingUserInSession | A Participant tries to join a Session with a name that already exists                                                                                                                                                |
| 105   | userNotStreaming      | <ul><li>Attempting to start a stream with no video and audio</li><li>Attempting to send SDP response when there was no offer</li><li>Attempting to subscribe to a client that did not publish anything yet</li></ul> |
| 106   | userNotAuthorized     | <ul><li>Attempting to send synchronization data while not having a synchronizer role</li><li>Invalid oldParticipantSecret during reconnection</li></ul>                                                              |

## Session Error codes <a href="#session-error-codes" id="session-error-codes"></a>

| Codes | Errors              | Description                                                                                                                                                                      |
| ----- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 201   | sessionGenericError | <ul><li>Unable to handle the request as the Session is locked </li><li>Active requests counter underflow (Internal server logic)</li><li>The Session is already locked</li></ul> |
| 203   | sessionClosed       |                                                                                                                                                                                  |
| 206   | sessionIsFull       | Exceeded limit of streaming participants in the room                                                                                                                             |

## Media Error codes <a href="#media-error-codes" id="media-error-codes"></a>

| Codes | Errors        | Description                                                                                                                                                                   |
| ----- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 303   | mediaEndpoint | <ul><li>Already subscribed to media stream from target client</li><li>SDP exchange for this media stream is already in progress</li><li>Timeout during SDP exchange</li></ul> |

## Transport Error codes <a href="#transport-error-codes" id="transport-error-codes"></a>

| Codes | Errors            | Description                                                                                |
| ----- | ----------------- | ------------------------------------------------------------------------------------------ |
| 801   | transportRequest  | Failed to make request to media node                                                       |
| 802   | transportResponse | Invalid response from media node                                                           |
| 803   | transport         | <ul><li>Request method is not supported</li><li>Invalid or missing request field</li></ul> |

## Generic Error codes <a href="#generic-error-codes" id="generic-error-codes"></a>

| Codes | Errors                 | Description                                                                       |
| ----- | ---------------------- | --------------------------------------------------------------------------------- |
| -1    | unknownError           | Internal server error                                                             |
| 901   | genericConnectionError | The Client closed the connection in the middle of the process of joining the room |
| 999   | genericError           | The Request is not supported                                                      |

## Token Errors <a href="#token-errors" id="token-errors"></a>

| Codes | Errors               |
| ----- | -------------------- |
| 1101  | invalidToken         |
| 1102  | invalidTokenData     |
| 1103  | invalidTokenJsonData |

## Socket Erros <a href="#socket-erros" id="socket-erros"></a>

| Codes | Errors            |
| ----- | ----------------- |
| 1104  | invalidSocketURL  |
| 1105  | invalidStunServer |


# DataTypes

## VideoCodecAdapter <a href="#videocodecadapter" id="videocodecadapter"></a>

| Case | Description                                     |
| ---- | ----------------------------------------------- |
| H264 | Hardware H.264 encode & decode via VideoToolbox |
| VP8  | Stable, software VP8 encode & decode via libvpx |

## VideoRendererAdapter <a href="#videorendereradapter" id="videorendereradapter"></a>

| Case   | Description                                         |
| ------ | --------------------------------------------------- |
| Metal  | Modern advanced rendering using graphics processors |
| OpenGL | Hardware-accelerated 2D and 3D graphics rendering   |

## WTALogLevel <a href="#wtaloglevel" id="wtaloglevel"></a>

| Case    | Description |
| ------- | ----------- |
| debug   |             |
| info    |             |
| warning |             |
| error   |             |
| none    |             |

## StreamQualityAdapter <a href="#streamqualityadapter" id="streamqualityadapter"></a>

| Case      | Description              |
| --------- | ------------------------ |
| bad       | Bad stream quality       |
| good      | Good stream quality      |
| excellent | Excellent stream quality |

## SessionStateAdapter <a href="#sessionstateadapter" id="sessionstateadapter"></a>

| Case          | Description                             |
| ------------- | --------------------------------------- |
| created       | Session is created                      |
| connected     | Session is connected                    |
| reconnecting  | Session is reconnecting                 |
| switchingMode | Session is switch mode from MESH to SFU |
| disconnecting | Session is disconnecting                |
| disconnected  | Session is disconnected                 |

## VideoQualityAdapter <a href="#videoqualityadapter" id="videoqualityadapter"></a>

<table data-header-hidden><thead><tr><th width="427.2212389380531">Case</th><th>Description</th></tr></thead><tbody><tr><td>Case</td><td>Description</td></tr><tr><td>Low</td><td>144x81</td></tr><tr><td>Default</td><td>480x270</td></tr><tr><td>High</td><td>1200x675</td></tr><tr><td>w144</td><td>144x81</td></tr><tr><td>w160</td><td>160x90</td></tr><tr><td>w176</td><td>176x99</td></tr><tr><td>w240</td><td>240x135</td></tr><tr><td>w288</td><td>288x162</td></tr><tr><td>w320</td><td>320x180</td></tr><tr><td>w352</td><td>352x198</td></tr><tr><td>w360</td><td>368x207</td></tr><tr><td>w480</td><td>480x270</td></tr><tr><td>w608</td><td>608x342</td></tr><tr><td>w640</td><td>640x360</td></tr><tr><td>w720</td><td>720x405</td></tr><tr><td>w768</td><td>768x432</td></tr><tr><td>w800</td><td>800x450</td></tr><tr><td>w960</td><td>960x540</td></tr><tr><td>w1024</td><td>1024x576</td></tr><tr><td>w1088</td><td>1088x612</td></tr><tr><td>w1200</td><td>1200x675</td></tr></tbody></table>

## ParticipantTypeAdapter <a href="#participanttype" id="participanttype"></a>

| Case            | Description               |
| --------------- | ------------------------- |
| fullParticipant | Publish and receive media |
| viewer          | Receive media             |
| broadcaster     | Publish media             |


# Web reference

| Classes                                                                                                                                                        | Description                     |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
| [Session](broken://pages/-Lrnwj3cLqVpPHahv-ML)                                                                                                                 | Session logic                   |
| [Participant](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-participant-websdk)                   | Participant logic               |
| [SessionListeners](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-sessionlisteners-websdk)         | Ssession events                 |
| [ParticipantListeners](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-participantlisteners-websdk) | Participant events              |
| [ErrorListeners](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-errorlisteners-websdk)             | Errors handling                 |
| [ReconnectListeners](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/reconnectlisteners)                              | Reconnect functionality         |
| [MediaDevices](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/untitled)                                              | Choosing the user media devices |


# WT Session

Session is the real-time communication session between two or more browsers or devices using the WT SDK. During a session, audio, video, and data are transmitted between the participants.

* [connect(streamingToken, participantName, userConstraints, metadata)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/wt-session/wtsession.connect-stoken-pname-uc-m)
* [connectAsAViewer(streamingToken, participantName)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/wt-session/wtsession.connectasaviewer-stoken-pname)
* [WTSession.connectWithSharingScreen(sToken, pName)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/wt-session/wtsession.connectwithsharingscreen-stoken-pname)
* [WTSession.disconnect()](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/wt-session/wtsession.disconnect)
* [WTSession.enableStats()](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/wt-session/wtsession.enablestats)
* [WTSession.sendMessage(msg)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/wt-session/wtsession.sendmessage-msg)
* [WTSession.sendPlayerData(time)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/wt-session/wtsession.sendplayerdata-time)
* [async WTSession.getSessionState()](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/wt-session/async-wtsession.getsessionstate)

{% hint style="warning" %}
Known issue:

When applying H264 codec in Safari v15.x feature of enable/disable camera can cause a crash. To prevent this kind of problem instead of enable/disable video track .stop() method applied. In such cases light indicator of camera stay enabled, but video stream is stopped.&#x20;
{% endhint %}


# WTSession.connect(sToken, pName, uC, m)

`connect(streamingToken, participantName, userConstraints, metadata)`

This method will allow you to connect to the session

* *streamingToken* (*required*) - JWT token which you can get from CAS. Note: For connecting two or users in the same session you need to share tokens between participants.
* *participantName (required)* - participant name in the session
* userConstraints (*optional*) - object with two fields {audio: boolean, video: boolean, videoCodecs\*: Object }

```
//* Defaults for videoCodecs constraints:
videoCodecs: {
   priorityList: ['H264', 'VP8'],
   forcingCodec: false,
   applyToAnswer: false,
   skipPrioritization: false,
}
```

1. priorityList (Array) - ordered video codecs list by priority (keep in mind that browsers are still select the best approach based on internal algorithms)
2. forcingCodec (String || boolean) - a specific codec which will be used to prepare  a SDP offer (marks that device able use for output streaming video the only selected codec)
3. applyToAnswer (boolean) - If true codec from forcingCodec property will be applied not only to outgoing video, but onto incoming as well. Keep in mind if some of device doesn’t support selected codec it would not be able to connect
4. skipPrioritization (boolean) - if true then browser sort and apply codecs according to internal SDP specification

* *metadata (optional)* - object allow to share some info between participants


# WTSession.connectAsAViewer(sToken, pName)

`connectAsAViewer(streamingToken, participantName)`

*This method will allow you to connect to the session* as a viewer meaning, without Audio or Video.

* *streamingToken* (*required*) - JWT token which you can get from CAS. Note: For connecting two or users in the same session you need to share tokens between participants.
* *participantName (required)* - participant name in the session


# WTSession.connectWithSharingScreen(sToken, pName)

`connectWithSharingScreen(streamingToken, participantName)`

This method will allow you to connect to the session using only screen sharing (with microphone and screen sharing)

* *streamingToken* (*required*) - JWT token which you can get from CAS. Note: For connecting two or users in the same session you need to share tokens between participants.
* *participantName (required)* - participant name in the session


# WTSession.disconnect()

`disconnect()`

Disconnect from the session.


# WTSession.enableStats()

`enableStats()`

This method will allow the sending WebRTC Stats. WebRTC stats is a set of statistics and metrics that provide information about the performance and quality of a WebRTC connection.


# WTSession.sendMessage(msg)

`sendMessage(message)`

This method will allow you to send message to other participants in the room.

* message - a string with the message


# WTSession.sendPlayerData(time)

`sendPlayerData(time)`

This method will allow send current player time

* time - number


# async WTSession.getSessionState()

`async getSessionState()`

This method will retrieve actual session state from web-server

* Result has the following shape:

```
const state = WTSDK.Session.getSessionState();
// state
// {
//     participants, //Array of participants
//     sessionId, // Current session ID
// }
```


# SessionListeners

SessionListeners is used to register callback functions that handle the lifecycle of a session.

* [onConnected(fn)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-sessionlisteners-websdk/wtsessionlisteners.onconnected-fn)
* [onDisconnected(fn)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-sessionlisteners-websdk/wtsessionlisteners.ondisconnected-fn)
* [onStreamCreated(fn)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-sessionlisteners-websdk/wtsessionlisteners.onstreamcreated-fn)
* [onLocalStreamCreated(fn)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-sessionlisteners-websdk/wtsessionlisteners.onlocalstreamcreated-fn)
* [onMosReport(fn)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-sessionlisteners-websdk/wtsessionlisteners.onmosreport-fn)
* [offMosReport(fn)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-sessionlisteners-websdk/wtsessionlisteners.offmosreport-fn)
* [onMessageReceived(fn)](broken://pages/DwOIC14xahFhfW7X7nrP)
* [onSyncData(fn)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-sessionlisteners-websdk/wtsessionlisteners.onsyncdata-fn)
* [onIceDisconnected(fn)](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-sessionlisteners-websdk/wtsessionlisteners.onicedisconnected-fn)<br>


# WTSessionListeners.onConnected(fn)

`onConnected(fn)`

An event handler for when participant connects to Session

The *callback function* will be invoked after the connection to the session is established

* *The callback function* receives a parameter with the list of participants in the session


# WTSessionListeners.onDisconnected(fn)

`onDisconnected(fn)`

An event handler for when participant disconnected from the Session

The *callback function* will be invoked after disconnection from session


# WTSessionListeners.onStreamCreated(fn)

`onStreamCreated(fn)`

An event handler for when participant's streams are created

The *callback function* will be invoked when a stream of a new participant is created

* *The Callback function* receives the MediaStream of the new participant


# WTSessionListeners.onLocalStreamCreated(fn)

`onLocalStreamCreated(fn)`

* The *callback function* will be invoked when the sdk gets the stream from the user’s webcam and returns back this stream without publishing


# WTSessionListeners.onMosReport(fn)

`onMosReport(fn)`

The callback function will be invoked when the SDK calculated new MoS values.


# WTSessionListeners.offMosReport(fn)

`offMosReport(fn)`

Disable a callback which reacting to MoS values calculation.


# WTSessionListeners.onMessageReceived(fn)

`onMessageReceived(fn)`

`const fn = ({message, participantId}) => {/*code*/}`

The callback will be invoked when a participant sends a message to the session

* message - string message
* participantId - identification of participant that sent a message


# WTSessionListeners.onSyncData(fn)

`onSyncData(fn)`

`const fn= ({data, participantId}) => {/*code*/}`

The callback will be invoked when participant receives a synchronization event. Inside the data from the other participants will be available


# WTSessionListeners.onIceDisconnected(fn)

`onIceDisconnected(fn)`\
`const fn = () => {/*code*/}`

The callback will be invoked in case of ice connection state becomes `disconnected`


# Participant

## Public fields <a href="#sessionerror-publicconstructorsummary" id="sessionerror-publicconstructorsummary"></a>

| **Name**          | **Description**                                                                                                                                                                                      |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| participantId     | <ul><li>Unique identifier of a participant</li></ul>                                                                                                                                                 |
| participantName   | <ul><li>Name of participant</li></ul>                                                                                                                                                                |
| isLocal           | <ul><li>Identifies whether the participant is a local one</li></ul>                                                                                                                                  |
| stream            | <ul><li>Participant Media Stream - Read <a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaStream">here</a> for more information</li></ul>                                               |
| streamConstraints | <ul><li>An object with two fields:<br>{audio: boolean, video: boolean}</li><li>Example:<br>{audio: false, video: true}. - that means a stream of participant consists only of video tracks</li></ul> |
| settings          | <ul><li>An object with two fields:<br>{mutedAudio: boolean, mutedVideo: boolean} - Those fields contain the state of the Audio and Video of the participant</li></ul>                                |

## Public method Summar <a href="#sessionerror-publicconstructorsummary" id="sessionerror-publicconstructorsummary"></a>

| Name                                                                                                                                                                                                                            | Description                                                                            | Parameters                                                                                                                                                          |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| enableAudio()                                                                                                                                                                                                                   |                                                                                        |                                                                                                                                                                     |
| disableAudio()                                                                                                                                                                                                                  |                                                                                        |                                                                                                                                                                     |
| enableVideo()                                                                                                                                                                                                                   |                                                                                        |                                                                                                                                                                     |
| disableVideo()                                                                                                                                                                                                                  |                                                                                        |                                                                                                                                                                     |
| isAudioEnabled()                                                                                                                                                                                                                |                                                                                        | <ul><li>Return boolean state of the local audio</li></ul>                                                                                                           |
| isVideoEnabled()                                                                                                                                                                                                                |                                                                                        | <ul><li>Return boolean state of the local video</li></ul>                                                                                                           |
| startScreenSharing(callback: Function)                                                                                                                                                                                          | <ul><li>The callback function will be invoked when screen sharing is started</li></ul> |                                                                                                                                                                     |
| stopScreenSharing(callback: Function)                                                                                                                                                                                           | <ul><li>The callback function will be invoked when screen sharing is stopped</li></ul> |                                                                                                                                                                     |
| toggleRemoteAudio(participantId: String)                                                                                                                                                                                        | <ul><li>Toggle remote <em>participantId</em> audio</li></ul>                           |                                                                                                                                                                     |
| toggleRemoteVideo(participantId: String)                                                                                                                                                                                        | <ul><li>Toggle remote <em>participantId</em> video</li></ul>                           |                                                                                                                                                                     |
| isRemoteAudioEnabled(participantId: String)                                                                                                                                                                                     |                                                                                        |                                                                                                                                                                     |
| isRemoteVideoEnabled(participantId: String)                                                                                                                                                                                     |                                                                                        |                                                                                                                                                                     |
| [setMediaQuality](/watch-together-sdk/sscale-confluence-api-references/sscale-confluence-web-sdk-reference/sscale-confluence-participant-websdk/setmediaquality) ({videoHeight: Number, videoWidth: Number, frameRate: Number}) | <ul><li>Apply constraints to video resolution and frame rate</li></ul>                 |                                                                                                                                                                     |
| publishRemoteParticipant(participantId)                                                                                                                                                                                         | <ul><li>Allow the remote participant to publish into the session</li></ul>             |                                                                                                                                                                     |
| disconnectRemoteParticipant(participantId, message)                                                                                                                                                                             | <ul><li>Disconnect the remote participant from the session</li></ul>                   | <ul><li>participantId - id of participant who will be disconnected from a session</li><li>message - optional parameter to provide a reason of kick action</li></ul> |


# setMediaQuality

| Methods                                                                                     | Description                                                                                                                                                                                                                                                                      | Paramet                                                                                              |
| ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| <p>setMediaQuality(<br>{videoHeight: Number, <br>videoWidth: Number, frameRate: Number}</p> | <p>Apply constraints to the <br>video resolution and frame rate.</p><ul><li>It is possible to invoke <br>the function with a <br>sub-set of the parameters.<br>Example:<code>WT.Participant</code><br><code>.setMediaQuality(</code><br><code>{frameRate: 18});</code></li></ul> | <p></p><ul><li>videoWidth - number</li><li>VideoHeight - number</li><li>frameRate - number</li></ul> |

### Browser Compatibility

| Browser       | Video Resolution                                                                  | Frame Rate |
| ------------- | --------------------------------------------------------------------------------- | ---------- |
| Chrome        | +                                                                                 | +          |
| Safari(macOS) | +                                                                                 | -          |
| Chrome mobile | +                                                                                 | +          |
| Safari iOS    | +                                                                                 | +          |
| Mozilla       | Note: if you changed width or height  - the other parameter changed automatically | +          |


# ParticipantListeners

## Public method Summary <a href="#sessionerror-publicconstructorsummary" id="sessionerror-publicconstructorsummary"></a>

| Methods                                                                                                                                           | Description                                                                                                                          | Parameters                                                                                                                                                          |
| ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <p>onParticipantJoin(fn)</p><p></p><p>const fn = (partricipant) => { <code>//do smth }</code></p>                                                 | The Event handler will be invoked when new participant joined to the Session but haven't started streaming yet                       |                                                                                                                                                                     |
| <p>onParticipantLeft(fn)</p><p></p><p><code>const fn = ({participantId}) => { //do smth }</code></p>                                              | The Event handler will be invoked when the participant leaves the Session                                                            | <ul><li>participantId <em>-</em> unique identification of the user</li></ul>                                                                                        |
| <p>onParticipantSpeaking(fn)</p><p></p><p><code>const fn = ({participantId}) => { //do smth }</code></p>                                          | The Event handler will be invoked when the participant starts speaking                                                               |                                                                                                                                                                     |
| <p>onParticipantStopSpeaking(fn)</p><p></p><p><code>const fn = ({participantId}) => { //do smth }</code></p>                                      | The Event handler will be invoked when the participant stops speaking                                                                |                                                                                                                                                                     |
| onOrganizerReceived(fn)                                                                                                                           | The Event handler will be invoked when the user receives the organizer previlegious.                                                 | <ul><li>Fn - custom callback function without any parameters</li></ul>                                                                                              |
| <p>onParticipantMediaStreamChanged(fn)<br><br><code>const fn = ({participantId, mediaState, mediaType}) => { //do smth }</code></p><p></p><p></p> | <p>The Event handler will be invoked when a participant mute/unmute himself(audio or video).<br>Params object with keys:<br><br></p> | <ul><li>participantId -participant id which was muted</li><li>mediaType - allowed values Audio/Video</li><li>mediaState - allowed values Enabled/Disabled</li></ul> |
| <p>onParticipantRejected(fn)<br><br><code>const fn = (participant, message) => { //do smth }</code></p>                                           | The Event handler will be invoked when the participant was kicked from the session                                                   | <ul><li>participant - Participant object (who initiated a kick action)</li><li>message -  optional parameter to provide a reason of kick action</li></ul>           |


# ErrorsListeners

## Public method Summary <a href="#sessionerror-publicconstructorsummary" id="sessionerror-publicconstructorsummary"></a>

| Methods                                                                                   | Parameters                      |
| ----------------------------------------------------------------------------------------- | ------------------------------- |
| <p>onSessionError(fn)</p><p><em>Event listener for handling session availability</em></p> | Fired when Session has an error |

## Session error codes <a href="#session-error-codes" id="session-error-codes"></a>

| Codes | Errors                                       | Description                                                                                                                                                                                                          |
| ----- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 206   | SESSION\_IS\_FULL\_ERROR\_CODE               | Exceeded limit of streaming participants in the room                                                                                                                                                                 |
| 204   | SESSION\_CANNOT\_BE\_CREATED\_ERROR\_CODE    |                                                                                                                                                                                                                      |
| 203   | SESSION\_CLOSED\_ERROR\_CODE                 |                                                                                                                                                                                                                      |
| 201   | SESSION\_GENERIC\_ERROR\_CODE                |                                                                                                                                                                                                                      |
| 107   | USER\_VIDEO\_PERMISSION\_DENIED\_ERROR\_CODE | Occurs when a user that was not granted permission for using web camera tries to join a session                                                                                                                      |
| 108   | USER\_AUDIO\_PERMISSION\_DENIED\_ERROR\_CODE | Occurs when a user that was not granted permission for using microphone tries to join a session                                                                                                                      |
| 106   | USER\_NOT\_AUTHORIZED\_ERROR\_CODE           | <ul><li>Attempting to send synchronization data while not having a synchronizer role</li><li>Invalid oldParticipantSecret during reconnection</li></ul>                                                              |
| 105   | USER\_NOT\_STREAMING\_ERROR\_CODE            | <ul><li>Attempting to start a stream with no video and audio</li><li>Attempting to send SDP response when there was no offer</li><li>Attempting to subscribe to a client that did not publish anything yet</li></ul> |
| 104   | EXISTING\_USER\_IN\_SESSION\_ERROR\_CODE     | A Participant tries to join a Session with a name that already exists                                                                                                                                                |
| 103   | USER\_CLOSED\_ERROR\_CODE                    | <ul><li>Source or target user has left the Session during the media subscription process</li><li>Target user has left the Session during SDP exchange</li></ul>                                                      |
| 102   | USER\_NOT\_FOUND\_ERROR\_CODE                | <ul><li>Attempting to execute request before joining the Session</li><li>Addressing client that is not in the Session</li></ul>                                                                                      |
| 101   | USER\_GENERIC\_ERROR\_CODE                   | <ul><li>Invalid participant type</li><li>Target client is not is not available while in the process of media (un)subscription</li><li>ICE candidates queue overflow</li></ul>                                        |

## Media error codes <a href="#media-error-codes" id="media-error-codes"></a>

| Codes | Errors                       | Description                                                                                                                                                                   |
| ----- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 303   | MEDIA\_ENDPOINT\_ERROR\_CODE | <ul><li>Already subscribed to media stream from target client</li><li>SDP exchange for this media stream is already in progress</li><li>Timeout during SDP exchange</li></ul> |

## Token error codes

| Codes | Errors                      | Description                                          |
| ----- | --------------------------- | ---------------------------------------------------- |
| 1100  | TOKEN\_EXPIRED              | <ul><li>Token expired</li></ul>                      |
| 1101  | TOKEN\_DATA\_IS\_NOT\_VALID | <ul><li>Token data missing required fields</li></ul> |


# ReconnectListeners

## Public Method Summary <a href="#public-method-summary" id="public-method-summary"></a>

| **Methods**                                              | **Description**                                                                                     | **Parameters** |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | -------------- |
| onLocalConnectionLost(fn)                                | The Event handler is invoked when the local participant loses network connection                    |                |
| onLocalConnectionResumed(fn)                             | The Event handler is invoked when the user acquired back the connection to the network              |                |
| onLocalParticipantReconnected(fn)                        | The Event handler is invoked when the user successfully reconnected to the session                  |                |
| onLocalParticipantReconnecting(fn)                       | The Event handler is invoked when the user is in the process of reconnecting                        |                |
| onRemoteParticipantConnectionLost((participantId) => {}) | The Event handler is invoked when the remote participant loses connection to the session            |                |
| onRemoteParticipantReconnecting((participantId) => {})   | The Event handler is invoked when the remote participant starts the reconnect process               |                |
| onRemoteParticipantReconnected((participantId) => {})    | The Event handler is invoked when the remote participant is successfully reconnected to the session |                |


# MediaDevices

The `mediaDevices` API that enables access to input devices such as cameras and microphones, as well as the ability to capture media streams from those devices.

## Public method summary

| **Method**                                  | **Description**                                       | **Parameters**                                                                                                                                                                                                                                                |
| ------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| getMediaDevices()                           | Returns the list of available for using media devices |                                                                                                                                                                                                                                                               |
| setAudioConstraints(deviceId)               | Selects the audio device for the session              |                                                                                                                                                                                                                                                               |
| setVideoConstraints(deviceId)               | Selects the video device for the session              |                                                                                                                                                                                                                                                               |
| doPreview(constraints)                      | Returns a preview stream for session                  |                                                                                                                                                                                                                                                               |
| changeMediaDevice(deviceId, type, selector) | Allows the user to change media device                | <ul><li>deviceId - id of selected device provided by getMediaDevices() method</li><li>type - type of device to change. Possible values are: \['camera', 'microphone', 'speaker']</li><li>querySelector for video element to replace a stream object</li></ul> |


# Cluster authentication service reference (CAS)

### Steaming Token (Access Token) <a href="#get-steaming-token" id="get-steaming-token"></a>

## \<url>/stream/token/v2/

<mark style="color:blue;">`GET`</mark> `Https://<url>/stream/token/v2/`

#### Path Parameters

| Name           | Type   | Description |
| -------------- | ------ | ----------- |
| /stream/token/ | string |             |

#### Headers

| Name            | Type   | Description         |
| --------------- | ------ | ------------------- |
| Auth-API-Secret | string | Customer api secret |
| Auth-API-Key    | string | Customer api key    |
| Accept          | string | application/json    |

{% tabs %}
{% tab title="200 " %}

```
{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyaWQiOiJlMDFkYmU0NTg5MzU0YmU3OTc3OTg0ZmU2NjIxMTgwOCIsInVuY3QiOiJiNjA2NGFlZjVkZTU0NWZiODM1...
}
```

{% endtab %}
{% endtabs %}

### Example - acquiring an Access Token <a href="#acquiring-an-access-token" id="acquiring-an-access-token"></a>

{% tabs %}
{% tab title="cUrl(Bash)" %}

```bash
curl -iL --request GET --url https://YOUR_CAS_URL/stream/token/v2/ --header 'auth-api-key: API_KEY'   --header 'auth-api-secret: API_SECRET'
```

{% endtab %}
{% endtabs %}

The `Access Token` is a JWT token - more about JWT you can read - [here](https://en.wikipedia.org/wiki/JSON_Web_Token).

A successful response will look like that:

```
{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...."
}
```

{% hint style="info" %}
You can get your API\_KEY and API\_SECRET in your private area, [here](https://media.sceenic.co/).
{% endhint %}


# Synchronization SDK overview

## Overview <a href="#overview" id="overview"></a>

The Sync SDK will allow you to create groups of people that are watching a video stream and synchronize the content the group is watching across all platforms.

To get the Web SDK login to your [private area](https://media.sceenic.co/) to receive the NPM package token, the API\_KEY, and API\_SECRET.

* [Get the sync SDK](https://media.sceenic.co/)
* [Sample code React](https://bitbucket.org/svmt/react-demo-sync/src/master/)
* [Sample code Kotlin](https://bitbucket.org/svmt/samplesyncwatchtogether-kotlin/src/master/)
* [Sample code Java](https://bitbucket.org/svmt/samplesyncwatchtogether-java/src/master/)
* [Sample code Swift](https://bitbucket.org/svmt/sample-synchsdk-ios/src/master/)

## The sync platform <a href="#the-synch-platform" id="the-synch-platform"></a>

To synchronize the content across all players in the group we connect to the players, extract the relevant time data, and manage it on a dedicated back-end.

The sync SDK’s architecture is a client-server architecture:

* Clients - Web SDK, iOS, and Android SDKs
* Server - Provides the synchronization logic and reporting service as to the Quality of Service

## System use case <a href="#system-use-case" id="system-use-case"></a>

The system use case is the following - Allow a group of Players to play the same video content in a synchronized manner for the entire group.

Groups can be as large as ten of thousands of Players or as small as two, which will depend on your specific use case.

This use case will fit well for:

* Sport matches
* Live concerts
* Live and breaking news

Or any other content you believe requires synchronization.

![](/files/-MXc-iazFoOydu9LR4_r)

**System use case - Synchronization**

### Benefits <a href="#benefits" id="benefits"></a>

#### Synchronization accuracy <a href="#synchronization-accuracy" id="synchronization-accuracy"></a>

Synchronization between the clients in the session of about 40-100 milliseconds.

#### Different synchronization algorithms <a href="#control-over-the-synchronization-algorithms" id="control-over-the-synchronization-algorithms"></a>

* Follow the leader - the first client that logs in the session will dictate the speed
* Average - calculates the average common timeline of the clients as the synchronization timeline
* Weighted average - calculates the average common timeline of the clients with respect to the platform they come from (Web, iOS, or Android) as the synchronization timeline
* Linear - all the clients will be synchronized independently with the server timeline

#### Reporting (In progress) <a href="#reporting" id="reporting"></a>

A reporting API access that lets you follow the synchronization accuracy of every group and every client in the system.

#### Player support <a href="#player-support" id="player-support"></a>

Any player can be used as long as it supports getting the absolute current position of the stream and changing playback rate.

Absolute current position for the stream can be obtained using various options, for example:

* Using EXT-X-*PROGRAM*-*DATE*-*TIME tag*
* Using ID3 tags
* Using initial PTS as an offset

To use any player you will need to extend our object model and implement all the required methods.

For more information have a look at the [Tutorials](/synchronization-sdk/tutorials) and [API References.](https://sync-api-doc.web.app/)

#### Messaging <a href="#messaging" id="messaging"></a>

As all the clients are connected through our system you will be able to send control messages of different types from client to client or broadcast messages.

This will allow you the following

* Have control over the various player and functionalities
* Create a chat between clients
* Create your own custom messaging protocol between clients

## Support <a href="#android-javaandkotlin-support" id="android-javaandkotlin-support"></a>

Need technical support? contact us at <Support@sceenic.co>.


# Tutorials


# Android


# Android - Java/Kotlin

## Overview <a href="#overview" id="overview"></a>

Follow this step-by-step tutorial to implement the video playback Synchronization SDK.

While the client-side SDK will take care of most of the functionality, in order to make this sample application work, you will need to use the API\_KEY provided to you.

Full code samples can be found here

* [Java sample application](https://bitbucket.org/svmt/samplesyncwatchtogether-java/src/master/)
* [Kotlin sample application](https://bitbucket.org/svmt/samplesyncwatchtogether-kotlin/src/master/)

## Requirements <a href="#requirements" id="requirements"></a>

To complete this guide successfully the following prerequisites are required:

* [Android Studio](https://developer.android.com/studio)
* A Sceenic account
* Access key for sync SDK

## Authentication <a href="#authentication" id="authentication"></a>

An `Access Token` is needed in order to allow a client to connect to a sync group.

{% hint style="danger" %}
**Note:** It is important that the client application does not request an Access Token directly from the backend. By doing that you risk exposing the API\_TOKEN and API\_SECRET.
{% endhint %}

### Acquiring an Access Token <a href="#acquiring-an-access-token" id="acquiring-an-access-token"></a>

{% tabs %}
{% tab title="cUrl(Bash)" %}

```bash
curl -iL --request GET --url https://YOUR_CAS_URL/sync/token --header 'auth-api-key: API_KEY'   --header 'auth-api-secret: API_SECRET'
```

{% endtab %}
{% endtabs %}

The `Access Token` is a JWT token - more about jwt you can read - [here](https://en.wikipedia.org/wiki/JSON_Web_Token).

A successful response will look like that:

```
{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...."
}
```

{% hint style="info" %}
You can get your API\_KEY and API\_SECRET in your private area, [here](https://media.sceenic.co/).
{% endhint %}

{% hint style="warning" %}
**Note:** Every Sync Token corresponds to one specific sync group only. To allow two different clients to connect to the same group, the clients need to use the same `Access Token`.
{% endhint %}

## &#x20;Create a project <a href="#create-a-project" id="create-a-project"></a>

* Open the Android Studio and select **New Project** from the **File** menu
* Select **Empty Activity** and click next
* Choose Java or Kotlin as your programming language for the project
* Configure your project's location, application, and package names
* Set the minimum **SDK** version of the application to **21** or higher

## Add the sync SDK library to your project <a href="#add-the-sync-sdk-library-to-your-project" id="add-the-sync-sdk-library-to-your-project"></a>

* Create (if it does not exist) in the project a **libs** directory under the **app** folder
* Add the file **wtsyncsdk\_v....aar** to the **libs** folder
* Edit the application's **build.gradle** file (in the app folder) with SDK

{% tabs %}
{% tab title="Build.gradle" %}

```kotlin
android {
    // Required
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    
    // Required
    packagingOptions {
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/license.txt'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/notice.txt'
        exclude 'META-INF/ASL2.0'
        exclude("META-INF/*.kotlin_module")
    }
}
dependencies {	
    implementation files('libs/wtsyncsdk_v....aar')	
    implementation 'androidx.core:core-ktx:1.7.0'
}
```

{% endtab %}
{% endtabs %}

### Permissions <a href="#permissions" id="permissions"></a>

* Add the following permissions to the **AndroidManifest.xml** file under the **app/src/main** folder in the project above the application tag.

```kotlin
<uses-permission android:name = "android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name = "android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name = "android.permission.INTERNET" />
```

## Instantiating the SDK <a href="#instantiating-the-sdk" id="instantiating-the-sdk"></a>

* SDK builder - Call SyncSdkBuilder to instantiate the SDK.

{% tabs %}
{% tab title="Java" %}

```java
mSyncSdk = new SyncSdk.SyncSdkBuilder()
      .accessToken(mAccessToken)      // mAccessToken - authorization token
      .name(mDisplayName)             // mDisplayName - username
      .syncListener(this)             // this - SyncListener object
      .build();
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk = SyncSdk.SyncSdkBuilder()     
      .accessToken(mAccessToken)      // mAccessToken - authorization token
      .name(mDisplayName)             // mDisplayName - username
      .syncListener(this)             // this - SyncListener object
      .build() 
```

{% endtab %}
{% endtabs %}

* Start synchronization

{% tabs %}
{% tab title="Java" %}

```java
if (mSyncSdk != null) {
    mSyncSdk.startSync();
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk?.startSync()
```

{% endtab %}
{% endtabs %}

* Stop synchronization

{% tabs %}
{% tab title="Java" %}

```java
if (mSyncSdk != null) {
    mSyncSdk.stopSync();
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk?.stopSync()
```

{% endtab %}
{% endtabs %}

* Group play video

{% tabs %}
{% tab title="Java" %}

```java
if (mSyncSdk != null) {
    mSyncSdk.groupPlay();
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk?.groupPlay()
```

{% endtab %}
{% endtabs %}

* Group pause video

{% tabs %}
{% tab title="Java" %}

```java
if (mSyncSdk != null) {
    mSyncSdk.groupPause();
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk?.groupPause()
```

{% endtab %}
{% endtabs %}

* Group seek video

{% tabs %}
{% tab title="Java" %}

```java
if (mSyncSdk != null) {
    mSyncSdk.groupSeek(position);
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk?.groupSeek(position)
```

{% endtab %}
{% endtabs %}

* Group seek success

{% tabs %}
{% tab title="Java" %}

```java
if (mSyncSdk != null) {
    mSyncSdk.groupSeekSuccess(position);
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
mSyncSdk?.groupSeekSuccess(position)
```

{% endtab %}
{% endtabs %}

## Managing synchronization logic <a href="#managing-synchronization-logic" id="managing-synchronization-logic"></a>

The synchronization logic is managed using callbacks, using them you will be able to create the interaction you need.

* In the sample application, you can see an example for implementing the necessary callbacks

{% tabs %}
{% tab title="Java" %}

```java
public class MainActivity extends AppCompatActivity implements SyncListener {
    ...

    @Override
    public void onClientList(@NotNull List<SyncClient> syncClients) {
        // remote sync client joined to the group, update ui using adapter
        // update ui with the list of connected clients in the same group
    }
    
    @Override
    public void onSetPlaybackRate(float rate) {
        // setting playback rate
    }

    @Override
    public void onPlaybackFromPosition(long position) {
       // playback from position
       // player.seekTo(position);
    }

    @Override
    public long onGetPlayerPosition() {
        // getting player position
        // Implement your logic to extract player position in miliseconds,
        // be sure that all clients connected to the same group extracts relevant
        // and from the same content timestamps(position)
        // return player.getCurrentPosition();
        return 0L;
    }

    @Override
    public float onGetPlaybackRate() {
        // getting playback rate
        // Implement your logic to extract player rate/speed
        // return player.speed;
        return 0f;
    }

    @Override
    public void onSyncInfo(@NotNull SyncInfo syncInfo) {
        // information about synchronization's accuracy and delta
        // syncInfo.accuracy 0.0% - 100.0%
        // syncInfo.delta starts from 0L and describes how big is delay
        // in miliseconds, can be negative value     
    }
    
    @Override
    public void onSyncGroupPlay() {
        // received an event onSyncGroupPlay()
        // play function should be called if its not playing already
        if (!player.isPlaying()) { 
            player.play();
        }   
    }
    
    @Override
    public void onSyncGroupPause() {
        // received an event onSyncGroupPause()
        // pause function should be called if its still playing
        if (player.isPlaying()) { 
            player.pause();
        }   
    }
    
    @Override
    public void onSyncGroupSeek(long position) {
        // received an event onSyncGroupSeek(long position) with position parameter
        // seek function should be called to the given position
        // works with VOD
        if (!player.isCurrentWindowLive()) { 
            player.seekTo(position);
        }
        // Client should send success seek request every time onSyncGroupSeek called
        if (mSyncSdk != null) {
            mSyncSdk.groupSeekSuccess(position);
        }
    }

    @Override
    public void onSyncDisconnected() {
        // client has been disconnected from the group. All connections 
        // will be closed. Clear ui and data
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
class MainActivity : AppCompatActivity(), SyncListener {
    ...

    override fun onClientList(syncClients: List<SyncClient>) {
        // remote client joined to the group, update ui using adapter
        // update ui with the list of connected clients in the same group
    }
    
    override fun onSetPlaybackRate(rate: Float) {
        // setting playback rate
    }
    
    override fun onPlaybackFromPosition(position: Long) {
       // playback from position
       // player.seekTo(position)
    }
    
    override fun onGetPlayerPosition(): Long {
        // getting player position
        // Implement your logic to extract player position in miliseconds,
        // be sure that all clients connected to the same group extracts relevant
        // and from the same content timestamps(position)
        // return player.getCurrentPosition()
        // Important! In case if content is Http Live Stream
        // Value of position shoulb be based on Program-Date-Time tag 
        return 0L
    }
    
     override fun onGetPlaybackRate(): Float {
        // getting playback rate
        // Implement your logic to extract player rate/speed
        // return player.speed
        return 0f
    }
    
    override fun onSyncInfo(syncInfo: SyncInfo)
         // information about synchronization's accuracy and delta
        // syncInfo.accuracy 0.0% - 100.0%
        // syncInfo.delta starts from 0L and describes how big is delay
        // in miliseconds, can be negative value   
    }
    
    
    override fun onSyncGroupPlay() {
        // received an event onSyncGroupPlay()
        // play function should be called
        player.play()
    }
        
    override fun onSyncGroupPause() {
        // received an event onSyncGroupPause()
        // pause function should be called
        player.pause()
    }
    
   override fun onSyncGroupSeek(position: Long) {
        // received an event onSyncGroupSeek(long position) with position parameter
        // seek function should be called to the given position
        // works with VOD
        if (!player.isCurrentWindowLive) { 
            player.seekTo(position)
        }
        // Client should send success seek request every time onSyncGroupSeek called
        mSyncSdk?.groupSeekSuccess(position)
    }
    
    override fun onSyncDisconnected() {
        // client has been disconnected from the group. All connections will be closed.
        // clear ui and data  
    }
```

{% endtab %}
{% endtabs %}

## Running the application <a href="#running-the-application" id="running-the-application"></a>

Once coding is finished, you should be able to run the application in the Android Studio emulator.

You can view the complete Watch Together sample application here

* [Java sample application](https://bitbucket.org/svmt/samplesyncwatchtogether-java/src/master/)
* [Kotlin sample application](https://bitbucket.org/svmt/samplesyncwatchtogether-kotlin/src/master/)

## Support <a href="#support" id="support"></a>

Need technical support? contact us at <Support@sceenic.co>.


# iOS


# iOS - Swift/Objective-c

## Overview <a href="#overview" id="overview"></a>

Follow this step-by-step tutorial to implement the video playback Synchronization SDK.

While the client-side SDK will take care of most of the functionality, in order to make this sample application work, you will need to use the API\_KEY provided to you.

* Full code samples can be found here&#x20;
  * [Swift code samples](https://bitbucket.org/svmt/synch-ios-swift/src/master/)
  * [Objective-c adapter code samples](https://bitbucket.org/svmt/sample-synchsdk-ios-objc/src/master/)

## Requirements <a href="#requirements" id="requirements"></a>

To complete this guide successfully the following prerequisites are required:

* A Sceenic account
* [Xcode](https://itunes.apple.com/ua/app/xcode/id497799835?mt=12)
* Access key for Synch SDK
* iOS version 11 or higher

## Authentication <a href="#authentication" id="authentication"></a>

An `Access Token` is needed in order to allow a client to connect to a sync group.

{% hint style="danger" %}
**Note:** It is important that the client application does not request an Access Token directly from the backend. By doing that you risk exposing the API\_TOKEN and API\_SECRET.
{% endhint %}

### Acquiring an Access Token <a href="#acquiring-an-access-token" id="acquiring-an-access-token"></a>

{% tabs %}
{% tab title="cUrl(Bash)" %}

```bash
curl -iL --request GET --url https://YOUR_CAS_URL/sync/token --header 'auth-api-key: API_KEY'   --header 'auth-api-secret: API_SECRET'
```

{% endtab %}
{% endtabs %}

The `Access Token` is a JWT token - more about jwt you can read - [here](https://en.wikipedia.org/wiki/JSON_Web_Token).

A successful response will look like that:

```
{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...."
}
```

{% hint style="info" %}
You can get your API\_KEY and API\_SECRET in your private area, [here](https://media.sceenic.co/).
{% endhint %}

{% hint style="warning" %}
**Note:** Every Sync Token corresponds to one specific sync group only. To allow two different clients to connect to the same group, the clients need to use the same `Access Token`.
{% endhint %}

## &#x20;Create a project <a href="#create-a-project" id="create-a-project"></a>

* Open Xcode and create a new project
* Choose a Single View application
* Configure your product organization, bundle, and team names
* Set the application location on your computer and press “create”

## Adding iOS SynchSDK library to the project

You can drag SynchSDK.framework to the project tree and use this library manually.

### Cocoapods <a href="#cocoapods" id="cocoapods"></a>

* Download library ([Private area](https://media.sceenic.co/)) package and unpack it
* Create a folder with the name "SynchSDK" at the root of the project
* Copy all files ("SynchSDK.framework","SynchSDK.podspec") to the folder
* You can use [Cocoapods](https://cocoapods.org/) to install SynchSDK by adding it to your Podfile\
  `pod 'SynchSDK', :path => './SynchSDK'`
* \[When using the Objective-c adapater] - do the following extra steps
  * Create a folder with the name "SynchSDK" at the root of the project
  * Copy all files ("SynchSDKAdapter.framework","SynchSDKAdapter.podspec") to the folder
  * You can use [Cocoapods](https://cocoapods.org/) to install SynchSDK by adding it to your Podfile\
    `pod 'SynchSDKAdapter', :path => './SynchSDKAdapter'`

## Instantiating the SDK

* Initialize SynchSDK object:

{% tabs %}
{% tab title="Swift" %}

```swift
let synchSDK = SyncSDK?

do {
    synchSDK = try SyncSDK(accessToken: token, userName: userName) //token - authorization token, username - name of user
    synchSDK.attachListener(self)// self - SynchListener object
} catch {
    print(error)// error is SynchTokenError
    //fallback logic
}
    
```

{% endtab %}

{% tab title="Objective-c adpater" %}

```objectivec
@interface YourViewController () <SynchListenerAdapter>
@property (nonatomic, strong) SynchSDKAdapter* synch;
end;

- (void)viewDidLoad {
    [super viewDidLoad];
    NSError* err = nil;
    _synch = [[SynchSDKAdapter alloc] initWithAccessToken: token username:@"userName" error: &err];
    if (err != NULL) {
        NSLog(@"%@", err);
        //fallbacklogic
    } 
    [_synch attachListener: self];
    
```

{% endtab %}
{% endtabs %}

* Start synchronization - To start using the SynchSDK object you will require a SynchSdk’s URL and a valid Access Token to be available before connecting.

{% tabs %}
{% tab title="Swift" %}

```swift
synchSDK.startSynchronize()
```

{% endtab %}

{% tab title="Objective-c adpater" %}

```objectivec
[synch startSynchronize];
```

{% endtab %}
{% endtabs %}

* Stop synchronization

{% tabs %}
{% tab title="Swift" %}

```swift
synchSDK.stopSynchronize()
```

{% endtab %}

{% tab title="Objective-c adpater" %}

```objectivec
[synch stopSynchronize];
```

{% endtab %}
{% endtabs %}

## Managing synchronization logic

To manage the synchronization logic we provided several callbacks and will allow you to customize the interactions you need.

* The SynchListener interface, which the StreamView implements in the sample application, will allow you to control the flow of logic of the Synchronization you are managing

{% tabs %}
{% tab title="Swift" %}

```swift

extension StreamView: SynchListener{
    func onClientList(clientList: [Client]) {
        // remote client joined to the group, update ui using adapter
    }
    
    func onSetPlaybackRate(rate: Float) {
        // setting playback rate
    }
    
    func onPlaybackFromPosition(position: Int, participantId: String) {
        // playback from position
    }
    
    func onGetPlayerPosition() -> Int {
        // getting player position
    }
    
    func onGetPlaybackRate() -> Float {
        // getting playback rate
    }
    
    func onSyncInfo(accuracy: Float, delta: Int) {
        // information about synchronization's accuracy and delta
    }
    func onResumePlay(participantId: String) { 
        player?.play()
    }
    
    func onPause(participantId: String) {
        player?.pause()
    }
    
}
```

{% endtab %}

{% tab title="Objective-c adapter" %}

```objectivec
- (void)onClientListWithClientList:(NSArray<ClientAdapter *> * _Nonnull)clientList {
    // remote client joined to the group, update ui using adapter
}

- (float)onGetPlaybackRate {
     // setting playback rate
}

- (NSInteger)onGetPlayerPosition {
        // getting player position
}

- (void)onPlaybackFromPositionWithPosition:(NSInteger)position participantId:(NSString*) participantId {
   // playback from position
}

- (void)onSetPlaybackRateWithRate:(float)rate {
     // getting playback rate
}

- (void)onSyncInfoWithAccuracy:(float)accuracy delta:(NSInteger)delta {
     // information about synchronization's accuracy and delta
}

- (void)onPauseWithParticipantId:(NSString *)participantId {
    [_player pause];
}

- (void)onResumePlayWithParticipantId:(NSString *)participantId{
    [_player play];
}
```

{% endtab %}
{% endtabs %}

## Support

Need technical support? contact us at <Support@sceenic.co>.


# Web


# Installing the NPM package

## Requirements <a href="#requirements" id="requirements"></a>

To use our Sync SDK npm package, an Access Token will be needed. If you don't have a token, please contact us at <Support@sceenic.co>.

## What is the Access Token? Why do you need it? <a href="#what-is-the-access-token-why-do-you-need-it" id="what-is-the-access-token-why-do-you-need-it"></a>

Sync SDK is a private package that is hosted on the npm registry. Only authorized users can access it. When you receive access to the SDK we will generate an authentication token for you. An authentication token is a hexadecimal string that gives you access to SDK.

## Before installation <a href="#before-installation" id="before-installation"></a>

In your project root folder create a *.npmrc* file. This file will set the specific npm config values for this project. Insert the code snippet below to the .npmrc file. Replace `YOUR_ACCESS_TOKEN` with the token that was provided.

```typescript
//registry.npmjs.org/:_authToken=YOUR_ACCESS_TOKEN
```

## Installation <a href="#installation" id="installation"></a>

```typescript
npm i @sscale/syncsdk
```

## Documentation <a href="#documentation" id="documentation"></a>

* [Code samples](https://bitbucket.org/svmt/react-demo-sync/src/master/)
* [API References](https://sync-api-doc.web.app/)

## Usage <a href="#usage" id="usage"></a>

```typescript
import { SyncSdk } from '@sscale/syncsdk';
```


# Web - TypeScript/React

## Overview <a href="#overview" id="overview"></a>

Follow this step-by-step tutorial to implement the video playback Synchronization SDK.

While the client-side SDK will take care of most of the functionality, in order to make this sample application work, you will need to use the API\_KEY provided to you.

[Code samples](https://bitbucket.org/svmt/react-demo-sync/src/master)\
[Demo application](https://sceenicsyncdemo.web.app/)\
[API Documentation](https://sync-api-doc.web.app/)

## Requirements <a href="#requirements" id="requirements"></a>

* A Sceenic account
* Code Editor( [WebStorm](https://www.jetbrains.com/webstorm/download/), [VS Code](https://code.visualstudio.com/download))
* Access key for sync SDK.
* [Node >= 8.10 and npm >= 5.6](https://nodejs.org/en/)

## Authentication <a href="#authentication" id="authentication"></a>

An `Access Token` is needed in order to allow a client to connect to a sync group.

{% hint style="danger" %}
**Note:** It is important that the client application does not request an Access Token directly from the frontend. By doing that you risk exposing the API\_TOKEN and API\_SECRET.
{% endhint %}

### Acquiring an Access Token <a href="#acquiring-an-access-token" id="acquiring-an-access-token"></a>

{% tabs %}
{% tab title="cUrl(Bash)" %}

```bash
curl -iL --request GET --url https://YOUR_CAS_URL/sync/token --header 'auth-api-key: API_KEY'   --header 'auth-api-secret: API_SECRET'
```

{% endtab %}
{% endtabs %}

The `Access Token` is a JWT token - more about jwt you can read - [here](https://en.wikipedia.org/wiki/JSON_Web_Token).

A successful response will look like that:

```
{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...."
}
```

{% hint style="info" %}
You can get your API\_KEY and API\_SECRET in your private area, [here](https://media.sceenic.co/).
{% endhint %}

{% hint style="warning" %}
**Note:** Every Sync Token corresponds to one specific sync group only. To allow two different clients to connect to the same group, the clients need to use the same `Access Token`.
{% endhint %}

### Going to production

When moving from the Sandbox environment to production you will need to implement your own authentication server. \
This server will supply the various clients (Web, Android, and iOS) with a valid Access Token so that they can use the service.

For that you will need:

* API\_KEY, and API\_SECRET - can be retrieved in your private area once you [login ](https://media.sceenic.co/)
* Your own working authentication server

## Create a project <a href="#create-a-project" id="create-a-project"></a>

To create a project we recommended using the following stack: React + TypeScript + SyncSDK.

* Create an empty React + Typescript project

```
npx create-react-app my-app --template typescript  
```

## Add the sync SDK library to your project <a href="#add-the-sync-sdk-library-to-your-project" id="add-the-sync-sdk-library-to-your-project"></a>

* Create ***.npmrc*** file. In the project’s root folder
* Put your `Access Token` inside `//registry.npmjs.org/:_authToken=YOUR_ACCESS_TOKEN`.
* Install Sync SDK via `npm i @sscale/syncsdk`.

For more information on NPM installation look [here](/synchronization-sdk/tutorials/web/web-sdk-npm-package).

## Basic setup <a href="#sample-application-ui" id="sample-application-ui"></a>

* Add the following import

```javascript
import { SyncSdk, PlayerDecorator } from '@sscale/syncsdk';
```

* Create player instance (it is possible to use a simple HTML player)

```markup
<video width="960" height="540" id='player'></video>
```

* Create Sync SDK instance

```typescript
const syncInstance = new SyncSDK()
```

* Connect to group or create new

```javascript
 syncInstance.createGroup(JWT_ACCESS_TOKEN, clientName)
             .then(() => {})
             .catch(e => {});
```

* Implement player decorator by extending `PlayerDecorator` and implementing required methods. `PlayerDecorator` example implementation for different players can be found [here](https://bitbucket.org/svmt/react-demo-sync/src/master/src/decorators). Check API of `PlayerDecorator`[ here](https://sync-api-doc.web.app/classes/PlayerDecorator.html).

```javascript
class HtmlVideoClient extends PlayerDecorator {
    public isSeekable(): boolean;
    public play(): void;
    public pause(): void;
    public mute(): void;
    public unmute(): void;
    public isPlaying(): boolean;
    public getCurrentPosition(): number;
    public getPlaybackRate(): number;
    public changePlaybackRate(rate: number): void;
    public fastSeekToPosition(position: number): void;
    public setVolume(volume: number): void;
}
```

* Connect HTML5 video client to the SDK

```javascript
const video = document.getElementById('player');
const client = new HtmlVideoClient(video);
syncInstance.addPlayerClient(client);
```

* Enable/disable synchronization

```typescript
syncInstance.startSynchronize()
            .then(() => {})
            .catch(e => {});
```

```typescript
syncInstance.stopSynchronize()
```

In addition, have a look at the extra features available for you:

* Chat
* Remote control
* Sync process visualization

Have a look at the [demo application ](https://sceenicsyncdemo.web.app/)to see all the features and API of sync instance [here](https://sync-api-doc.web.app/classes/SyncSDK.html).

## Support <a href="#support" id="support"></a>

Need technical support? contact us at <Support@sceenic.co>.


# v2.0 Migration Guide

* Please check the updated [API Documentation](https://sync-api-doc.web.app/) for more details.
* Have a look at the SyncSDK version 2 implentation for reference [here](https://bitbucket.org/svmt/react-demo-sync/src/master/).

#### Improved Player decorator usage

SyncSDK now doesn't have any player-specific logic inside to allow for better control. `PlayerDecorator` example implementation for different players can be found [here](https://bitbucket.org/svmt/react-demo-sync/src/master/src/decorators).&#x20;

* `startSynchronize()` method of SyncSDK no longer accepts `useProgramDateTime` parameter
* `addPlayer()` method of SyncSDK was renamed to `addPlayerClient()`
* `addPlayerClient()` method of SyncSDK no longer accepts `clientType` parameter
* `PlayerDecorator`'s constructor now accepts player instance as a parameter which is stored in `this.player` for further usage by other methods
* `PlayerDecorator` no longer has `setPlayerInstance` method, player instance should now be passed in `PlayerDecorator`'s constructor
* `PlayerType, HtmlVideoPlayer, YoutubePlayer, Player` are no longer exported and can't be used
* `getOffset()` and `setOffset()` SyncSDK methods are no longer available

#### Improved error handling

* `createGroup()` and `startSynchronize()` methods of SyncSDK now return Promise which can be rejected with [SyncError](https://sync-api-doc.web.app/classes/SyncError.html)

#### Better synchronization

* `getPlaybackRate(`) method should now be implemented by `PlayerDecorator` subclasses and return current playback rate

#### Improved group control

* `unmute()` method should now be implemented by `PlayerDecorator` subclasses and unmute player when called
* `unmuteGroup()` method is now available in SyncSDK
* `setGroupPosition()` method of SyncSDK now accepts optional `delta` parameter. The parameter is in milliseconds and will be applied to the position returned by `PlayerDecorator.getCurrentPosition()` before sending to other clients

#### Unification of measurement units

SyncSDK now uses milliseconds for playback position in all methods.

* `getCurrentPosition()` method of `PlayerDecorator` should now return the position in milliseconds
* `fastSeekToPosition()` method of `PlayerDecorator` should now accept the position in milliseconds


# API references


# Android reference


# SynchSDK

## Nested classes <a href="#nested-classes" id="nested-classes"></a>

| Classes                                                  |
| -------------------------------------------------------- |
| SyncSdkBuilder - creates an instance of the SyncSdk      |
| SyncListener - an interface containing the API callbacks |

## Public Constructor Summary

| Type           | Methods                   | Parameters                                                 |
| -------------- | ------------------------- | ---------------------------------------------------------- |
| SyncSdkBuilder | accessToken(mAccessToken) | <ul><li>mAccessToken - authorization token;</li></ul>      |
| SyncSdkBuilder | name(mDisplayName)        | <ul><li>mDisplayName - username;</li></ul>                 |
| SyncSdkBuilder | syncListener(this)        | <ul><li>this - interface to syncSdk's callbacks;</li></ul> |
| SyncSdkBuilder | build()                   | <ul><li>initialize SyncSdk object</li></ul>                |

## Public Method Summary <a href="#public-method-summary" id="public-method-summary"></a>

| Type | Methods                                                                                                                                           | Parameters                                         |
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| void | startSync() - Start synchronization using the SyncSdk object requires a SyncSdk’s URL and a valid Access Token to be available before connecting. |                                                    |
| void | stopSync()                                                                                                                                        |                                                    |
| void | groupPlay() - start player for all members group using the SyncSdk object                                                                         |                                                    |
| void | groupPause() - pause player for all members group using the SyncSdk object                                                                        |                                                    |
| void | groupSeek(Long position) - seek player’s video for all members group using the SyncSdk object                                                     | <p></p><ul><li>position - position video</li></ul> |
| void | groupSeekSuccess(Long position) - confirmation about seeking player's video using the SyncSdk object                                              | <p></p><ul><li>position - position video</li></ul> |


# SynchListener

The Interface SyncListener invokes all the events from the SyncSdk.

The SyncListener is created together with the SyncSdk instance.

## Interface Callbacks Summary <a href="#interface-callbacks-summary" id="interface-callbacks-summary"></a>

| Type  | Methods                                                                                            | Parameters                                                                                                                                                                     |
| ----- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| void  | onClientList(syncClients: List\<SyncClient>) - List of all clients in the synchronization group    | <ul><li>syncClients- The list of connected clients in the group</li><li>SyncClient - data class. It contains id, name, isLeader and isLocal properties of the client</li></ul> |
| void  | onSetPlaybackRate(rate: Float) - Set playback rate                                                 | <ul><li>rate - Playback rate</li></ul>                                                                                                                                         |
| void  | onPlaybackFromPosition(psition: Long) - Playback from position, similar to seek to the position    | <ul><li>position - The value is in ms. The point from which the Player should start to play</li></ul>                                                                          |
| Long  | onGetPlayerPosition(): Long - Get player position                                                  | <ul><li>position - The value is in ms. The actual Player current position, must be implemented and return a value</li></ul>                                                    |
| Float | onGetPlaybackRate(): Float - Get playback rate                                                     | <ul><li>rate - The actual Player rate/speed. Must be implemented and return a value</li></ul>                                                                                  |
| void  | onSyncInfo(syncInfo: SyncInfo) - Synchronization infomration about delta and accuracy              | <ul><li>syncInfo - The Object contains the synchronization’s delta and accuracy information</li></ul>                                                                          |
| void  | onDisconnected() - The client has been disconnected from the group. All connections will be closed |                                                                                                                                                                                |


# iOS reference


# SynchSDK

## Nested classes <a href="#nested-classes" id="nested-classes"></a>

| Classes                                                   |
| --------------------------------------------------------- |
| SynchSDK - creates an instance of the SynchSDK            |
| SynchListener - an interface containing the API callbacks |

## Public Method Summary <a href="#public-method-summary" id="public-method-summary"></a>

| Type     | Methods                                                 | Parameters                                                                                                                       |
| -------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| SynchSDK | **init**(accessToken: String)                           | <ul><li>accessToken - authentication token</li></ul>                                                                             |
| void     | createGroup(**\_** groupId: String, clientName: String) | <ul><li><p>groupId - group identificator</p><ul><li>Max length is 255 chars</li></ul></li><li>clientName - client name</li></ul> |
| void     | attachListener(**\_** listner: SyncListener)            | <ul><li>listner - syncSdk's callbacks</li></ul>                                                                                  |
| void     | startSynchronize()                                      |                                                                                                                                  |
| void     | stopSynchronize()                                       |                                                                                                                                  |
| void     | playerSeek(position: Int)                               | <p></p><ul><li>position: value should be in milliseconds</li></ul>                                                               |
| void     | playerPause()                                           |                                                                                                                                  |
| void     | playerPlay()                                            |                                                                                                                                  |


# SynchListener

Interface SynchListener monitors all events from the SynchSdk.

The SynchListener is created together with the SynchSdk instance.

## Public Method Summary <a href="#public-method-summary" id="public-method-summary"></a>

| Type  | Methods                                                                                        | Parameters                                                                                                                                                                                     |
| ----- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| void  | onClientList(clientList: \[Client]) - List of all participants in the synchronization group    | <ul><li>clientList - The list of connected clients in the group</li></ul>                                                                                                                      |
| void  | onSetPlaybackRate(rate: Float) - Set playback rate                                             | <ul><li>rate - Playback rate</li></ul>                                                                                                                                                         |
| void  | onPlaybackFromPosition(position: Int)- Playback from position, similar to seek to the position | <ul><li><p>position - Recieve the value for the groupSeek functionality.</p><ul><li>The value is in ms and represents the point from which the Player should start to play</li></ul></li></ul> |
| Int   | onGetPlayerPosition() -> Int - Get player position                                             | <ul><li>position - The value is in ms. The actual Player current position, must be implemented and return a value</li></ul>                                                                    |
| Float | onGetPlaybackRate() -> Float - Get playback rate                                               | <ul><li>The actual Player rate/speed. Must be implemented and return a value</li></ul>                                                                                                         |
| void  | onSyncInfo(accuracy: Float, delta: Int) - Synchronization information about delta and accuracy | <ul><li>accuracy - synchronization accuracy</li><li>delta - synchronization delta</li></ul>                                                                                                    |
| void  | onPause()                                                                                      | receive group pause event                                                                                                                                                                      |
| void  | onResumePlay()                                                                                 | receive group play event                                                                                                                                                                       |

### Client model description

| Type   | Field  | Description                                   |
| ------ | ------ | --------------------------------------------- |
| String | name   | clients name                                  |
| String | id     | clients id                                    |
| Bool   | leader | abstract flag if client is leader/syncronizer |
| Bool   | local  | if client from client list is a local client  |


# Celebrity SDK overview

## Overview <a href="#watchtogetheroverview-overview" id="watchtogetheroverview-overview"></a>

Celebrity SDK works together with Watch Together SDK and will allow you to connect celebrities with fans in video chats by controlling access of a celebrity to your video-chat as a participant, as well as by viewing and connecting to allowed sessions as a celebrity.

To register for our service please contact us at <Support@sceenic.co>.

If you already have credentials to the private area, login and retrieve your NPM\_TOKEN from [Private area](https://media.sceenic.co/).

### Participant

As a participant of Watch Together video-chat you will be able to:

* invite a celebrity to the session
* check if a celebrity is already invited to the session
* revoke the celebrity invitation to the session

### Celebrity

A celebrity will be able to:

* get a list of available sessions
* connect to the sessions


# Tutorials


# Web


# Installing the NPM package

## Requirements <a href="#requirements" id="requirements"></a>

To use our Celebrity SDK npm package, an Access Token will be needed. Access Token is available in [Private area](https://media.sceenic.co/). If you don't have access to the private area, please contact us at <Support@sceenic.co>.

## What is the Access Token? Why do you need it? <a href="#what-is-the-access-token-why-do-you-need-it" id="what-is-the-access-token-why-do-you-need-it"></a>

Celebrity SDK is a private package that is hosted on the npm registry. Only authorized users can access it. When you receive access to the SDK we will generate an authentication token for you. An authentication token is a hexadecimal string that gives you access to SDK.

## Before installation <a href="#before-installation" id="before-installation"></a>

In your project root folder create a *.npmrc* file. This file will set the specific npm config values for this project. Insert the code snippet below to the .npmrc file. Replace `YOUR_ACCESS_TOKEN` with the token that was provided.

```typescript
//registry.npmjs.org/:_authToken=YOUR_ACCESS_TOKEN
```

## Installation <a href="#installation" id="installation"></a>

```typescript
npm i @sscale/celebritysdk
```

## Documentation <a href="#documentation" id="documentation"></a>

* [API References](https://celebrity-api-doc.web.app/)

## Usage <a href="#usage" id="usage"></a>

```typescript
import { ParticipantAPI, CelebrityAPI } from '@sscale/celebritysdk';
```


# Web - TypeScript/React

Follow this step-by-step tutorial on how to use CelebritySDK.

[API Documentation](https://celebrity-api-doc.web.app/)

## Requirements <a href="#requirements" id="requirements"></a>

* A Sceenic account
* Code Editor( [WebStorm](https://www.jetbrains.com/webstorm/download/), [VS Code](https://code.visualstudio.com/download))
* NPM token for Celebrity SDK
* [Node >= 8.10 and npm >= 5.6](https://nodejs.org/en/)

## Create a project <a href="#create-a-project" id="create-a-project"></a>

To create a project we recommended using the following stack: React + TypeScript.

* Create an empty React + Typescript project

```
npx create-react-app my-app --template typescript  
```

## Add Celebrity SDK library to your project <a href="#add-the-sync-sdk-library-to-your-project" id="add-the-sync-sdk-library-to-your-project"></a>

* Create ***.npmrc*** file. In the project’s root folder
* Put your `Access Token` inside `//registry.npmjs.org/:_authToken=YOUR_ACCESS_TOKEN`.
* Install Celebrity SDK via `npm i @sscale/celebritysdk`.

For more information on NPM installation look [here](/celebrity-sdk/tutorials/web/installing-the-npm-package).

## Basic usage

### Participant

* Add the following import:

```
 import { ParticipantAPI } from '@sscale/celebritysdk';
```

* Create participant API instance:

```
const participantAPI = new ParticipantAPI();
```

* Pass Watch Together access token ([reference](/watch-together-sdk/sscale-confluence-api-references/cluster-authentication-service-reference-cas-2)) to participant API instance:

```
participantAPI.setToken(WT_TOKEN);
```

* Invite/check/uninvite celebrity to/from your live Watch Together session created using WT\_TOKEN:

```
participantAPI.inviteCelebrity({})           
              .then(() => {})           
              .catch(e => {});
              
participantAPI.isCelebrityInvited()           
              .then((isInvited) => {})           
              .catch(e => {});

participantAPI.uninviteCelebrity()
              .then(() => {})           
              .catch(e => {});
```

### Celebrity

* Add the following import:

```
import { CelebrityAPI } from '@sscale/celebritysdk';
```

* Create celebrity API instance:

```
const celebrityAPI = new CelebrityAPI();
```

* Login with username/password provided by Sceenic:

```
celebrityAPI.login(username, password)            
            .then(() => {})            
            .catch(e => {});
```

* Subscribe to the list of available video/audio chat sessions:

```
celebrityAPI.subscribe(    
    ({total, sessions}) => {},    
    (e) => {}
);
```

* Unsubscribe/logout:

```
celebrityAPI.unsubscribe();

celebrityAPI.logout();
```

\
**Have a look at the API Documentation** [**here**](https://celebrity-api-doc.web.app/)**.**

## **Support**

Need technical support? Contact us at <Support@sceenic.co>.


# API References


# Chat SDK overview

## Overview <a href="#watchtogetheroverview-overview" id="watchtogetheroverview-overview"></a>

Chat SDK will allow you to create a messenger for your users without the need to implement the server and client logic. The only thing you will spend time on is how to create your UI as beautiful as possible as you can leave all the complicated stuff to us.&#x20;

To register for our service please contact us at <Support@sceenic.co>.

If you already have credentials to the private area, login and retrieve your NPM\_TOKEN from [Private area](https://media.sceenic.co/).

### Features

* usernames
* rooms
* room names
* invite
* kick
* leave
* messages
* emojis
* history
* read status
* and the list will only grow\...

### [Demo](https://sceenic-chat-demo.web.app/)


# Tutorials




---

[Next Page](/llms-full.txt/1)

