{"id":27657072,"url":"https://github.com/sendbird/sendbird-calls-sdk-react-native","last_synced_at":"2025-08-24T18:39:23.996Z","repository":{"id":207721345,"uuid":"447100916","full_name":"sendbird/sendbird-calls-sdk-react-native","owner":"sendbird","description":"Sendbird Calls SDK for React-Native","archived":false,"fork":false,"pushed_at":"2025-07-16T01:26:08.000Z","size":4092,"stargazers_count":2,"open_issues_count":0,"forks_count":3,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-08-23T23:06:53.521Z","etag":null,"topics":["audio","callkit","calls","conference","javascript","react-native","reactnative","reactnativecalls","sendbird","typescript","video","voip"],"latest_commit_sha":null,"homepage":"https://sendbird.com/docs/calls/sdk/v1/react-native/overview","language":"Kotlin","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sendbird.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-01-12T06:21:54.000Z","updated_at":"2025-07-16T01:26:11.000Z","dependencies_parsed_at":null,"dependency_job_id":"e14228c8-afbe-4d78-afdc-0ac78aff57dd","html_url":"https://github.com/sendbird/sendbird-calls-sdk-react-native","commit_stats":null,"previous_names":["sendbird/sendbird-calls-sdk-react-native"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/sendbird/sendbird-calls-sdk-react-native","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-calls-sdk-react-native","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-calls-sdk-react-native/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-calls-sdk-react-native/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-calls-sdk-react-native/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sendbird","download_url":"https://codeload.github.com/sendbird/sendbird-calls-sdk-react-native/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sendbird%2Fsendbird-calls-sdk-react-native/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271923706,"owners_count":24844239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-24T02:00:11.135Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","callkit","calls","conference","javascript","react-native","reactnative","reactnativecalls","sendbird","typescript","video","voip"],"created_at":"2025-04-24T06:53:56.900Z","updated_at":"2025-08-24T18:39:23.929Z","avatar_url":"https://github.com/sendbird.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# [Sendbird](https://sendbird.com) Calls SDK for React-Native\n\n\u003cspan\u003e\n\u003cimg src=\"https://img.shields.io/badge/React Native-0.60+-black?logo=react\" alt=\"RN 0.60+\" \u003e\n\u003cimg src=\"https://img.shields.io/badge/iOS-11+-lightgray?logo=apple\" alt=\"iOS 12.0+\" \u003e\n\u003cimg src=\"https://img.shields.io/badge/Android-21+-green?logo=android\" alt=\"Android 21.0+\" /\u003e\n\u003c/span\u003e\n\n## Table of contents\n\n1. [Introduction](#introduction)\n1. [Before getting started](#before-getting-started)\n1. [Getting started](#getting-started)\n1. [Configuring the application for the SDK](#configuring-the-application-for-the-sdk)\n1. [Making your first call](#making-your-first-call)\n1. [Implementation direct call guide](#implementation-direct-call-guide)\n1. [Making your first group call](#making-your-first-group-call)\n1. [Implementation group call guide](#implementation-group-call-guide)\n1. [Appendix](#appendix)\n\n\u003cbr /\u003e\n\n## Introduction\n\n**Sendbird Calls** is the latest addition to our product portfolio. It enables real-time calls between users within a Sendbird application. SDKs are provided for iOS, Android, and JavaScript. Using any one of these, developers can quickly integrate voice and video call functions into their own client apps, allowing users to make and receive web-based real-time voice and video calls on the Sendbird platform.\n\n\u003e If you need any help in resolving any issues or have questions, please visit [our community](https://community.sendbird.com)\n\n### How it works\n\nSendbird Calls SDK for React-Native provides a module to make and receive voice and video calls. **Direct calls** in the SDK refers to one-to-one calls. To make a direct voice or video call, the caller specifies the user ID of the intended callee, and dials. Upon dialing, all of the callee’s authenticated devices will receive notifications for an incoming call. The callee then can choose to accept the call from any one of the devices. When the call is accepted, a connection is established between the devices of the caller and the callee. This marks the start of a direct call.\n\n**Group calls** in the SDK refers to many-to-many calls. One person creates a room, and multiple people can join the room by using the room ID of the created room.\n\nIn both **Direct calls** and **Group calls**, participants can control audio devices like mute or unmute audio and video devices such as toggle between the front and rear camera. With this, they're possible to participate in the calls using both or only one of them.\n\nThe [Sendbird Dashboard](https://dashboard.sendbird.com/auth/signin) displays **Direct calls** logs in the Calls menu for dashboard owners and admins to review. And you can see the created **Group calls** room information and the entering and exiting times of users who participated in the room.\n\n### More about Sendbird Calls SDK for React-Native\n\nFind out more about Sendbird Calls for React-Native on Calls SDK for React-Native doc.\n\n\u003cbr /\u003e\n\n## Before getting started\n\nThis section shows the prerequisites you need to check to use Sendbird Calls SDK for React-Native.\n\n### Requirements\n\n- React-Native 0.60 or higher\n- iOS 12.0 or higher\n- Android 5.0 (API level 21) or higher\n\n### SDK dependencies\n\n- [Sendbird Calls SDK for iOS](https://github.com/sendbird/sendbird-calls-ios), which can be integrated by `CocoaPods`\n- [Sendbird Calls SDK for Android](https://github.com/sendbird/sendbird-calls-android), which can be integrated by `Gradle`\n\n\u003cbr /\u003e\n\n## Getting started\n\nThis section gives you information you need to get started with Sendbird Calls SDK for React-Native.\n\n### Install Calls SDK\n\n### React-Native\n\n```shell\nnpm i @sendbird/calls-react-native\nnpx pod-install\n```\n\n\u003cbr /\u003e\n\n## Configuring the application for the SDK\n\n## iOS\n\n### Background Mode\n\nTo support background operation, VoIP-enabled apps must have `Background Mode` enabled in the **Xcode Project** \u003e **Signing \u0026 Capabilities** pane. Select the checkbox for **Voice over IP**.\n\nTo receive push notifications, the app also must have **Push Notifications** enabled in the **Xcode Project** \u003e **Signing \u0026 Capabilities** pane.\n\n\u003e For more information about VoIP push notification and PushKit, see Apple's [CallKit](https://developer.apple.com/documentation/callkit) and [PushKit](https://developer.apple.com/documentation/pushkit)\n\n### Configure the app’s Info.plist File\n\niOS requires that apps display authorization message to grant the app access to the camera and microphone.\n\n- Microphone-enabled apps must include the [NSMicrophoneUsageDescription](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW25) key in the app’s `Info.plist` file.\n- Camera-enabled apps must include the [NSCameraUsageDescription](https://developer.apple.com/documentation/bundleresources/information_property_list/nscamerausagedescription) key in the app’s `Info.plist` file.\n\n## Android\n\n### (Optional) Configure ProGuard to shrink code and resources\n\nWhen you build your APK with `minifyEnabled true`, add the following line to the module's ProGuard rules file.\n\n```\n# SendBird Calls SDK\n-keep class com.sendbird.calls.** { *; }\n-keep class org.webrtc.** { *; }\n-dontwarn org.webrtc.**\n-keepattributes InnerClasses\n```\n\n\u003cbr /\u003e\n\n## Getting Permissions\n\nThe SDK requires system permissions. The following permissions allow the SDK to access the microphone and use audio.\n\n- Camera\n- Microphone\n- Bluetooth (Android)\n\nWe recommend [`react-native-permissions`](https://github.com/zoontek/react-native-permissions) library\n\n```ts\nimport Permissions, { PERMISSIONS } from 'react-native-permissions';\n\nconst CALL_PERMISSIONS = Platform.select({\n  android: [PERMISSIONS.ANDROID.CAMERA, PERMISSIONS.ANDROID.RECORD_AUDIO, PERMISSIONS.ANDROID.BLUETOOTH_CONNECT],\n  ios: [PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.MICROPHONE],\n  default: [],\n});\n\nconst result = await Permissions.requestMultiple(CALL_PERMISSIONS);\n```\n\n## Making your first direct call\n\nFollow the step-by-step instructions below to authenticate and make your first direct call.\n\n### Step 1: Initialize the SendbirdCall instance in a client app\n\nAs shown below, the `SendbirdCalls` instance must be initiated when a client app is launched.\nInitialize the `SendbirdCalls` instance with the `APP_ID` of the Sendbird application you would like to use to make a call.\n\n```ts\nimport { SendbirdCalls } from '@sendbird/calls-react-native';\n\nSendbirdCalls.initialize(APP_ID);\n```\n\n\u003e Note: If another initialization with another `APP_ID` takes place, all existing data in the app will be deleted and the `SendbirdCalls` instance will be initialized with the new `APP_ID`.\n\n### Step 2: Authenticate a user and register a push token\n\nIn order to make and receive calls, authenticate the user with SendBird server with the `SendbirdCalls.authenticate()` method and **register a push token** to Sendbird.\n\n#### iOS\n\nRegister a VoIP push token by using the `SendbirdCalls.ios_registerVoIPPushToken()` method after authentication has completed.\nVoIP Push Notification will also enable receiving calls even when the app is in the background or terminated state.\nA valid APNS certificate also needs to be registered on the [Sendbird Dashboard](https://dashboard.sendbird.com/auth/signin): **Application** \u003e **Settings** \u003e **Notifications** \u003e **Add certificate**.\nFor more details on registering push tokens, refer to Calls SDK for React-Native doc.\n\n\u003e **NOTE**: In order to receive incoming calls to a user's device, you must implement either VoIP notifications or remote notifications.\n\u003e If you want to register a APNS token, you can register APNS token by using `SendbirdCalls.registerPushToken()` method\n\u003e\n\u003e refer to Calls SDK for iOS doc.\n\n#### Android\n\nRegister a FCM push token by using the `SendbirdCalls.registerPushToken()` method after authentication has completed.\nPush Notification will also enable receiving calls even when the app is in the background or closed entirely.\n\n```ts\nimport { SendbirdCalls } from '@sendbird/calls-react-native';\nimport RNVoipPushNotification from 'react-native-voip-push-notification';\nimport messaging from '@react-native-firebase/messaging';\n\n// Authenticate\nSendbirdCalls.authenticate({\n    userId: USER_ID,\n    accessToken: ACCESS_TOKEN,\n})\n    .then(user =\u003e {\n        // The user has been authenticated successfully\n    })\n    .catch(error =\u003e {\n        // error\n    })\n\n// Update FCM push token\nif (Platform.OS === 'android') {\n    const fcmToken = await messaging().getToken();\n    await SendbirdCalls.registerPushToken(fcmToken);\n    // The FCM Push Token has been registered successfully\n}\n\n// Update VoIP push token\nif (Platform.OS === 'ios') {\n    RNVoipPushNotification.addEventListener('register', async (voipToken) =\u003e {\n        await SendbirdCalls.ios_registerVoIPPushToken(voipToken)\n        // The VoIP Push Token has been registered successfully\n    });\n    RNVoipPushNotification.registerVoipToken();\n}\n```\n\n### Step 3: Add an event handler\n\nThe SDK provides two types of event handlers for various events that client apps may respond to: `SendbirdCallListener` Listener and `DirectCallListener`\n\n#### - SendbirdCallListener\n\nRegister a device-specific `onRinging` event handler using the `SendbirdCalls.setListener()` method.\nIt is recommended to add the event handler during initialization because it is a prerequisite for detecting onRinging event.\nThe code below shows the way device-wide events such as incoming calls are handled once `SendbirdCallListener.onRinging` is added.\n\n```ts\nSendbirdCalls.setListener({\n  onRinging(callProps: DirectCallProperties) {\n    // Process incoming call\n  },\n});\n```\n\n| Listener  | Invoked when                                        |\n| --------- | --------------------------------------------------- |\n| onRinging | Incoming calls are received in the callee’s device. |\n\n\u003e **NOTE**: You can set up only one SendbirdCallListener.\n\n#### - DirectCallListener\n\nRegister a call-specific `DirectCallListener` event handler using the `DirectCall.addListener()` method.\nResponding to call-specific events, such as establishing a successful call connection, is then handled as shown below:\n\n```ts\nconst unsubscribe = directCall.addListener({\n  onEstablished: (call: DirectCallProperties) =\u003e {},\n\n  onConnected: (call: DirectCallProperties) =\u003e {},\n\n  onEnded: (call: DirectCallProperties) =\u003e {},\n\n  onRemoteAudioSettingsChanged: (call: DirectCallProperties) =\u003e {},\n\n  onRemoteVideoSettingsChanged: (call: DirectCallProperties) =\u003e {},\n\n  onCustomItemsUpdated: (call: DirectCallProperties, updatedKeys: string[]) =\u003e {},\n\n  onCustomItemsDeleted: (call: DirectCallProperties, deletedKeys: string[]) =\u003e {},\n\n  onReconnecting: (call: DirectCallProperties) =\u003e {},\n\n  onReconnected: (call: DirectCallProperties) =\u003e {},\n\n  onAudioDeviceChanged: (call: DirectCallProperties, info: AudioDeviceChangedInfo) =\u003e {},\n\n  onRemoteRecordingStatusChanged: (call: DirectCallProperties) =\u003e {},\n\n  onUserHoldStatusChanged: (call: DirectCallProperties, isLocalUser: boolean, isUserOnHold: boolean) =\u003e {},\n\n  onLocalVideoSettingsChanged: (call: DirectCallProperties) =\u003e {},\n});\n\nunsubscribe();\n```\n\n\u003e **NOTE** Don't forget remove the listener.\n\u003e For example, you can call `unsubscribe()` from `onEnded` of listener you set or clean-up of `useEffect`.\n\n\u003cbr/\u003e\n\n| Method                           | Invocation criteria                                                                                                                                                                                                                                                                                               |\n| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| onEstablished()                  | The callee accepted the call using the method `directCall.accept()`. However, neither the caller or callee’s devices are connected to media devices yet.                                                                                                                                                          |\n| onConnected()                    | A connection is established between the caller and callee’s media devices such as microphones and speakers. The voice or video call can begin.                                                                                                                                                                    |\n| onEnded()                        | The call is ended on either the caller or the callee’s devices. When the `directCall.end()` method is used from either party, a call ends. directCall.end event listener is also invoked if the call is ended for other reasons. Refer to Call results in Appendix for all possible reasons for call termination. |\n| onRemoteAudioSettingsChanged()   | The other party changed their audio settings.                                                                                                                                                                                                                                                                     |\n| onRemoteVideoSettingsChanged()   | The other party changed their video settings.                                                                                                                                                                                                                                                                     |\n| onCustomItemsUpdated()           | One or more of `DirectCall`’s custom items (metadata) have been updated.                                                                                                                                                                                                                                          |\n| onCustomItemsDeleted()           | One or more of `DirectCall`’s custom items (metadata) have been deleted.                                                                                                                                                                                                                                          |\n| onReconnecting()                 | `DirectCall` started attempting to reconnect to the other party after a media connection disruption.                                                                                                                                                                                                              |\n| onReconnected()                  | The disrupted media connection reestablished.                                                                                                                                                                                                                                                                     |\n| onAudioDeviceChanged()           | The audio device used in the call has changed.                                                                                                                                                                                                                                                                    |\n| onRemoteRecordingStatusChanged() | The other user's recording status has been changed.                                                                                                                                                                                                                                                               |\n| onUserHoldStatusChanged()        | The local or remote user puts a call on hold or removes a hold from a call.                                                                                                                                                                                                                                       |\n| onLocalVideoSettingsChanged()    | The local user's video settings has been changed.                                                                                                                                                                                                                                                                 |\n\n### Step 4: Make a call\n\nFirst, prepare the call parameters to initiate a call.\nThe parameter contains the initial call configuration, such as callee’s user id, audio or video capabilities, and `CallOptions` object.\nOnce prepared, the call parameters are then passed into the `SendbirdCalls.dial()` method to start the call.\n\n\u003e **NOTE**: For reduce the event delay between Native and JavaScript, SDK does not convert `DirectCallProperties` to `DirectCall` on `SendbirdCallListener.onRinging` or `SendbirdCalls.Dial`.\n\u003e So you need to get `DirectCall` using `SendbirdCalls.getDirectCall()` after receiving the event or call method.\n\n```ts\nconst callOptions: CallOptions = {\n    audioEnabled: true,\n    videoEnabled: true,\n    frontCamera: true,\n}\n\nconst callProps = await SendbirdCalls.dial(CALLEE_ID, IS_VIDEO_CALL, callOptions);\n\nconst directCall = await SendbirdCalls.getDirectCall(callProps.callId);\ndirectCall.addListener({\n    // ...\n});\n```\n\n### Step 5: Receive a call\n\nRegister `SendbirdCallListener` first to receive incoming calls.\nAccept or decline incoming calls using the `directCall.accept()` or the `directCall.end()` methods.\nIf the call is accepted, a media session will automatically be established by the SDK.\n\nBefore accepting any calls, the `DirectCall.addListener` must be registered upfront in the `SendbirdCallListener.onRinging`.\nOnce registered, `DirectCallListener` enables reacting to in-call events through listener methods.\n\n```ts\nSendbirdCalls.setListener({\n  async onRinging(callProps: DirectCallProperties) {\n    const directCall = await SendbirdCalls.getDirectCall(callProps.callId);\n\n    const unsubscribe = directCall.addListener({\n      onEnded(call) {\n        unsubscribe();\n      },\n    });\n\n    directCall.accept();\n  },\n});\n```\n\n\u003cbr /\u003e\n\n## Implementation direct call guide\n\n### Make a call\n\nRegister `SendbirdCallLisetner` first to receive incoming calls.\nAccept or decline incoming calls using the `directCall.accept()` or the `directCall.end()` methods.\nIf the call is accepted, a media session will automatically be established by the SDK.\n\nBefore accepting any calls, the `DirectCall.addListener` must be registered upfront in the `SendbirdCallListener.onRinging`.\nOnce registered, `DirectCallListener` enables reacting to in-call events through listener methods.\n\n```ts\nSendbirdCalls.setListener({\n  async onRinging(callProps: DirectCallProperties) {\n    const directCall = await SendbirdCalls.getDirectCall(callProps.callId);\n\n    const unsubscribe = directCall.addListener({\n      onEnded(call) {\n        unsubscribe();\n      },\n    });\n\n    directCall.accept();\n  },\n});\n```\n\n### Receive a call\n\nRegister `SendbirdCallListener` first to receive incoming calls.\nAccept or decline incoming calls using the `directCall.accept()` or the `directCall.end()` methods.\nIf the call is accepted, a media session will automatically be established by the SDK.\n\nBefore accepting any calls, the `DirectCall.addListener` must be registered upfront in the `SendbirdCallListener.onRinging`.\nOnce registered, `DirectCallListener` enables reacting to in-call events through listener methods.\n\n```ts\nSendbirdCalls.setListener({\n  async onRinging(callProps) {\n    const directCall = await SendbirdCalls.getDirectCall(callProps.callId);\n\n    const unsubscribe = directCall.addListener({\n      onEnded(call) {\n        unsubscribe();\n      },\n    });\n\n    directCall.accept();\n  },\n});\n```\n\n### Receive a call in background\n\n#### iOS\n\nWhen the app is in the foreground, incoming call events are received through the SDK’s persistent internal server connection.\nHowever, when the app is terminated or in the background, incoming calls are received through PushKit.\nPushKit messages received by the `Native iOS SDK` must be delivered to the `SendBirdCall.pushRegistry(_:didReceiveIncomingPushWith:for:)` method.\n\n\u003e **NOTE**: in objective-c, `[SBCSendBirdCall pushRegistry:_ didReceiveIncomingPushWith:for:]`\n\nSo you should implement native features that `CallKit` and `PushKit(VoIP Push)` to your react-native app.\n(e.g. [`react-native-callkeep`](https://github.com/react-native-webrtc/react-native-callkeep), [`react-native-voip-push-notification`](https://github.com/react-native-webrtc/react-native-voip-push-notification))\n\n```objc\n// AppDelegate.h\n\n#import \u003cPushKit/PushKit.h\u003e\n\n@interface AppDelegate : UIResponder \u003cUIApplicationDelegate, RCTBridgeDelegate, PKPushRegistryDelegate\u003e\n```\n\n```objc\n// AppDelegate.m\n\n#import \u003cRNCallKeep.h\u003e\n#import \u003cSendBirdCalls/SendBirdCalls-Swift.h\u003e\n\n// ...\n\n- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)())completion\n{\n  [SBCSendBirdCall pushRegistry:registry didReceiveIncomingPushWith:payload for:type completionHandler:^(NSUUID * _Nullable uuid) {\n\n    // IMPORTANT: Incoming calls MUST be reported when receiving a PushKit push.\n    //  If you don't report to CallKit, the app will be terminated.\n\n    if(uuid != nil) {\n\n      // Report valid call\n\n      SBCDirectCall* call = [SBCSendBirdCall callForUUID: uuid];\n      [RNCallKeep reportNewIncomingCall: [uuid UUIDString]\n                                 handle: [[call remoteUser] userId]\n                             handleType: @\"generic\"\n                               hasVideo: [call isVideoCall]\n                    localizedCallerName: [[call remoteUser] nickname]\n                        supportsHolding: YES\n                           supportsDTMF: YES\n                       supportsGrouping: YES\n                     supportsUngrouping: YES\n                            fromPushKit: YES\n                                payload: [payload dictionaryPayload]\n                  withCompletionHandler: completion];\n    } else {\n\n      // Report and end invalid call\n\n      NSUUID* uuid = [NSUUID alloc];\n      NSString* uuidString = [uuid UUIDString];\n\n      [RNCallKeep reportNewIncomingCall: uuidString\n                                 handle: @\"invalid\"\n                             handleType: @\"generic\"\n                               hasVideo: NO\n                    localizedCallerName: @\"invalid\"\n                        supportsHolding: NO\n                           supportsDTMF: NO\n                       supportsGrouping: NO\n                     supportsUngrouping: NO\n                            fromPushKit: YES\n                                payload: [payload dictionaryPayload]\n                  withCompletionHandler: completion];\n      [RNCallKeep endCallWithUUID:uuidString reason:1];\n    }\n  }];\n}\n```\n\n```ts\nimport RNCallKeep from 'react-native-callkeep';\nimport RNVoipPushNotification from 'react-native-voip-push-notification';\n\nSendbirdCalls.setListener({\n  async onRinging(callProps) {\n    const directCall = await SendbirdCalls.getDirectCall(callProps.callId);\n\n    // handle incoming call with CallKit (react-native-callkeep)\n    RNCallKeep.addEventListener('answerCall', async () =\u003e {\n      directCall.accept();\n    });\n    RNCallKeep.addEventListener('endCall', async () =\u003e {\n      directCall.end();\n    });\n\n    const unsubscribe = directCall.addListener({\n      onEnded() {\n        RNCallKeep.removeEventListener('answerCall');\n        RNCallKeep.removeEventListener('endCall');\n        RNCallKeep.endAllCalls();\n        unsubscribe();\n      },\n    });\n\n    RNCallKeep.displayIncomingCall(\n      callProps.ios_callUUID,\n      callProps.remoteUser?.userId,\n      callProps.remoteUser?.nickname ?? 'Unknown',\n      'generic',\n      callProps.isVideoCall,\n    );\n  },\n});\n\nRNVoipPushNotification.registerVoipToken();\n```\n\n#### Android\n\nWhen the app is in the foreground, incoming call events are received through the SDK’s persistent internal server connection.\nHowever, when the app is closed or in the background, incoming calls are received through the Firebase Cloud Messaging’s (FCM) push notifications.\nThe FCM messages received by SendbirdCalls must be delivered to the SDK through the `SendbirdCalls.android_handleFirebaseMessageData()` method.\n\n```ts\nimport messaging, { FirebaseMessagingTypes } from '@react-native-firebase/messaging';\n\nSendbirdCalls.setListener({\n  async onRinging(callProps) {\n    const directCall = await SendbirdCalls.getDirectCall(callProps.callId);\n\n    // handle incoming call with what you want (e.g. Notifee foreground service)\n  },\n});\n\nconst firebaseListener = async (message: FirebaseMessagingTypes.RemoteMessage) =\u003e {\n  SendbirdCalls.android_handleFirebaseMessageData(message.data);\n};\nmessaging().setBackgroundMessageHandler(firebaseListener);\nmessaging().onMessage(firebaseListener);\n```\n\n### Handle a current call\n\nDuring an ongoing call, a caller may mute or unmute their microphone by using the `directCall.muteMicrophone()` or `directCall.unmuteMicrophone()` methods.\n`DirectCallListener.onRemoteAudioSettingsChanged()` listener method will notify any changes that a remote user makes on audio settings to the local user.\n\nThe caller may start or stop video using the `directCall.startVideo()` or `directCall.stopVideo()` methods.\nIf the remote user changes the video settings, the local user will be notified through the `DirectCallListener.onRemoteVideoSettingsChanged()` listener.\nThe current video device can be changed by using the `directCall.selectVideoDevice()` or `directCall.switchCamera()`.\n\n```swift\n// mute my microphone\ndirectCall.muteMicrophone();\n\n// unmute my microphone\ndirectCall.unmuteMicrophone();\n\n// starts to show video\ndirectCall.startVideo();\n\n// stops showing video\ndirectCall.stopVideo();\n\n// changes current video device\ndirectCall.selectVideoDevice(VIDEO_DEVICE).catch(error =\u003e {\n})\n\n// receives the event\ndirectCall.addListener({\n  ...\n\n  onRemoteAudioSettingsChanged: (callProps: DirectCallProperties) =\u003e {\n    if (callProps.isRemoteAudioEnabled) {\n        // The peer has been unmuted.\n        // Consider displaying an unmuted icon.\n    } else {\n        // The peer has been muted.\n        // Consider displaying and toggling a muted icon.\n    }\n  },\n\n  onRemoteVideoSettingsChanged: (callProps: DirectCallProperties) =\u003e {\n    if (callProps.isRemoteVideoEnabled) {\n        // The peer has started video.\n    } else {\n        // The peer has stopped video.\n    }\n  },\n});\n```\n\n### End a call\n\nA caller may end a call using the `directCall.end()` method. The event will then be processed through the `DirectCallListener.onEnded()` listener.\nThis listener is also triggered if the remote user ends the call.\n\n```ts\n// End a call\ncall.end();\n\n// receives the event\ndirectCall.addListener({\n  // ...\n\n  onEnded: (callProps: DirectCallProperties) =\u003e {\n    // Consider releasing or destroying call-related view from here.\n  },\n});\n```\n\n### Unregister a push token and deauthenticate a user\n\n#### - Unregister one or all VoIP push tokens\n\n\u003e **Warning**: unregister all is not supported yet.\n\nUsers will no longer receive call notifications after the VoIP push token has been unregistered\nthrough the `unregisterPushToken(TOKEN)` or `unregisterVoIPPushToken(TOKEN)` method before deauthenticate.\n\n[//]: # 'To stop sending notifications to all of the user’s logged in devices, call the `unregisterAllVoIPPushTokens(completionHandler:)` method.'\n\n```ts\nconst unregisterPushToken = () =\u003e {\n  SendbirdCalls.unregisterPushToken(TOKEN);\n\n  // or\n\n  SendbirdCalls.ios_unregisterVoIPPushToken(TOKEN);\n};\n```\n\n#### - Deauthenticate a user\n\nWhen users log out of their call client apps, they must be deauthenticated with `SendbirdCalls.deauthenticate()` method.\n\n```ts\nconst signOut = () =\u003e {\n  SendbirdCalls.deauthenticate();\n};\n```\n\n### Display Video\n\n```tsx\nimport { DirectCallVideoView } from '@sendbird/calls-react-native';\n\nconst YourApp = () =\u003e {\n  const directCall = useDirectCall(callId);\n\n  return (\n    \u003cView\u003e\n      {/* Remote video view */}\n      \u003cDirectCallVideoView viewType={'remote'} callId={directCall.callId} /\u003e\n\n      {/* Local video view */}\n      \u003cDirectCallVideoView viewType={'local'} callId={directCall.callId} /\u003e\n    \u003c/View\u003e\n  );\n};\n```\n\n### Mirror a DirectCallVideoView\n\n```tsx\nconst VideoView = () =\u003e {\n  return \u003cDirectCallVideoView mirror={false} callId={'CALL_ID'} viewType={'remote'} /\u003e;\n};\n```\n\n### Retrieve a call information\n\nThe local or remote user’s information is available via the `directCall.localUser` and `directCall.remoteUser` properties.\n\n### Retrieve call history\n\nSendbird server automatically stores details of calls, which can be retrieved later to display call history for users.\nA user’s call history is available through a `DirectCallLogListQuery` instance.\n\n```tsx\nimport { DirectCallEndResult, DirectCallLogListQuery, SendbirdCalls } from '@sendbird/calls-react-native';\n\nconst useCallHistory = () =\u003e {\n  const [history, setHistory] = useState\u003cDirectCallLog[]\u003e([]);\n  const query = useRef\u003cDirectCallLogListQuery\u003e();\n\n  useEffect(() =\u003e {\n    const effect = async () =\u003e {\n      query.current = await SendbirdCalls.createDirectCallLogListQuery({\n        myRole: 'ALL',\n        endResults: [\n          DirectCallEndResult.COMPLETED,\n          DirectCallEndResult.CANCELED,\n          DirectCallEndResult.DECLINED,\n          DirectCallEndResult.DIAL_FAILED,\n          DirectCallEndResult.ACCEPT_FAILED,\n        ],\n        limit: 20,\n      });\n\n      const data = await query.current.next();\n      setHistory(data);\n    };\n\n    effect();\n\n    return () =\u003e {\n      query.current.release();\n    };\n  }, []);\n\n  return {\n    next: async () =\u003e {\n      if (query.hasNext) {\n        const data = await query.current.next();\n        setHistory((prev) =\u003e prev.concat(...data));\n      }\n    },\n    history,\n  };\n};\n```\n\n| Params     | Description                                                                                                                                                                                                                                                                           |\n| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| limit      | Specifies the number of call history entries to return at once.                                                                                                                                                                                                                       |\n| myRole     | Returns the call history of the specified role. For example, the `params.myRole = CALLEE` returns only the callee’s call history.)                                                                                                                                                    |\n| endResults | Filters the results based on the call end result such as `COMPLETED`,`NO_ANSWER`, etc. If multiple values are specified, they are processed as an `OR` condition. For example, for `endResults: [NO_ANSWER]`, only the history entries that resulted in `NO_ANSWER` will be returned. |\n\n| Method    | Description                                                                 |\n| --------- | --------------------------------------------------------------------------- |\n| next()    | Used to query the call history from Sendbird Calls server.                  |\n| hasNext   | If **true**, there are additional call history entries yet to be retrieved. |\n| isLoading | If **true**, the call history is being retrieved from the server.           |\n\n### Timeout options\n\n\u003e **Warning**: Not supported yet.\n\nThe following table lists a set of methods of the `SendbirdCalls` class.\n\n### Sound effects\n\nYou should add files to your native project\nfor Android, add your files to `res/raw/{filename}.mp3`\nfor iOS, when you add files to a project, xcode automatically added to the bundled resources (Build Phases \u003e Copy Bundle Resources)\n\n#### - Sound types\n\n| Type         | Description                                                                                                                                       |\n| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |\n| dialing      | Refers to a sound that is played on a caller’s side when the caller makes a call to a callee.                                                     |\n| ringing      | Refers to a sound that is played on a callee’s side when receiving a call.                                                                        |\n| reconnecting | Refers to a sound that is played when a connection is lost, but immediately tries to reconnect. Users are also allowed to customize the ringtone. |\n| reconnnected | Refers to a sound that is played when a connection is re-established.                                                                             |\n\n#### - Add sound\n\n| Method             | Description                                                                                             |\n| ------------------ | ------------------------------------------------------------------------------------------------------- |\n| addDirectCallSound | Adds a specific sound to a direct call such as a ringtone or an alert tone with an Android resource ID. |\n\n| Parameter | Type      | Description                                                 |\n| --------- | --------- | ----------------------------------------------------------- |\n| soundType | SoundType | Specifies the sound type to be used according to the event. |\n| fileName  | int       | Specifies the Android resource ID.                          |\n\n#### - Remove sound\n\n| Method                | Description                                  |\n| --------------------- | -------------------------------------------- |\n| removeDirectCallSound | Removes a specific sound from a direct call. |\n\n| Parameter | Type      | Description                                                 |\n| --------- | --------- | ----------------------------------------------------------- |\n| soundType | SoundType | Specifies the sound type to be used according to the event. |\n\n\u003cbr /\u003e\n\n### Call results\n\nInformation relating the end result of a call can be obtained at any time through the `directCall.endResult` property, best invoked within the `onEnded()` listener.\n\n| DirectCallEndResult   | Description                                                                                                                                                       |\n| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| NO_ANSWER             | The callee failed to either accept or decline the call within a specific amount of time.                                                                          |\n| CANCELED              | The caller canceled the call before the callee could accept or decline.                                                                                           |\n| DECLINED              | The callee declined the call.                                                                                                                                     |\n| COMPLETED             | The call ended after either party ended it                                                                                                                        |\n| TIMED_OUT             | Sendbird Calls server failed to establish a media session between the caller and callee within a specific amount of time.                                         |\n| CONNECTION_LOST       | The data stream from either the caller or the callee has stopped due to a `WebRTC` connection issue.                                                              |\n| DIAL_FAILED           | The `dial()` method call has failed.                                                                                                                              |\n| ACCEPT_FAILED         | The `accept()` method call has failed.                                                                                                                            |\n| OTHER_DEVICE_ACCEPTED | The incoming call was accepted on a different device. This device received an incoming call notification, but the call ended when a different device accepted it. |\n| NONE                  | Default value of the endResult.                                                                                                                                   |\n| UNKNOWN               | Ended with unknown reason.                                                                                                                                        |\n\n\u003cbr /\u003e\n\n## Making your first group call\n\nFollow the step-by-step instructions below to authenticate and make your first group call.\n\n### Step 1: Initialize the SendbirdCall instance in a client app\n\nAs shown below, the `SendbirdCalls` instance must be initiated when a client app is launched.\nInitialize the `SendbirdCalls` instance with the `APP_ID` of the Sendbird application you would like to use to make a call.\n\n```ts\nimport { SendbirdCalls } from '@sendbird/calls-react-native';\n\nSendbirdCalls.initialize(APP_ID);\n```\n\n\u003e Note: If another initialization with another `APP_ID` takes place, all existing data in the app will be deleted and the `SendbirdCalls` instance will be initialized with the new `APP_ID`.\n\n### Step 2: Authenticate a user\n\nIn order to participate in the group calls, authenticate the user with SendBird server with the `SendbirdCalls.authenticate()` method.\n\n```ts\nimport { SendbirdCalls } from '@sendbird/calls-react-native';\n\n// Authenticate\nSendbirdCalls.authenticate({\n  userId: USER_ID,\n  accessToken: ACCESS_TOKEN,\n})\n  .then((user) =\u003e {\n    // The user has been authenticated successfully\n  })\n  .catch((error) =\u003e {\n    // error\n  });\n```\n\n### Step 3: Create a room\n\nBy calling the `SendbirdCalls.createRoom()` by passing `SMALL_ROOM_FOR_VIDEO` as the parameter, you can create a room for up to 6 participants to make a video call. When a room is created, the status of the room becomes `OPEN` and `ROOM_ID` is generated.\n\n```ts\nconst room = await SendbirdCalls.createRoom(SendbirdCalls.RoomType.SMALL_ROOM_FOR_VIDEO);\n```\n\n\u003e **Note**: Share the room ID with other users for them to enter the room from the client app.\n\n### Step 4: Enter a room\n\nA user can search a room with a specific `ROOM_ID` to participate in a group call at any time.\n\n#### - retrieve a room instance\n\nTo enter a room, you must first acquire the room instance from Sendbird server with the room ID. To fetch the most up-to-date room instance from Sendbird server, use the `SendbirdCalls.fetchRoomById()` method. Also, you can use the `SendbirdCalls.getCachedRoomById()` method that returns the most recently cached room instance from Sendbird Calls SDK.\n\n```ts\n// get room instance using ROOM_ID\nconst room = await SendbirdCalls.fetchRoomById(ROOM_ID);\n\n// get cached room instance using ROOM_ID\nconst cachedRoom = await SendbirdCalls.getCachedRoomById(ROOM_ID);\n```\n\n#### - enter a room\n\nOnce the room is retrieved, call the `enter()` method to enter the room. An object that sets whether to use video and audio is passed to `enter()` as a parameter. If no parameters are passed, both audio and video are enabled as default.\n\nWhen a user enters a room, a participant is created with a unique `participant ID` to represent the user in the room.\n\nIf you create a room using `SendbirdCalls.createRoom()`, you can use the returned room instance without needing to get a room instance.\n\n```ts\nconst enterParams: EnterParams = {\n    audioEnabled: true,\n    videoEnabled: true,\n}\nawait room.enter(enterParams)\n```\n\n\u003e **NOTE**: If there is no room whose ID is room ID passed as a parameter among the cached room instances, `SendbirdCalls.getCachedRoomById()` returns `null`. So you should need to check the returned value before calling `enter()`.\n\n### Step 5: Handle events in a room\n\nA user can receive events of a room that they are currently participating. Users will be notified when other participants enter or leave the room, change their media settings, or when the room is deleted.\n\n#### - Add event listener\n\nAdd an event listener for the user to receive events that occur in a room that the user joins as a participant.\n\n```ts\nconst unsubscribe = room.addListener({\n  onRemoteParticipantEntered: (participant: Participant) =\u003e {},\n\n  onRemoteParticipantExited: (participant: Participant) =\u003e {},\n\n  onRemoteParticipantStreamStarted: (participant: Participant) =\u003e {},\n\n  onRemoteVideoSettingsChanged: (participant: Participant) =\u003e {},\n\n  onRemoteAudioSettingsChanged: (participant: Participant) =\u003e {},\n\n  onAudioDeviceChanged: (info: AudioDeviceChangedInfo) =\u003e {},\n\n  onCustomItemsUpdated: (updatedKeys: string[]) =\u003e {},\n\n  onCustomItemsDeleted: (deletedKeys: string[]) =\u003e {},\n\n  onDeleted: () =\u003e {},\n\n  onError: (e: SendbirdError, participant: Participant | null) =\u003e {},\n});\n\nunsubscribe();\n```\n\n\u003e **NOTE** Don't forget to remove the listener.\n\u003e For example, you can call `unsubscribe()` from clean-up of `useEffect`.\n\n\u003cbr/\u003e\n\n| Method                             | Invocation criteria                                                             |\n| ---------------------------------- | ------------------------------------------------------------------------------- |\n| onRemoteParticipantEntered()       | Invoked when a remote participant has entered a room.                           |\n| onRemoteParticipantExited()        | Invoked when a remote participant has exited a room.                            |\n| onRemoteParticipantStreamStarted() | Invoked when a remote participant has started media streaming.                  |\n| onRemoteVideoSettingsChanged()     | Invoked when a remote participant's video settings have changed.                |\n| onRemoteAudioSettingsChanged()     | Invoked when a remote participant's audio settings have changed.                |\n| onAudioDeviceChanged()             | Invoked when the audio device used in the call has changed.                     |\n| onCustomItemsUpdated()             | Invoked when one or more of `Room`’s custom items (metadata) have been updated. |\n| onCustomItemsDeleted()             | Invoked when one or more of `Room`’s custom items (metadata) have been deleted. |\n| onDeleted()                        | Invoked when `Room` is deleted.                                                 |\n| onError()                          | Invoked when a participant stream is lost due to reconnection failure.          |\n\n\u003cbr /\u003e\n\n### Step 6: Exit a room\n\nTo leave a room, call `exit()`. On the room handlers of the remaining participants, the `onRemoteParticipantExited()` method will be called.\n\n```ts\nroom.exit();\n```\n\n\u003cbr /\u003e\n\n## Implementation group call guide\n\n### Create a room\n\nA room is a must to use a **Group calls** to talk to multiple people. You can create a new room using `SendbirdCalls.createRoom()`. Once the room is created, you must use `enter()` to enter the room. And then you have to share the `ROOM_ID` of the room with other users in order for other participants can enter the room.\n\n```ts\nconst room = await SendbirdCalls.createRoom(SendbirdCalls.RoomType.SMALL_ROOM_FOR_VIDEO);\nawait room.enter();\n```\n\n### Handle events in a room\n\nA user can receive events of a room that they are currently participating. Users will be notified when other participants enter or leave the room, change their media settings, or when the room is deleted.\n\nYou don't need to define all events method, you just need to define the methods you want to implement. And, don't forget to remove the listener. For example, you can call `unsubscribe()` from clean-up of `useEffect`.\n\n```tsx\nuseEffect(() =\u003e {\n  const unsubscribe = room.addListener({\n    onRemoteParticipantEntered: (participant: Participant) =\u003e {},\n\n    onRemoteParticipantExited: (participant: Participant) =\u003e {},\n\n    onRemoteParticipantStreamStarted: (participant: Participant) =\u003e {},\n\n    ...\n  });\n\n  return unsubscribe();\n}, []);\n```\n\n### Enter a room\n\nUse `SendbirdCalls.fetchRoomById()` with `ROOM_ID` to get the room instance you want to enter. Or, if you have fetched the room before, you can use `SendbirdCalls.getCachedRoomById()` to get a cached room instance. Then call the `enter()` method to enter the room.\n\nWhen a user enters a room, a participant is created with a unique `participant ID` to represent the user in the room. When the remote user enters the room, the `onRemoteParticipantEntered()` listener method is called. And then when the participant has started media streaming, `onRemoteParticipantStreamStarted()` listener method is called.\n\n\u003e **NOTE**: If there is no room whose ID is room ID passed as a parameter among the cached room instances, `SendbirdCalls.getCachedRoomById()` returns `null`. So you should need to check the returned value.\n\n```ts\n// get room instance using ROOM_ID\nconst room = await SendbirdCalls.fetchRoomById(ROOM_ID);\nawait room.enter()\n\n// get cached room instance using ROOM_ID\nconst cachedRoom = await SendbirdCalls.getCachedRoomById(ROOM_ID);\nawait cachedRoom?.enter()\n\n// receives the event\nroom.addListener({\n  onRemoteParticipantEntered: (participant: Participant) =\u003e {\n    // the remote participant entered the room\n  },\n\n  onRemoteParticipantStreamStarted: (participant: Participant) =\u003e {\n    // the remote participant has started media streaming\n  },\n\n  ...\n});\n```\n\n### Handle a current call\n\nParticipants can mute or unmute their microphones using the `room.localParticipant.muteMicrophone()` or `room.localParticipant.unmuteMicrophone()` methods.\n`onRemoteAudioSettingsChanged()` listener method is invoked whenever the remote participant's audio settings change.\n\nYou can also use the `room.localParticipant.startVideo()` and `room.localParticipant.stopVideo()` methods to turn video off or on. `onRemoteVideoSettingsChanged()` method is invoked whenever the remote participant's video settings change.\n\nIf you want to switch to using the device's front and back cameras, call `room.localParticipant.switchCamera()`.\n\n```ts\n// mute my microphone\nroom.localParticipant.muteMicrophone();\n\n// unmute my microphone\nroom.localParticipant.unmuteMicrophone();\n\n// starts to show video\nroom.localParticipant.startVideo();\n\n// stops showing video\nroom.localParticipant.stopVideo();\n\n// changes current video device\nroom.localParticipant.switchCamera();\n\n// receives the event\nroom.addListener({\n  onRemoteVideoSettingsChanged: (participant: Participant) =\u003e {\n    if (participant.isVideoEnabled) {\n      // remote Participant has started video.\n    } else {\n      // remote Participant has stopped video.\n    }\n  },\n\n  onRemoteAudioSettingsChanged: (participant: Participant) =\u003e {\n    if (participant.isAudioEnabled) {\n      // remote Participant has been unmuted.\n      // Consider displaying an unmuted icon.\n    } else {\n      // remote Participant has been muted.\n      // Consider displaying and toggling a muted icon.\n    }\n  },\n\n  ...\n});\n```\n\n### Exit a room\n\nParticipants can use the `exit()` method to leave the room and end the group call. When the remote participant leaves the room, the `onRemoteParticipantExited()` listener method is called.\n\n```ts\n// Exit a room\nroom.exit();\n\n// receives the event\nroom.addListener({\n  onRemoteParticipantExited: (participant: Participant) =\u003e {\n    // Consider destroying the remote participant's view.\n  },\n\n  ...\n});\n```\n\n## Display Video\n\nBy passing the `participant` instance and `ROOM_ID` to the `GroupCallVideoView` component, you can display the streamed view on the screen. Group calls can have up to 6 people, so you should need to think about how to arrange views on the screen depending on the number of participants.\n\n```tsx\nimport { useEffect, useState } from 'react';\n\nimport { GroupCallVideoView, Room, SendbirdCalls } from '@sendbird/calls-react-native';\n\nconst YourApp = () =\u003e {\n  const [room, setRoom] = useState\u003cRoom\u003e();\n\n  useEffect(() =\u003e {\n    SendbirdCalls.getCachedRoomById(ROOM_ID).then((room) =\u003e setRoom(room));\n  }, []);\n\n  return (\n    \u003cView\u003e\n      {room?.participants.map((participant) =\u003e (\n        \u003cGroupCallVideoView participant={participant} roomId={room.roomId} resizeMode={'contain'} /\u003e\n      ))}\n    \u003c/View\u003e\n  );\n};\n```\n\n\u003cbr/\u003e\n\n| Props       | Description                                           |\n| ----------- | ----------------------------------------------------- |\n| participant | participant instance to display on screen             |\n| roomId      | ID of the participating room                          |\n| resizeMode  | how to resize the image. 'contain', 'cover', 'center' |\n| style       | style object for component                            |\n\n### Retrieve a participant information\n\nThe local or remote participant’s information is available via the `room.participants` or `room.localParticipant` and `room.remoteParticipants` properties.\n\n\u003cbr /\u003e\n\n## Appendix\n\n### Encoding Configurations\n\n#### iOS\n\n| Category           | Value      | Note                                                  |\n| ------------------ | ---------- | ----------------------------------------------------- |\n| Frames per second  | 24 fps     |                                                       |\n| Maximum resolution | 720p       | 1280x720 px; standard HD                              |\n| Audio Codec        | OPUS       |                                                       |\n| Video Codec        | H.264, VP8 | H.264 is used between iOS devices as a default codec. |\n\n#### Android\n\n| Category           | Value | Note                    |\n| ------------------ | ----- | ----------------------- |\n| Frames per second  | 24    |                         |\n| Maximum resolution | 720p  | 1280 x 720; standard HD |\n| Audio codec        | OPUS  |                         |\n| Video codec        | VP8   |                         |\n\n### SDK Sizes\n\n#### iOS\n\n| File               | Raw Files | Compiled Size |\n| ------------------ | --------- | ------------- |\n| Calls SDK (1.7.0)  | 77.1 MB   | 2.32 MB       |\n| WebRTC SDK (1.3.0) | 1.18 GB   | 6.32 MB       |\n\n(Xcode 12.3, Any iOS Device (arm64))\n\n#### Android\n\n| File       | Raw files | Compiled size |\n| ---------- | --------- | ------------- |\n| Calls SDK  | 1.77MB    | 1.18MB        |\n| WebRTC SDK | 26.8MB    | 12MB          |\n\n### Call relay protocol\n\nSendbird Calls is based on WebRTC to enable real-time calls between users with P2P connections, but sometimes connectivity issues may occur for users due to network policies that won’t allow WebRTC communications through Firewalls and NATs (Network Address Translators). For this, Sendbird Calls uses two different types of protocols, **Session Traversal Utilities for NAT (STUN)** and **Traversal Using Relays around NAT (TURN)**. **STUN** and **TURN** are protocols that support establishing a connection between users.\n\n\u003e **Note**: See our [GitHub page](https://github.com/sendbird/guidelines-calls/tree/master/Recommendation%20on%20firewall%20configuration) to learn about the requirements and how to use the Calls SDKs behind a firewall.\n\n---\n\n#### How STUN and TURN works\n\nSession Traversal Utilities for NAT (STUN) is a protocol that helps hosts to discover the presence of a NAT and the IP address, which eventually makes the connection between two endpoints. Traversal Using Relays around NAT (TURN) is a protocol that serves as a relay extension for data between two parties.\n\nSendbird Calls first try to make a P2P connection directly using the Calls SDK. If a user is behind a NAT/Firewall, Calls will discover the host's public IP address as a location to establish connection using STUN. In most cases, STUN server is only used during the connection setup and once the session has been established, media will flow directly between two users. If the NAT/Firewall still won't allow the two users to connect directly, TURN server will be used to make a connection to relay the media data between two users. Most of the WebRTC traffic is connected with STUN.\n\n_- Last Updated: June 17th, 2022_\n\n\u003cbr /\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsendbird%2Fsendbird-calls-sdk-react-native","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsendbird%2Fsendbird-calls-sdk-react-native","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsendbird%2Fsendbird-calls-sdk-react-native/lists"}