Room Class Methods - Android
- Call each method shown as
suspend funfrom a coroutine. The examples uselifecycleScope, which comes fromandroidx.lifecycle:lifecycle-runtime-ktx. - A
suspendmethod returns when the call has finished. If the call is refused or fails, it throwsVideoSDKException, which carries acodeand aname. The exception is unchecked, so an exception you don't catch crashes the app. - Every
suspendmethod is safe to call from the main thread. Room events reach your listeners on the main thread. - Called before the room is joined, or while it is reconnecting, most methods report
3035or3065ononErrorand return without throwing. A call refused while reconnecting is not retried after the room reconnects. The sections below say which methods throw in that case.
join()
suspend fun join()
- Joins the room and turns on the mic and camera you asked for in
VideoSDK.createRoom().createRoom()returns a newRoombut does not join it. - Before you call it, add your
RoomEventListenerwithaddEventListener(), or you can miss the first events. - Get
CAMERAandRECORD_AUDIOgranted first, withVideoSDK.checkPermissions(). A device whose permission is missing stays off for the whole session. - You are in the room once
onRoomJoinedfires. That event can arrive beforejoin()returns. - The same
Roomcan be joined again afterleave().
Events associated with join():
- The local participant receives
onRoomStateChangedwithCONNECTINGwhen the join starts. - The local participant receives
onRoomJoinedonce joined, thenonRoomStateChangedwithCONNECTED. If the join fails, it receivesonErrorandonRoomStateChangedwithFAILED. - The local participant receives
onStreamEnabledfor each stream turned on by the join. - Remote participants receive
onParticipantJoinedwith the newParticipant.
Token with ask_join
join()doesn't let the participant in directly. Participants whose token hasallow_joinreceiveonEntryRequested.- The waiting participant receives
onEntryRespondedwith the decision.
Token with allow_join
join()lets the participant in directly.
Returns
Unit
Throws
VideoSDKException with:
3055(ERROR_OPERATION_IN_PROGRESS) if a join is already running.3054(ERROR_ALREADY_IN_REQUESTED_STATE) if this room is already joined.3056(ERROR_OPERATION_ABORTED) ifleave()cancelled the join.3047(ERROR_UNABLE_TO_JOIN_ROOM), or the server's error code, such as4002(INVALID_TOKEN) for a token the server rejects. The code reported ononErrorcan differ from the thrown one.
Example
room.addEventListener(roomEventListener)
lifecycleScope.launch {
try {
room.join()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Join failed: ${e.code} ${e.name} ${e.message}")
}
}
leave()
suspend fun leave()
- Leaves the room.
- It also works while the room is reconnecting. Called while a join is still running, it cancels that join. Call it after a failed join too, so the room is released; then create a new room to try again.
- The camera and microphone are released before
onRoomLeftfires.
Events associated with leave():
- Every participant receives
onStreamDisabledfor each stream you were sending. - The local participant receives
onRoomStateChangedwithDISCONNECTED, thenonRoomLeftwithLeaveReason.MANUAL_LEAVE_CALLED. - Remote participants receive
onParticipantLeftwith theParticipantwho left.
Returns
Unit
Throws
VideoSDKException with:
3035(ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED) ifjoin()was never called.3054(ERROR_ALREADY_IN_REQUESTED_STATE) if the room has already been left.
Neither is reported on onError.
lifecycleScope is already cancelled when onDestroy() runs, so a leave() launched there never runs. Leave the room before the screen is destroyed, for example from your leave button.
Example
fun onLeaveClicked() {
lifecycleScope.launch {
try {
room.leave()
} catch (e: VideoSDKException) {
// 3035: never joined, 3054: already left
Log.w("VideoSDK", "Leave refused: ${e.code} ${e.name}")
}
finish()
}
}
end()
suspend fun end()
- Ends the room for every participant, you included.
- The server decides whether your token may end the room. If it refuses, you stay in the room.
Events associated with end():
- Every participant,
localParticipantincluded, receivesonRoomLeft.
Returns
Unit
Throws
VideoSDKException with:
3035(ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED) when you are not in the room, and3065(ERROR_ROOM_RECONNECTING) while it is reconnecting.3057(ERROR_END_ROOM_FAILED) when the server refuses. You stay in the room.3079(ERROR_OPERATION_TIMED_OUT) when the server doesn't answer.
Example
lifecycleScope.launch {
try {
room.end()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "End failed: ${e.code} ${e.name}")
}
}
enableCam()
suspend fun enableCam(customCameraTrack: CustomStreamTrack? = null)
- Turns on your camera and sends it to the room.
- Every participant receives
onStreamEnabledwith the newRTCStream. - Without a
customCameraTrack, the camera opens with the settings the room already has: those of the last custom track, or the default profile. - If the call is refused,
customCameraTrackis released, so the camera doesn't stay open.
Parameters
- customCameraTrack:
CustomStreamTrack?- A camera track from
VideoSDK.createCameraVideoTrack(). Its settings are also used by laterenableCam()calls. OPTIONAL
- A camera track from
Returns
Unit
Throws
VideoSDKException with 3054 (ERROR_ALREADY_IN_REQUESTED_STATE) when the camera is already on, 3055 (ERROR_OPERATION_IN_PROGRESS) while another camera call is running, 3066 (ERROR_ACTION_NOT_SUPPORTED_IN_MODE) in RECV_ONLY or SIGNALLING_ONLY mode, or the camera error, also reported on onError, when the camera can't be opened.
Example
lifecycleScope.launch {
try {
room.enableCam()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Camera failed: ${e.code} ${e.name}")
}
}
disableCam()
suspend fun disableCam()
- Turns off your camera. The camera is closed, so the system camera indicator goes off.
- Every participant receives
onStreamDisabledwith theRTCStreamthat stopped.
Returns
Unit
Throws
VideoSDKException with 3054 (ERROR_ALREADY_IN_REQUESTED_STATE) when the camera is already off, 3055 (ERROR_OPERATION_IN_PROGRESS) while another camera call is running, or 3066 (ERROR_ACTION_NOT_SUPPORTED_IN_MODE) in RECV_ONLY or SIGNALLING_ONLY mode.
Example
lifecycleScope.launch {
try {
if (room.isCamEnabled) room.disableCam() else room.enableCam()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Camera toggle failed: ${e.code} ${e.name}")
}
}
unmuteMic()
suspend fun unmuteMic(customMicrophoneTrack: CustomStreamTrack? = null)
- Turns on your microphone and sends it to the room.
- Every participant receives
onStreamEnabledwith the newRTCStream. - Without a
customMicrophoneTrack, the microphone opens with the audio settings the room already has. - If the call is refused,
customMicrophoneTrackis released, so the microphone doesn't stay open.
Parameters
- customMicrophoneTrack:
CustomStreamTrack?- A microphone track from
VideoSDK.createMicrophoneAudioTrack(). Its settings are also used by laterunmuteMic()calls. OPTIONAL
- A microphone track from
Returns
Unit
Throws
VideoSDKException with 3054 (ERROR_ALREADY_IN_REQUESTED_STATE) when the mic is already on, 3055 (ERROR_OPERATION_IN_PROGRESS) while another mic call is running, or 3066 (ERROR_ACTION_NOT_SUPPORTED_IN_MODE) in RECV_ONLY or SIGNALLING_ONLY mode.
muteMic()
suspend fun muteMic()
- Turns off your microphone. The microphone is released, so the system microphone indicator goes off.
- Every participant receives
onStreamDisabledwith theRTCStreamthat stopped.
Returns
Unit
Throws
VideoSDKException with 3054 (ERROR_ALREADY_IN_REQUESTED_STATE) when the mic is already muted, 3055 (ERROR_OPERATION_IN_PROGRESS) while another mic call is running, or 3066 (ERROR_ACTION_NOT_SUPPORTED_IN_MODE) in RECV_ONLY or SIGNALLING_ONLY mode.
Example
lifecycleScope.launch {
try {
if (room.isMicEnabled) room.muteMic() else room.unmuteMic()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Mic toggle failed: ${e.code} ${e.name}")
}
}
enableScreenShare()
suspend fun enableScreenShare(data: Intent, enableAudio: Boolean = false)
suspend fun enableScreenShare(customTrack: CustomStreamTrack? = null)
- Starts sharing your screen.
- Pass the
Intentfrom the screen-capture consent dialog, or a screen-share track. - The SDK starts the foreground service that Android 14 and newer require for screen capture, so the call takes a moment to return. The SDK declares the permission that service needs.
- Every participant receives
onStreamEnabledwith the newRTCStream. - Every participant receives
onPresenterChanged()with your participant ID.
Parameters
- data:
Intent- The result
Intentof the consent dialog. Use it only when the result code isRESULT_OK. The share uses theh720p_15fpsprofile.
- The result
- enableAudio:
Booleantruealso captures the device's audio and mixes it into your microphone track, so muting the mic silences both.OPTIONAL, defaultfalse
- customTrack:
CustomStreamTrack?- A screen-share track from
VideoSDK.createScreenShareVideoTrack(). It carries the consent, the profile and whether to capture audio.nulluses the track from the most recentcreateScreenShareVideoTrack()call. OPTIONAL, defaultnull
- A screen-share track from
Returns
Unit
Throws
VideoSDKException with:
3054(ERROR_ALREADY_IN_REQUESTED_STATE) when a share is already running, or3055(ERROR_OPERATION_IN_PROGRESS) while another screen-share call is running.3066(ERROR_ACTION_NOT_SUPPORTED_IN_MODE) inRECV_ONLYorSIGNALLING_ONLYmode.3016(ERROR_GET_DISPLAY_MEDIA_PERMISSION_DENIED) when the user declined the consent, or it can't be used.3048(ERROR_MEDIA_NOT_READY) when you passnulland no screen-share track was created.3071(ERROR_INVALID_CUSTOM_TRACK_CONFIG) whencustomTrackis not a screen-share track.
Example
private val screenShareLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val data = result.data
if (result.resultCode == Activity.RESULT_OK && data != null) {
lifecycleScope.launch {
try {
room.enableScreenShare(data)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Screen share failed: ${e.code} ${e.name}")
}
}
}
}
fun startScreenShare() {
val manager = getSystemService(MediaProjectionManager::class.java)
screenShareLauncher.launch(manager.createScreenCaptureIntent())
}
disableScreenShare()
suspend fun disableScreenShare()
- Stops sharing your screen and stops the screen-capture foreground service.
- Every participant receives
onStreamDisabledwith theRTCStreamthat stopped. - Every participant receives
onPresenterChanged()withnull.
Returns
Unit
Throws
VideoSDKException with 3054 (ERROR_ALREADY_IN_REQUESTED_STATE) when nothing is being shared, 3055 (ERROR_OPERATION_IN_PROGRESS) while another screen-share call is running, or 3066 (ERROR_ACTION_NOT_SUPPORTED_IN_MODE) in RECV_ONLY or SIGNALLING_ONLY mode.
uploadBase64File()
suspend fun uploadBase64File(base64Data: String, token: String, fileName: String): String
- Uploads a file to VideoSDK's temporary storage and returns its URL. Pass that URL to
fetchBase64File()to get the file back. - The room must be joined.
Parameters
- base64Data:
String- Your file, converted to base64.
- token:
String- Your VideoSDK token. See Authentication and token.
- fileName:
String- The file name, with its extension.
Returns
String- The URL of the uploaded file.
Throws
VideoSDKException with:
3073(ERROR_INVALID_PARAMETER) if any argument is empty.3035or3065if the room is not joined or is reconnecting. These are also reported ononError.3074(ERROR_FILE_UPLOAD_FAILED) if the upload fails. The same error is also reported ononError.
Example
private fun uploadFile() {
val base64Data = "<Your File's base64>" // Convert your file to base64 and pass here
val token = "<VIDEOSDK_TOKEN>"
val fileName = "myImage.jpeg" // Provide name with extension here
lifecycleScope.launch {
try {
val fileUrl = room.uploadBase64File(base64Data, token, fileName)
Log.d("VideoSDK", "Uploaded file url: $fileUrl")
} catch (e: VideoSDKException) {
Log.d("VideoSDK", "Error in upload file: ${e.code} ${e.message}")
}
}
}
fetchBase64File()
suspend fun fetchBase64File(url: String, token: String): String
- Downloads a file from VideoSDK's temporary storage and returns it as base64.
- The room must be joined.
Parameters
- url:
String- The URL returned by
uploadBase64File().
- The URL returned by
- token:
String- Your VideoSDK token. See Authentication and token.
Returns
String- The file inbase64format.
Throws
VideoSDKException with:
3073(ERROR_INVALID_PARAMETER) if any argument is empty.3035or3065if the room is not joined or is reconnecting. These are also reported ononError.3075(ERROR_FILE_FETCH_FAILED) if the fetch fails. The same error is also reported ononError.
Example
private fun fetchFile() {
val url = "<Your FileUrl>" // Provide fileUrl which is returned by uploadBase64File()
val token = "<VIDEOSDK_TOKEN>"
lifecycleScope.launch {
try {
val base64 = room.fetchBase64File(url, token)
Log.d("VideoSDK", "Fetched file in base64: $base64")
} catch (e: VideoSDKException) {
Log.d("VideoSDK", "Error in fetch file: ${e.code} ${e.message}")
}
}
}
startRecording()
suspend fun startRecording(
webhookUrl: String? = null,
awsDirPath: String? = null,
config: RecordingConfig? = null,
transcription: PostTranscriptionConfig? = null,
)
- Starts recording the room.
- It returns once the request is sent, before anything is recorded.
onRecordingStarted()confirms that the recording is running.
Parameters
-
webhookUrl:
String?- A URL the server calls back about this recording, for example when it starts, stops or fails. See Webhooks and Events.
nullfor none. OPTIONAL, defaultnull
- A URL the server calls back about this recording, for example when it starts, stops or fails. See Webhooks and Events.
-
awsDirPath:
String?- The folder in the storage bucket configured for your account. See Configure Recording Storage.
nullstores the recording with VideoSDK. OPTIONAL, defaultnull
- The folder in the storage bucket configured for your account. See Configure Recording Storage.
-
config:
RecordingConfig?(packagelive.videosdk.rtc.android.model)- Leave
config, or any of its fields,nullto use the defaults.modedefaults to video and audio, andqualityapplies only to video and audio. - layout:
ConfigLayout?- type:
ConfigLayoutGrid?—GRID|SPOTLIGHT|SIDEBAR - priority:
ConfigLayoutPriority?—SPEAKER|PIN - gridSize:
Int?max 4
- type:
- theme:
ConfigTheme?—DARK|LIGHT|DEFAULT - mode:
ConfigMode?—VIDEO_AND_AUDIO|AUDIO - quality:
ConfigQuality?—LOW|MED|HIGH - orientation:
ConfigOrientation?—LANDSCAPE|PORTRAIT OPTIONAL, defaultnull
- Leave
-
transcription:
PostTranscriptionConfig?- Transcribes the recording after the call, with an optional summary.
nullskips it. - enabled:
Boolean - summary:
SummaryConfig?, defaultnull- enabled:
Boolean - prompt:
String?, defaultnull
- enabled:
- modelId:
String?, defaultnull OPTIONAL, defaultnull
- Transcribes the recording after the call, with an optional summary.
Returns
Unit
Throws
VideoSDKException with 4011 (START_RECORDING_FAILED) when the server refuses the start, for example while a recording is already starting or running. The exception carries the server's reason, and the same error is reported on onError.
Events associated with startRecording():
- Every participant receives
onRecordingStateChanged()for each step. - Every participant receives
onRecordingStarted()once the recording is running.
Example
val webhookUrl = "https://webhook.your-api-server.com"
val config = RecordingConfig(
layout = ConfigLayout(
type = ConfigLayoutGrid.SPOTLIGHT,
priority = ConfigLayoutPriority.PIN,
gridSize = 4,
),
theme = ConfigTheme.DARK,
)
val prompt = "Write summary in sections like Title, Agenda, Speakers, Action Items, Outlines, Notes and Summary"
val transcription = PostTranscriptionConfig(
enabled = true,
summary = SummaryConfig(enabled = true, prompt = prompt),
modelId = "raman_v1",
)
lifecycleScope.launch {
try {
room.startRecording(webhookUrl = webhookUrl, config = config, transcription = transcription)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Recording failed: ${e.code} ${e.name}")
}
}
stopRecording()
suspend fun stopRecording()
- Stops recording the room.
- It returns once the request is sent.
onRecordingStopped()confirms that the recording has stopped.
Returns
Unit
Throws
VideoSDKException with 4012 (STOP_RECORDING_FAILED) when the server refuses the stop, for example when no recording is running or one is still starting. The exception carries the server's reason, and the same error is reported on onError.
Events associated with stopRecording():
- Every participant receives
onRecordingStateChanged()for each step, andonRecordingStopped()once it has stopped.
Example
lifecycleScope.launch {
try {
room.stopRecording()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Stop recording failed: ${e.code} ${e.name}")
}
}
startLivestream()
suspend fun startLivestream(outputs: List<LivestreamOutput>, config: LivestreamConfig? = null)
- Streams the room to one or more RTMP destinations, such as YouTube Live, Facebook or your own RTMP server.
- It returns once the request is sent.
onLivestreamStarted()confirms that the livestream is running.
Parameters
- outputs:
List<LivestreamOutput>- Each
LivestreamOutput(url, streamKey)is an RTMP ingest URL and its stream key. Pass at least one.
- Each
- config:
LivestreamConfig?(packagelive.videosdk.rtc.android.model)- layout:
ConfigLayout?- type:
ConfigLayoutGrid?—GRID|SPOTLIGHT|SIDEBAR - priority:
ConfigLayoutPriority?—SPEAKER|PIN - gridSize:
Int?max 25
- type:
- theme:
ConfigTheme?—DARK|LIGHT|DEFAULT - recording:
LivestreamRecordingConfig?—LivestreamRecordingConfig(enabled = true)also records the livestream. OPTIONAL, defaultnull. A field leftnulluses the default.
- layout:
Returns
Unit
Events associated with startLivestream():
- Every participant receives
onLivestreamStateChanged()for each step, andonLivestreamStarted()once it is running.
Example
val YOUTUBE_RTMP_URL = "rtmp://a.rtmp.youtube.com/live2"
val YOUTUBE_RTMP_STREAM_KEY = "<STREAM_KEY>"
val outputs = listOf(LivestreamOutput(YOUTUBE_RTMP_URL, YOUTUBE_RTMP_STREAM_KEY))
val config = LivestreamConfig(
layout = ConfigLayout(
type = ConfigLayoutGrid.SPOTLIGHT,
priority = ConfigLayoutPriority.PIN,
gridSize = 9,
),
theme = ConfigTheme.DARK,
)
lifecycleScope.launch {
try {
room.startLivestream(outputs, config)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Livestream failed: ${e.code} ${e.name}")
}
}
stopLivestream()
suspend fun stopLivestream()
- Stops the livestream.
- It returns once the request is sent.
onLivestreamStopped()confirms that the livestream has stopped.
Returns
Unit
Events associated with stopLivestream():
- Every participant receives
onLivestreamStateChanged()for each step.
Example
lifecycleScope.launch {
try {
room.stopLivestream()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Stop livestream failed: ${e.code} ${e.name}")
}
}
startHLS()
suspend fun startHLS(config: HLSConfig? = null, transcription: PostTranscriptionConfig? = null)
- Publishes the room as an HLS stream, for viewers who only watch and never join the room, such as a webinar audience.
- It returns once the request is sent. Don't start your player on
HLS_STARTED: wait untilonHlsStateChanged()reportsHLSState.HLS_PLAYABLE, then play itshlsUrl.playbackHlsUrl.
Parameters
-
config:
HLSConfig?(packagelive.videosdk.rtc.android.model)- Leave
config, or any of its fields,nullto use the defaults.modedefaults to video and audio, andqualityapplies only to video and audio. - layout:
ConfigLayout?- type:
ConfigLayoutGrid?—GRID|SPOTLIGHT|SIDEBAR - priority:
ConfigLayoutPriority?—SPEAKER|PIN - gridSize:
Int?max 25
- type:
- theme:
ConfigTheme?—DARK|LIGHT|DEFAULT - mode:
ConfigMode?—VIDEO_AND_AUDIO|AUDIO - quality:
ConfigQuality?—LOW|MED|HIGH - orientation:
ConfigOrientation?—LANDSCAPE|PORTRAIT OPTIONAL, defaultnull
- Leave
-
transcription:
PostTranscriptionConfig?- Transcribes the HLS stream after it ends, with an optional summary.
nullskips it. - enabled:
Boolean - summary:
SummaryConfig?, defaultnull- enabled:
Boolean - prompt:
String?, defaultnull
- enabled:
- modelId:
String?, defaultnull OPTIONAL, defaultnull
- Transcribes the HLS stream after it ends, with an optional summary.
Returns
Unit
Events associated with startHLS():
- Every participant receives
onHlsStateChanged()for each step:HLS_STARTING,HLS_STARTED, thenHLS_PLAYABLE.
Example
val config = HLSConfig(
layout = ConfigLayout(
type = ConfigLayoutGrid.SPOTLIGHT,
priority = ConfigLayoutPriority.PIN,
gridSize = 9,
),
orientation = ConfigOrientation.PORTRAIT,
theme = ConfigTheme.DARK,
)
val prompt = "Write summary in sections like Title, Agenda, Speakers, Action Items, Outlines, Notes and Summary"
val transcription = PostTranscriptionConfig(
enabled = true,
summary = SummaryConfig(enabled = true, prompt = prompt),
modelId = "raman_v1",
)
lifecycleScope.launch {
try {
room.startHLS(config, transcription)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "HLS failed: ${e.code} ${e.name}")
}
}
stopHLS()
suspend fun stopHLS()
- Stops the HLS stream.
- It returns once the request is sent.
onHlsStateChanged()reportsHLSState.HLS_STOPPEDonce HLS has stopped.
Returns
Unit
Events associated with stopHLS():
- Every participant receives
onHlsStateChanged()withHLS_STOPPING, thenHLS_STOPPED.
Example
lifecycleScope.launch {
try {
room.stopHLS()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Stop HLS failed: ${e.code} ${e.name}")
}
}
startTranscription()
suspend fun startTranscription(config: TranscriptionConfig? = null)
- Starts realtime transcription of everyone in the room, not just you.
- Text arrives on
onTranscriptionText(). The same sentence can arrive more than once as the service refines it.
Parameters
- config:
TranscriptionConfig?nulluses the service's defaults. Every property is optional:- webhookURL:
String?— a URL the service sends the finished transcript to. Live text arrives ononTranscriptionText()either way. - summary:
SummaryConfig?—SummaryConfig(enabled, prompt). Withenabled = true, a summary is produced after transcription stops.prompttells it how to write the summary. - modelConfig:
JSONObject?— settings for the transcription model, sent to the service unchanged.
- webhookURL:
OPTIONAL, defaultnull
Returns
Unit
Events associated with startTranscription():
- Every participant receives
onTranscriptionStateChanged()as transcription starts. - Every participant receives
onTranscriptionText()for each piece of text.
Example
// Realtime Transcription Configuration
val webhookUrl = "https://www.example.com"
val summaryConfig = SummaryConfig(
enabled = true,
prompt = "Write summary in sections like Title, Agenda, Speakers, Action Items, Outlines, Notes and Summary"
)
val transcriptionConfig = TranscriptionConfig(
webhookURL = webhookUrl,
summary = summaryConfig
)
lifecycleScope.launch {
try {
room.startTranscription(transcriptionConfig)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Transcription failed: ${e.code} ${e.name}")
}
}
stopTranscription()
suspend fun stopTranscription()
- Stops realtime transcription for the room.
Returns
Unit
Events associated with stopTranscription():
- Every participant receives
onTranscriptionStateChanged()as transcription stops.
Example
lifecycleScope.launch {
try {
room.stopTranscription()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Stop transcription failed: ${e.code} ${e.name}")
}
}
startTranslation()
suspend fun startTranslation(config: JSONObject? = null)
- Starts realtime translation for the room.
- Each participant receives only the language they asked for, set in
createRoom()withtranslationLanguageor withchangeTranslationLanguage(). One device never receives two languages at once.
Parameters
- config:
JSONObject?- Translation settings, sent to the server unchanged.
nulluses the defaults. OPTIONAL
- Translation settings, sent to the server unchanged.
Returns
Unit
Events associated with startTranslation():
- Every participant receives
onTranslationStateChanged()as translation starts. - Each participant receives
onTranslationText()in their own language.
Example
lifecycleScope.launch {
try {
room.startTranslation()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Translation failed: ${e.code} ${e.name}")
}
}
stopTranslation()
suspend fun stopTranslation()
- Stops realtime translation for every participant in the room, not just you.
Returns
Unit
Events associated with stopTranslation():
- Every participant receives
onTranslationStateChanged()as translation stops.
changeTranslationLanguage()
suspend fun changeTranslationLanguage(language: String)
- Changes the language you receive translations in. Other participants keep their own language.
- Translation must already be running: call
startTranslation()first. - The new language takes effect when
onTranslationLanguageChanged()reports the change. Until then,onTranslationText()keeps the old language.
Parameters
- language:
String- The language code, such as
"hi".
- The language code, such as
Returns
Unit
Throws
VideoSDKException with 4044 (CHANGE_TRANSLATION_LANGUAGE_FAILED) when the server refuses the change, for example before translation has started.
Example
lifecycleScope.launch {
try {
room.changeTranslationLanguage("hi")
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Language change failed: ${e.code} ${e.name}")
}
}
startWhiteboard()
suspend fun startWhiteboard()
- Opens a shared whiteboard for the room.
- Every participant receives
onWhiteboardStarted()with the URL to load in aWebView.
Returns
Unit
Throws
VideoSDKException with:
4056(WHITEBOARD_OPERATION_IN_PROGRESS) while anotherstartWhiteboard()orstopWhiteboard()call is running.4054(START_WHITEBOARD_FAILED) when the server refuses, for example when the whiteboard is already open. It is also reported ononError.
stopWhiteboard()
suspend fun stopWhiteboard()
- Closes the shared whiteboard for everyone.
- Every participant receives
onWhiteboardStopped().
Returns
Unit
Throws
VideoSDKException with:
4056(WHITEBOARD_OPERATION_IN_PROGRESS) while anotherstartWhiteboard()orstopWhiteboard()call is running.4055(STOP_WHITEBOARD_FAILED) when the server refuses, for example when no whiteboard is open. It is also reported ononError.
changeMode()
suspend fun changeMode(mode: Mode)
-
Changes your mode in the room.
-
Pass one of these
Modevalues:-
Mode.SEND_AND_RECV: Audio and video are both sent and received. -
Mode.SIGNALLING_ONLY: Audio and video are neither sent nor received, but the participant stays in the room. -
Mode.RECV_ONLY: Audio and video are received but not sent.
-
-
After a change to
SEND_AND_RECV, the mic and camera come back as they were just before the mode change: a device that was on turns back on, and one you had turned off stays off. For a participant who joined inRECV_ONLYorSIGNALLING_ONLY,micEnabledandcamEnabledfromcreateRoom()decide. Don't callunmuteMic()orenableCam()for a device that comes back on by itself: the call can throw3054.
Parameters
- mode:
Mode
Returns
Unit
Throws
VideoSDKException with 3054 (ERROR_ALREADY_IN_REQUESTED_STATE) when you are already in that mode, or 3055 (ERROR_OPERATION_IN_PROGRESS) while another changeMode() is running. Neither is reported on onError. A change that is refused also throws, with the refusal's code. A refusal from the server is reported on onError too, with 4065 (CHANGE_MODE_FAILED) or the server's code.
Events associated with changeMode():
- Every participant receives
onParticipantModeChanged(). - The local participant receives
onStreamEnabledoronStreamDisabledfor each stream the new mode adds or removes. - If the mode change ends your screen share, every participant receives
onPresenterChanged()withnull.
Example
lifecycleScope.launch {
try {
room.changeMode(Mode.SIGNALLING_ONLY)
} catch (e: VideoSDKException) {
// 3054: already in this mode, 3055: another change is running
Log.e("VideoSDK", "Mode change failed: ${e.code} ${e.name}")
}
}
respondEntry()
suspend fun respondEntry(participantId: String, decision: EntryDecision)
- Lets a waiting participant in, or turns them away.
- It answers a request delivered on
onEntryRequested.onEntryRespondedreports the decision. - You can also answer with the
allow()anddeny()callbacks thatonEntryRequestedpasses.
Parameters
- participantId:
String- The
participantIdfromonEntryRequested.
- The
- decision:
EntryDecisionEntryDecision.ALLOWEDorEntryDecision.DENIED.
Returns
Unit
Example
val participantId = "<participantId from onEntryRequested>"
lifecycleScope.launch {
try {
room.respondEntry(participantId, EntryDecision.ALLOWED)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "respondEntry failed: ${e.code} ${e.name}")
}
}
changeMic()
suspend fun changeMic(deviceId: String? = null)
suspend fun changeMic(customMicrophoneTrack: CustomStreamTrack)
- Moves your call audio to another device from
VideoSDK.getMics():SPEAKER_PHONE,EARPIECE,WIRED_HEADSETorBLUETOOTH. - Playback and recording move together. A wired or Bluetooth headset records from its own microphone; the speaker and earpiece use the phone's microphone.
- If the mic is on, it is turned off and on again on the new device, so
onStreamDisabledand thenonStreamEnabledfire for every participant. A muted mic stays muted, and the nextunmuteMic()uses the new device. changeMic(customMicrophoneTrack)sends a different microphone track instead. It turns the mic on if it was off. LaterunmuteMic()calls keep the track's audio settings.
Parameters
- deviceId:
String?- The
deviceIdof aMicrophoneDeviceInfofromVideoSDK.getMics(). With nodeviceId, it moves to the next device in that list, wrapping around. OPTIONAL
- The
- customMicrophoneTrack:
CustomStreamTrack- A microphone track from
VideoSDK.createMicrophoneAudioTrack().
- A microphone track from
Returns
Unit
Throws
VideoSDKException with:
3059(ERROR_NO_MICROPHONE_DEVICE_FOUND) whendeviceIdis not inVideoSDK.getMics(). It is also reported ononError.3034(ERROR_MICROPHONE_ACCESS_UNAVAILABLE) when the device list can't be read.3035or3065when the room is not joined or is reconnecting. These are also reported ononError.
If switching to a deviceId fails, the previous device stays selected.
Example
lifecycleScope.launch {
try {
val bluetooth = VideoSDK.getMics().firstOrNull { it.deviceId == "BLUETOOTH" }
if (bluetooth != null) {
room.changeMic(bluetooth.deviceId)
}
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Mic change failed: ${e.code} ${e.name}")
}
}
changeCam()
suspend fun changeCam(deviceId: String? = null)
suspend fun changeCam(customCameraTrack: CustomStreamTrack)
- Switches your camera to another one from
VideoSDK.getCameras(). - If the camera is on, it is turned off and on again on the new camera, so
onStreamDisabledand thenonStreamEnabledfire for every participant. A camera that is off stays off, and the nextenableCam()uses the new camera. changeCam(customCameraTrack)sends a different camera track instead. It turns the camera on if it was off. LaterenableCam()calls keep the track's settings.
Parameters
- deviceId:
String?- The
deviceIdof a camera fromVideoSDK.getCameras(). With nodeviceId, it moves to the next camera in that list, wrapping around. To pick the front or back camera, pass thedeviceIdof the camera with thatfacingMode. OPTIONAL
- The
- customCameraTrack:
CustomStreamTrack- A camera track from
VideoSDK.createCameraVideoTrack().
- A camera track from
Returns
Unit
Throws
VideoSDKException with:
3051(ERROR_NO_WEBCAM_DEVICE_FOUND) when no camera has thatdeviceId. It is also reported ononError.3033(ERROR_CAMERA_ACCESS_UNAVAILABLE) when the camera list can't be read.3035or3065when the room is not joined or is reconnecting. These are also reported ononError.
If the camera you pick with deviceId can't be opened, the previous camera stays selected.
Example
lifecycleScope.launch {
try {
val backCamera = VideoSDK.getCameras().firstOrNull { it.facingMode == FacingMode.back }
if (backCamera != null) {
room.changeCam(backCamera.deviceId)
}
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Camera change failed: ${e.code} ${e.name}")
}
}
setWebcamQuality()
suspend fun setWebcamQuality(quality: VideoQuality)
- Caps the quality of the camera video you send. Remote participants receive at most this quality.
VideoQuality.HIGHsends every layer,VideoQuality.MEDIUMstops the largest one andVideoQuality.LOWsends only the smallest. A camera that sends a single layer is not affected.- The cap stays in force after the camera is turned off and on again.
- When the room was created with
dynaCaston, the stream may also drop lower while no remote participant is watching the larger layers, but never goes above this cap.
Parameters
- quality:
VideoQualityVideoQuality.LOW,VideoQuality.MEDIUMorVideoQuality.HIGH.
Returns
Unit
Throws
VideoSDKException with 3062 (ERROR_WEBCAM_NOT_ENABLED) when the camera is off, or 3063 (ERROR_SET_WEBCAM_QUALITY_FAILED) when the cap can't be applied.
Example
lifecycleScope.launch {
try {
room.setWebcamQuality(VideoQuality.LOW)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Quality change failed: ${e.code} ${e.name}")
}
}
getAppliedVideoConfig()
fun getAppliedVideoConfig(): JSONObject?
- It returns the camera settings in force:
width,height,frameRate,facingMode,codecandapplied. - It is
nulluntil the camera has been opened once.appliedisfalsewhen the camera is set up but not running, for example afterdisableCam(). codecis the codec actually in use, which can differ from the one you asked for.
Returns
JSONObject?
Example
val video: JSONObject? = room.getAppliedVideoConfig()
Log.d("VideoSDK", "Camera: $video")
setVideoEffects()
fun setVideoEffects(config: VideoEffectsConfig): JSONObject
- Changes the camera effects while you are in the room: auto-framing, low-light correction, a style and a colour filter. To set them before joining, use
VideoSDK.setVideoEffects(). - Effects work only with
VideoEffectsConfig.Pipeline.GRAPH. A pipeline change takes effect the next time the camera starts, so turn the camera off and on, or switch cameras, to apply it now. Changes to the effects themselves apply at once. - If the graph can't start on the device,
onErrorreports3080(ERROR_VIDEO_EFFECTS_GRAPH_UNAVAILABLE) once, and the camera stays onPipeline.LEGACY: the call keeps going, but the video is sent without effects. AGRAPHrequest made before any camera has started is not an error. Checkappliedandreasonin the result. AGRAPHrequest made before the graph has ever run returnsappliedfalsewith areasonthat starts withpending:, and starts with the next camera.
Parameters
- config:
VideoEffectsConfig
Returns
JSONObject- The configuration in force, withappliedandreason.
Example
val result: JSONObject = room.setVideoEffects(
VideoEffectsConfig(
pipeline = VideoEffectsConfig.Pipeline.GRAPH,
lighting = VideoEffectsConfig.Lighting(enabled = true),
filter = VideoEffectsConfig.Filter(lut = VideoEffectsConfig.Lut.WARM),
)
)
val reason = result.optString("reason")
if (!result.optBoolean("applied") && !reason.startsWith("pending:")) {
Log.w("VideoSDK", "Effects not applied: $reason")
}
getAppliedVideoEffectsConfig()
fun getAppliedVideoEffectsConfig(): JSONObject
- It returns the video effects configuration in force, with
appliedandreason.
Returns
JSONObject
setVideoProcessor()
fun setVideoProcessor(processor: VideoFrameProcessor)
- It installs a processor that sees every captured camera frame before it is encoded, for an effect or overlay the SDK does not provide.
- It works before
join()too, so a pre-join preview is processed. onFrameCaptured()runs on the camera capture thread, so keep it under one frame interval or frames are dropped.- A new processor replaces the one already installed.
removeVideoProcessor()takes it out, and leaving the room removes it.
Parameters
- processor:
VideoFrameProcessorVideoFrameProcessoris afun interface.onFrameCaptured(videoFrame)returns the frame unchanged, or a new one.
Returns
Unit
Example
room.setVideoProcessor { videoFrame ->
// Return the frame unchanged, or a new one.
videoFrame
}
removeVideoProcessor()
fun removeVideoProcessor()
- It removes the processor set with
setVideoProcessor(). Camera frames are sent unprocessed again. - Calling it with no processor installed does nothing.
Returns
Unit
pauseAllStreams()
suspend fun pauseAllStreams(kind: StreamKind? = null)
- Stops the server sending you remote media of one kind. Unlike hiding the tiles, this saves bandwidth as well as rendering.
- Other participants are not affected. Call
resumeAllStreams()to get the media back.
Parameters
- kind:
StreamKind?StreamKind.AUDIO,StreamKind.VIDEOorStreamKind.SHARE(screen share).nullpauses all three.OPTIONAL, defaultnull
Returns
Unit
Events associated with pauseAllStreams():
- The local participant receives
onPausedAllStreams(), once for each kind.
Example
lifecycleScope.launch {
try {
room.pauseAllStreams()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Pause failed: ${e.code} ${e.name}")
}
}
resumeAllStreams()
suspend fun resumeAllStreams(kind: StreamKind? = null)
- Starts remote media of one kind flowing to you again after
pauseAllStreams().
Parameters
- kind:
StreamKind?StreamKind.AUDIO,StreamKind.VIDEOorStreamKind.SHARE(screen share).nullresumes all three.OPTIONAL, defaultnull
Returns
Unit
Events associated with resumeAllStreams():
- The local participant receives
onResumedAllStreams(), once for each kind.
Example
lifecycleScope.launch {
try {
room.resumeAllStreams()
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Resume failed: ${e.code} ${e.name}")
}
}
enableAdaptiveSubscription()
suspend fun enableAdaptiveSubscription()
- Turns on adaptive subscription. Each video you receive then follows the size you report with
setViewPort()on that participant. - Calling it while adaptive subscription is already on reports
4048(ADAPTIVE_SUBSCRIPTION_ENABLE_FAILED) ononError.
Returns
Unit
disableAdaptiveSubscription()
suspend fun disableAdaptiveSubscription()
- Turns off adaptive subscription. Each video you receive goes back to the quality its sender sends.
- Calling it while adaptive subscription is already off reports an error on
onError. Today that error is4048(ADAPTIVE_SUBSCRIPTION_ENABLE_FAILED), not4049, so don't match on4049for it.
Returns
Unit
requestMediaRelay()
suspend fun requestMediaRelay(
destinationRoomId: String,
token: String? = null,
kinds: List<StreamKind>? = null,
)
- Asks another room to accept your media, so you can be seen and heard there without anyone rejoining.
- The destination room must accept first. It receives
onMediaRelayRequestReceived, and your room receives the answer ononMediaRelayRequestResponse. Once accepted, you getonMediaRelayStarted, and the destination room getsonParticipantJoinedfor you.
Parameters
- destinationRoomId:
String- The ID of the room to relay into.
- token:
String?- A token valid for the destination room.
nulluses this room's token. OPTIONAL, defaultnull
- A token valid for the destination room.
- kinds:
List<StreamKind>?- The kinds to relay:
StreamKind.AUDIO,StreamKind.VIDEOandStreamKind.SHARE(screen share).nullrelays all three. An empty list relays none. OPTIONAL, defaultnull
- The kinds to relay:
Returns
Unit
Throws
VideoSDKException with 3073 (ERROR_INVALID_PARAMETER) when destinationRoomId is empty.
Example
lifecycleScope.launch {
try {
room.requestMediaRelay("<room-id>")
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Relay request failed: ${e.code} ${e.name}")
}
}
stopMediaRelay()
suspend fun stopMediaRelay(destinationRoomId: String)
- Stops relaying your media to one room, for every kind at once.
Parameters
- destinationRoomId:
String- The ID of the room you are relaying into.
Returns
Unit
Throws
VideoSDKException with 3073 (ERROR_INVALID_PARAMETER) when destinationRoomId is empty.
Events associated with stopMediaRelay():
- The local participant receives
onMediaRelayStopped()with the destination room's ID. - The destination room receives
onParticipantLeft()for you, withLeaveReason.REMOVE_PEER_MEDIA_RELAY_STOP.
Example
lifecycleScope.launch {
try {
room.stopMediaRelay("<room-id>")
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Stop relay failed: ${e.code} ${e.name}")
}
}
switchTo()
suspend fun switchTo(roomId: String, token: String? = null)
- Moves you into another room, such as a breakout room, without calling
leave()andjoin(). The sameRoomobject keeps working. onRoomJoinedfires again for the new room, withswitchRoomIdset to its ID.- You always enter the new room in
Mode.SEND_AND_RECV, whatever mode you were in. - Your mic and camera come back on in the new room as they were before the switch. You don't need to call
unmuteMic()orenableCam()again.
Parameters
- roomId:
String- The ID of the room to move to.
- token:
String?- A token valid for the new room.
nulluses this room's token. OPTIONAL, defaultnull
- A token valid for the new room.
Returns
Unit
Throws
VideoSDKException with:
3073(ERROR_INVALID_PARAMETER) whenroomIdis blank.4076(SWITCH_ROOM_FAILED) when the destination room has no live session, for example because nobody has joined it yet. It is not reported ononError, and you stay in the current room, stillCONNECTED.
Example
lifecycleScope.launch {
try {
room.switchTo("<room-id>")
//or
room.switchTo("<room-id>", token)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Switch failed: ${e.code} ${e.name}")
}
}
send()
suspend fun send(message: String, reliable: Boolean = true)
suspend fun send(data: ByteArray, reliable: Boolean = true)
- Sends a text or binary message to everyone else in the room.
- Messages have no topic and are not saved, so participants who join later don't receive them. Use PubSub for messages that need a topic or history.
- Other participants receive the message on
onDataReceived. You don't receive your own message.
Parameters
- message:
Stringor data:ByteArray- Up to 15 KiB.
- reliable:
Booleantrueresends until the message arrives and keeps the order.falsesends it once: faster on a poor network, but it can be lost.OPTIONAL, defaulttrue
Returns
Unit
Throws
VideoSDKException with:
3035or3065when the room is not joined or is reconnecting.3066(ERROR_ACTION_NOT_SUPPORTED_IN_MODE) in a mode that can't send.3072(ERROR_PAYLOAD_TOO_LARGE) when the message is larger than 15 KiB.3048(ERROR_MEDIA_NOT_READY) when there is no connection to send on.3079(ERROR_OPERATION_TIMED_OUT) when the connection never opens, and3070(ERROR_SEND_FAILED) when sending fails.
3035, 3079 and 3070 are also reported on onError.
Example
lifecycleScope.launch {
try {
room.send("Hello everyone")
room.send(byteArrayOf(1, 2, 3), reliable = false)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Send failed: ${e.code} ${e.name}")
}
}
activeRoom()
companion object {
fun activeRoom(): Room?
}
- Called as
Room.activeRoom(). It returns the room that is joined or joining, ornullwhen there is none. - It is set when
join()starts and cleared once the room is left. - Use it from code that holds no
Roomreference.
Returns
Room?
getActivePresenterId()
fun getActivePresenterId(): String?
- It returns the ID of the participant sharing their screen, or
nullwhen nobody is. - It follows
onPresenterChanged.
Returns
String?
getActiveSpeakerId()
fun getActiveSpeakerId(): String?
- It returns the ID of the participant speaking now, or
nullwhen the room is quiet. - It follows
onSpeakerChanged.
Returns
String?
Example
val presenterId: String? = room.getActivePresenterId()
val speakerId: String? = room.getActiveSpeakerId()
Log.d("VideoSDK", "Presenter: $presenterId, speaker: $speakerId")
setAudioDevice()
fun setAudioDevice(device: MicrophoneDeviceInfo)
- It routes call audio to
deviceand notifies the listener set withsetAudioDeviceChangeListener(). - It works before
join()too. - Unlike
changeMic(), it does not restart a live microphone.
Parameters
- device:
MicrophoneDeviceInfo- A device from
VideoSDK.getMics().
- A device from
Returns
Unit
Throws
VideoSDKException with 3059 (ERROR_NO_MICROPHONE_DEVICE_FOUND) when device is not one of the devices VideoSDK.getMics() lists right now. It is also reported on onError. The audio stays routed as it was.
Example
val speaker = VideoSDK.getMics().firstOrNull { it.deviceId == "SPEAKER_PHONE" }
if (speaker != null) {
try {
room.setAudioDevice(speaker)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Audio device change failed: ${e.code} ${e.name}")
}
}
setAudioDeviceChangeListener()
fun setAudioDeviceChangeListener(listener: AudioDeviceChangeEvent?)
- It sets the listener that is called whenever a headset or Bluetooth device is plugged in or removed, or the selected audio device changes.
- It is also called once right away with the current device and list.
- A new listener replaces the previous one, and
nullremoves it. The listener is set for the whole app, not just this room: it stays set afterleave().
Parameters
- listener:
AudioDeviceChangeEvent?AudioDeviceChangeEventis afun interface, so a lambda works.onAudioDeviceChanged(selectedAudioDevice: MicrophoneDeviceInfo?, audioDevices: List<MicrophoneDeviceInfo>)receives the device in use and every available device.selectedAudioDeviceisnullonly when no audio device is available, which is always the case beforeVideoSDK.initialize().
Returns
Unit
Example
room.setAudioDeviceChangeListener { selectedAudioDevice, audioDevices ->
Toast.makeText(
this@MainActivity,
"Selected AudioDevice: ${selectedAudioDevice?.label} (${audioDevices.size} available)",
Toast.LENGTH_SHORT
).show()
}
addEventListener()
fun addEventListener(listener: RoomEventListener)
- Adds a
RoomEventListener. Add it before you calljoin(), or you can miss the join and the first participants. - You can add more than one listener, and each one is called. The same listener added twice is called twice.
- Events reach your listeners on the main thread. Override only the events you need: every method has an empty default.
Parameters
- listener: RoomEventListener
Returns
Unit
removeEventListener()
fun removeEventListener(listener: RoomEventListener)
- Removes a listener you added. A listener that was never added is ignored.
Parameters
- listener: RoomEventListener
Returns
Unit
removeAllEventListeners()
fun removeAllEventListeners()
- Removes every
RoomEventListeneryou added.
Returns
Unit
Got a Question? Ask us on discord

