RoomEventListener Class - Android
implementation
RoomEventListeneris an open class, and every method has an empty body. Override only the events you need, and add the listener to theRoomclass using theaddEventListener()method. Add it before you calljoin(), or you can miss the first events.- Callbacks run on the main thread, except
onError, which can arrive on another thread. - If your callback throws an exception, the SDK catches and logs it, and the other listeners still run.
Example
private val roomEventListener = object : RoomEventListener() {
override fun onRoomJoined(switchRoomId: String?) {
Log.d("#room", "onRoomJoined()")
}
}
onRoomJoined()
- Called when the localParticipant successfully joined the room. From here you can enable media, subscribe to PubSub topics and call the other room methods.
- It fires again on the same
Roomafter a reconnect that needed a fresh join, and afterswitchTo(). Don't use it for one-time setup. - After a reconnect or a room switch, the SDK turns your mic and camera back on as they were before. You don't need to call
unmuteMic()orenableCam()again.
Event callback parameters
- switchRoomId:
String?- The ID of the room you switched to with
switchTo(). It isnullon a first join and on a rejoin after reconnecting.
- The ID of the room you switched to with
Example
override fun onRoomJoined(switchRoomId: String?) {
if (switchRoomId == null) {
Log.d("#room", "onRoomJoined()")
} else {
Log.d("#room", "Switched to room $switchRoomId")
}
}
onRoomLeft()
- Called when the localParticipant left the room.
- It fires on every way out:
leave(),end(), being removed, the same participant ID joining from another device, or reconnection giving up. onRoomStateChangedwithDISCONNECTEDalways fires just before it.- The camera and microphone are released before it fires, so you can join again from inside it.
Event callback parameters
- reason:
LeaveReason- The reason why you left. It isMANUAL_LEAVE_CALLEDafterleave(),MEETING_END_APIafter your ownend(), andROOM_CLOSEwhen another participant ended the room. See LeaveReason Enum for all possible values.
Example
override fun onRoomLeft(reason: LeaveReason) {
Log.d("#room", "onRoomLeft() :: reason: ${reason.code} ${reason.message}")
}
onParticipantJoined()
- Called when a new participant joined the room. AI agents arrive here too. The local participant never does.
- Add a
ParticipantEventListenerto the participant here. This event always fires before that participant's firstonStreamEnabled.
Event callback parameters
- participant: Participant - The participant. It is already in
participants.
Example
override fun onParticipantJoined(participant: Participant) {
Log.d("#room", participant.displayName + " joined")
}
onParticipantLeft()
- Called when a joined participant left the room.
Event callback parameters
- participant: Participant
- If this device never saw the participant, it is a stand-in with the right
idand an emptydisplayName.
- If this device never saw the participant, it is a stand-in with the right
- reason:
LeaveReason- The reason why the participant left. It isUNKNOWNwhen the server sent no reason. See LeaveReason Enum for all possible values.
Example
override fun onParticipantLeft(participant: Participant, reason: LeaveReason) {
Log.d("#room", "${participant.displayName} left :: reason: ${reason.message}")
}
onSpeakerChanged()
- Called when the active speaker changes. There is one active speaker at a time; this is not a volume level for each participant.
getActiveSpeakerId()returns the same value later.
Event callback parameters
- activeSpeakerId:
String?- The participant now speaking, ornullwhen nobody is speaking.
Example
override fun onSpeakerChanged(activeSpeakerId: String?) {
Log.d("#room", "Active speaker: $activeSpeakerId")
}
onPresenterChanged()
- Called when any participant starts or stops screen sharing.
getActivePresenterId()returns the same value later.
Event callback parameters
- presenterId:
String?- The participant now presenting, ornullonce sharing has stopped. AchangeMode()that ends an active share also passesnull.
Example
override fun onPresenterChanged(presenterId: String?) {
Log.d("#room", "Presenter: $presenterId")
}
onEntryRequested()
- Called when a new participant who is trying to join the room has the permission
ask_joinin their token. - It is only emitted to the participants in the room who have the permission
allow_joinin their token. - Nothing happens until you answer. Call
allow()ordeny(), or userespondEntry(). If you don't answer, the participant keeps waiting until the server times out the request. - An answer given after the room was left, or while it is reconnecting, is not sent. The reason arrives on
onError.
Event callback parameters
- participantId:
String- The waiting participant's ID. - name:
String- Their display name. It is empty if they joined without one. - allow:
() -> Unit- Lets them in. - deny:
() -> Unit- Turns them away.
Example
override fun onEntryRequested(
participantId: String,
name: String,
allow: () -> Unit,
deny: () -> Unit,
) {
Log.d("#room", "$name wants to join")
// let them in, or call deny() to turn them away
allow()
}
onEntryResponded()
- Called when the server has processed an entry decision.
- It is emitted to the participants in the room who have the permission
allow_joinin their token. - It is also emitted to the participant who requested to join the room.
Event callback parameters
- participantId:
String- The participant the decision was about. - decision:
EntryDecision?-EntryDecision.ALLOWEDorEntryDecision.DENIED. It isnullif the server sent neither.
Example
override fun onEntryResponded(participantId: String, decision: EntryDecision?) {
Log.d("#room", "Entry for $participantId: $decision")
}
onWebcamRequested()
- Called when another participant asks you to turn your camera on. Nothing changes until you answer.
accept()turns your camera on. It is live once your local participant'sonStreamEnabledfires, not whenaccept()returns.reject()sends nothing back. The other participant's request times out.
Event callback parameters
- participantId:
String- The participant who asked. - listener:
WebcamRequestListener- Callaccept()orreject()on it. It is valid for this one request only.
Example
override fun onWebcamRequested(participantId: String, listener: WebcamRequestListener) {
// turn the camera on, or call listener.reject() to decline
listener.accept()
}
onMicRequested()
- Called when another participant asks you to turn your microphone on. Nothing changes until you answer.
accept()turns your microphone on. It is live once your local participant'sonStreamEnabledfires, not whenaccept()returns.reject()sends nothing back. The other participant's request times out.
Event callback parameters
- participantId:
String- The participant who asked. - listener:
MicRequestListener- Callaccept()orreject()on it. It is valid for this one request only.
Example
override fun onMicRequested(participantId: String, listener: MicRequestListener) {
// turn the microphone on, or call listener.reject() to decline
listener.accept()
}
onRecordingStateChanged()
- Called at every step of the room's cloud recording, the intermediate ones included.
recordingStateholds the latest state.
Event callback parameters
- state:
RecordingState
state has the following values. A state this SDK version does not know is not delivered.
RECORDING_STARTING- Recording is in starting phase and hasn't started yet.RECORDING_STARTED- Recording has started successfully.RECORDING_STOPPING- Recording is in stopping phase and hasn't stopped yet.RECORDING_STOPPED- Recording has stopped successfully.
Example
override fun onRecordingStateChanged(state: RecordingState) {
when (state) {
RecordingState.RECORDING_STARTING -> Log.d("onRecordingStateChanged", "Room recording is starting")
RecordingState.RECORDING_STARTED -> Log.d("onRecordingStateChanged", "Room recording is started")
RecordingState.RECORDING_STOPPING -> Log.d("onRecordingStateChanged", "Room recording is stopping")
RecordingState.RECORDING_STOPPED -> Log.d("onRecordingStateChanged", "Room recording is stopped")
}
}
onRecordingStarted()
- Called when the server confirms that recording of the room has started.
Example
override fun onRecordingStarted() {
//
}
onRecordingStopped()
- Called when the server confirms that recording of the room has stopped.
Example
override fun onRecordingStopped() {
//
}
onLivestreamStateChanged()
- Called at every step of the room's livestream, the intermediate ones included.
liveStreamStateholds the latest state.
Event callback parameters
- state:
LiveStreamState
state has the following values. A state this SDK version does not know is not delivered.
LIVESTREAM_STARTING- Livestream is in starting phase and hasn't started yet. AfteronErrorreports4013(START_LIVESTREAM_FAILED), aLIVESTREAM_STARTINGdoesn't mean a start is still in progress.LIVESTREAM_STARTED- Livestream has started successfully.LIVESTREAM_STOPPING- Livestream is in stopping phase and hasn't stopped yet.LIVESTREAM_STOPPED- Livestream has stopped successfully.
Example
override fun onLivestreamStateChanged(state: LiveStreamState) {
when (state) {
LiveStreamState.LIVESTREAM_STARTING -> Log.d("LivestreamStateChanged", "Room livestream is starting")
LiveStreamState.LIVESTREAM_STARTED -> Log.d("LivestreamStateChanged", "Room livestream is started")
LiveStreamState.LIVESTREAM_STOPPING -> Log.d("LivestreamStateChanged", "Room livestream is stopping")
LiveStreamState.LIVESTREAM_STOPPED -> Log.d("LivestreamStateChanged", "Room livestream is stopped")
}
}
onLivestreamStarted()
- Called when the server confirms that the
RTMPlive stream of the room has started.
Example
override fun onLivestreamStarted() {
//
}
onLivestreamStopped()
- Called when the server confirms that the
RTMPlive stream of the room has stopped.
Example
override fun onLivestreamStopped() {
//
}
onHlsStateChanged()
- Called at every step of the room's HLS (HTTP Live Streaming), once per state.
hlsStateholds the latest state.
Event callback parameters
- state:
HLSStateHLS_STARTING- HLS is in starting phase and hasn't started yet.HLS_STARTED- The server accepted the request. The stream can't be played yet.HLS_PLAYABLE- The stream can be played now. The URLs arrive with this state.HLS_STOPPING- HLS is in stopping phase and hasn't stopped yet.HLS_STOPPED- HLS has stopped successfully.
- hlsUrl:
HLSUrl?- The playback URLs. It is set only withHLS_PLAYABLE, so start your player then. It isnullwith every other state, and withHLS_PLAYABLEonly if the server sent no URL.- playbackHlsUrl:
String?- The playlist for viewers. - livestreamUrl:
String?- The playlist that follows the live edge.
- playbackHlsUrl:
Example
override fun onHlsStateChanged(state: HLSState, hlsUrl: HLSUrl?) {
when (state) {
HLSState.HLS_STARTING -> Log.d("onHlsStateChanged", "Room HLS is starting")
HLSState.HLS_STARTED -> Log.d("onHlsStateChanged", "Room HLS is started")
HLSState.HLS_PLAYABLE -> {
Log.d("onHlsStateChanged", "Room HLS is playable now")
val playbackHlsUrl = hlsUrl?.playbackHlsUrl
val livestreamUrl = hlsUrl?.livestreamUrl
}
HLSState.HLS_STOPPING -> Log.d("onHlsStateChanged", "Room HLS is stopping")
HLSState.HLS_STOPPED -> Log.d("onHlsStateChanged", "Room HLS is stopped")
}
}
onTranscriptionStateChanged()
- This event will be triggered at every step of realtime transcription.
- One transcription runs for the whole room, so every participant gets these events.
transcriptionStateholds the latest state.
Event callback parameters
- state:
TranscriptionState
state has the following values. A state this SDK version does not know is not delivered.
TRANSCRIPTION_STARTING- Realtime Transcription is in starting phase and no text is arriving yet.TRANSCRIPTION_STARTED- Realtime Transcription has started, and text arrives ononTranscriptionText.TRANSCRIPTION_STOPPING- Realtime Transcription is in stopping phase. The last lines may still arrive.TRANSCRIPTION_STOPPED- Realtime Transcription has stopped.
Example
override fun onTranscriptionStateChanged(state: TranscriptionState) {
when (state) {
TranscriptionState.TRANSCRIPTION_STARTING -> Log.d("#room", "Transcription is starting")
TranscriptionState.TRANSCRIPTION_STARTED -> Log.d("#room", "Transcription is started")
TranscriptionState.TRANSCRIPTION_STOPPING -> Log.d("#room", "Transcription is stopping")
TranscriptionState.TRANSCRIPTION_STOPPED -> Log.d("#room", "Transcription is stopped")
}
}
onTranscriptionText()
- Called when text arrives from a running realtime transcription. Every participant receives every speaker's text.
- The same utterance can arrive more than once as the service refines it: first as
TranscriptionType.REALTIMElines, then once asTranscriptionType.FULL_SENTENCE. Add a line to a transcript only whengetType()isFULL_SENTENCE.
Event callback parameters
- data: TranscriptionText
- TranscriptionText.getParticipantId(): String - The speaker's participant ID, or
""when the server sent none. - TranscriptionText.getParticipantName(): String - The speaker's display name.
- TranscriptionText.getText(): String - The transcribed text.
- TranscriptionText.getTimestamp():
Long- The service's timestamp for the utterance. It is0when the service sent none. - TranscriptionText.getType():
TranscriptionType?-REALTIMEfor a partial result that can still change,FULL_SENTENCEfor the settled sentence, ornullfor an unknown value.
- TranscriptionText.getParticipantId(): String - The speaker's participant ID, or
Example
override fun onTranscriptionText(data: TranscriptionText) {
// a REALTIME line is a preview that a later line replaces
if (data.getType() != TranscriptionType.FULL_SENTENCE) return
val participantName = data.getParticipantName()
val text = data.getText()
val timestamp = data.getTimestamp()
Log.d("#room", "$participantName: $text $timestamp")
}
onTranslationStateChanged()
- This event will be triggered at every step of realtime translation.
- One translation runs for the whole room. A participant who joins while translation is already running receives
TRANSLATION_STARTEDtoo, before its ownonRoomJoined. Add yourRoomEventListenerbefore you calljoin(), or you miss it. translationStateholds the latest state.
Event callback parameters
- state:
TranslationState
state has the following values. A state this SDK version does not know is not delivered.
TRANSLATION_STARTING- Translation is in starting phase and no text is arriving yet.TRANSLATION_STARTED- Translation has started, and text arrives ononTranslationText.TRANSLATION_STOPPING- Translation is in stopping phase. The last lines may still arrive.TRANSLATION_STOPPED- Translation has stopped.
Example
override fun onTranslationStateChanged(state: TranslationState) {
if (state == TranslationState.TRANSLATION_STARTED) {
Log.d("#room", "Translation is running")
}
}
onTranslationText()
- Called for each utterance translated into the language this participant asked for. Set that language with
translationLanguageinVideoSDK.createRoom()or withchangeTranslationLanguage(). - Only that one language reaches this device.
Event callback parameters
- data: TranslationText
- TranslationText.getParticipantId(): String - The speaker's participant ID.
- TranslationText.getParticipantName(): String - The speaker's display name.
- TranslationText.getParticipantLanguage(): String - The language the speaker spoke, such as
"en". - TranslationText.getLanguage(): String - The language of this text, such as
"hi". - TranslationText.getText(): String - The translated text.
- TranslationText.getTimestamp(): Double - It is
0.0when the service sent none. - TranslationText.getType():
TranslationType?-PARTIALwhile the utterance is still being revised,FULLonce it is final, ornullfor an unknown value. A newPARTIALline replaces the previous one, so add text to a transcript only onFULL.
Example
override fun onTranslationText(data: TranslationText) {
if (data.getType() == TranslationType.FULL) {
Log.d("#room", "${data.getParticipantName()} (${data.getLanguage()}): ${data.getText()}")
}
}
onTranslationLanguageChanged()
- Called when any participant, including the local one, changes the language they receive translations in.
- After you call
changeTranslationLanguage(), the new language applies once this event arrives for your participant ID. Until then,onTranslationTextkeeps delivering the old language. - If the server refuses the change, for example because translation has not started,
changeTranslationLanguage()throwsVideoSDKExceptionwith4044(CHANGE_TRANSLATION_LANGUAGE_FAILED).
Event callback parameters
- participantId:
String- The participant whose language changed. - language:
String- The new language code.
Example
override fun onTranslationLanguageChanged(participantId: String, language: String) {
Log.d("#room", "$participantId now receives translations in $language")
}
onWhiteboardStarted()
- This event will be triggered when the whiteboard is successfully started.
Event callback parameters
- url:
String- Load it in aWebView. It is empty when the server sent no URL, which means there is nothing to show.
Example
override fun onWhiteboardStarted(url: String) {
super.onWhiteboardStarted(url)
//...
}
onWhiteboardStopped()
- This event will be triggered when the whiteboard session is successfully terminated.
Example
override fun onWhiteboardStopped() {
super.onWhiteboardStopped()
//...
}
onError()
- Called when the SDK detects an error or the server reports one for this room.
- Most errors that a
suspendmethod throws are also reported here. - Unlike the other callbacks, it can arrive off the main thread. Post to the main thread before you touch the UI.
Event callback parameters
- error:
VideoSDKError- code:
Int- The error code. See Error Codes. - name:
String- The error name, such asERROR_ROOM_RECONNECTING. It is empty only for a code this SDK version does not know that arrived without a name. - message:
String- A readable reason. It carries the server's message when the server sent one.
- code:
- Compare
errorwith aVideoSDKErrorconstant using==orwhen. Two errors are equal when their codes are equal.
Example
override fun onError(error: VideoSDKError) {
when (error) {
VideoSDKError.ERROR_ROOM_RECONNECTING -> Log.w("#room", "Reconnecting…")
else -> Log.e("#room", "Error ${error.code} (${error.name}): ${error.message}")
}
// onError can arrive off the main thread
runOnUiThread {
Toast.makeText(this@MainActivity, error.message, Toast.LENGTH_SHORT).show()
}
}
onRoomStateChanged()
- Called when the connection state of the room changes.
roomStateholds the latest state. - All available states are:
CONNECTING- The join has started.CONNECTED- Fires together withonRoomJoined, and again when a connection problem is fixed without rejoining.RECONNECTING- The connection dropped and the SDK is retrying. Until it recovers, most methods are refused with3065. While the phone is offline, the SDK waits up to 2 minutes for the network; then the room ends withonRoomLeft(LeaveReason.WEBSOCKET_CONNECTION_ATTEMPTS_EXHAUSTED). See Room Connection Events.DISCONNECTED- The room is over. It fires once, just beforeonRoomLeft.FAILED- The join failed, or reconnecting gave up. It is not retried. After a join fails, callleave(): it releases the room and firesonRoomLeftwithLeaveReason.MANUAL_LEAVE_CALLED. To try again, create a new room withVideoSDK.createRoom()and join that.
Event callback parameters
- roomState:
RoomState
Example
override fun onRoomStateChanged(roomState: RoomState) {
when (roomState) {
RoomState.CONNECTING -> Log.d("#room", "Joining…")
RoomState.RECONNECTING -> Log.d("#room", "Reconnecting…")
RoomState.CONNECTED -> Log.d("#room", "Connected")
RoomState.FAILED -> Log.d("#room", "Could not connect")
RoomState.DISCONNECTED -> Unit
}
}
onExternalCallRinging()
- Called when an incoming phone call starts ringing on the user's device.
- The external-call events only report the call. The SDK does not mute, pause or leave anything on its own.
- They are delivered to the room that was joined most recently.
The four external-call events need the READ_PHONE_STATE permission. Declare it in AndroidManifest.xml and request it at runtime. Without it, they never fire. See External call detection.
Event callback parameters
- callType:
CallType- AlwaysCallType.INCOMING. Android reports no ringing for a call the user dials.
Example
override fun onExternalCallRinging(callType: CallType) {
Log.d("#room", "onExternalCallRinging: User phone is ringing ($callType)")
}
onExternalCallAnswered()
- Called when a phone call starts on the user's device: the user answers an incoming call or dials out.
Event callback parameters
- callType:
CallType-CallType.INCOMINGafteronExternalCallRinging, otherwiseCallType.OUTGOING.
Example
override fun onExternalCallAnswered(callType: CallType) {
Log.d("#room", "onExternalCallAnswered: $callType call started")
}
onExternalCallRejected()
- Called when a ringing call ends without being answered. Android cannot tell a declined call from a missed one, so both arrive here.
Event callback parameters
- callType:
CallType- AlwaysCallType.INCOMING.
Example
override fun onExternalCallRejected(callType: CallType) {
Log.d("#room", "onExternalCallRejected: call was declined or missed")
}
onExternalCallHangup()
- Called when an answered or dialled phone call ends.
Event callback parameters
- callType:
CallType- The same valueonExternalCallAnsweredreported.
Example
override fun onExternalCallHangup(callType: CallType) {
Log.d("#room", "onExternalCallHangup: $callType call ended")
}
onPausedAllStreams()
- Called when the remote streams of one kind stop being delivered to you, after
pauseAllStreams(). pauseAllStreams()with nokindpauses audio, video and share, so this callback fires once for each kind.- A single stream paused with
RTCStream.pause()reports on the participant'sonStreamPausedinstead.
Parameters
kind: Specifies the type of media stream that was paused.- Type:
StreamKind? - Possible values:
StreamKind.AUDIO: Indicates that audio streams have been paused.StreamKind.VIDEO: Indicates that video streams have been paused.StreamKind.SHARE: Indicates that screen-sharing video streams have been paused.null: The server named a kind this SDK version does not know.
- Type:
Example
override fun onPausedAllStreams(kind: StreamKind?) {
Log.d("TAG", "onPausedAllStreams: $kind")
super.onPausedAllStreams(kind)
}
onResumedAllStreams()
- Called when the remote streams of one kind are delivered to you again, after
resumeAllStreams(). - Like
onPausedAllStreams, it fires once for each kind.
Parameters
kind: Specifies the type of media stream that was resumed.- Type:
StreamKind? - Possible values:
StreamKind.AUDIO: Indicates that audio streams have been resumed.StreamKind.VIDEO: Indicates that video streams have been resumed.StreamKind.SHARE: Indicates that screen-sharing video streams have been resumed.null: The server named a kind this SDK version does not know.
- Type:
Example
override fun onResumedAllStreams(kind: StreamKind?) {
Log.d("TAG", "onResumedAllStreams: $kind")
super.onResumedAllStreams(kind)
}
onParticipantModeChanged()
This event is triggered when any participant's mode changes, including a change the server made.
The new mode is one of the following:
-
SEND_AND_RECV: Audio and video are both sent and received. -
SIGNALLING_ONLY: Audio and video are neither sent nor received, but the participant stays in the room. -
RECV_ONLY: Audio and video are received but not sent.
The participant's mode property is already updated when this event fires.
Event Callback Parameters
- participantId:
String- The participant whose mode changed. - mode:
Mode- Their new mode.
Example
override fun onParticipantModeChanged(participantId: String, mode: Mode) {
Log.d("#room", "$participantId is now in $mode mode")
}
onPinStateChanged()
- This event will be triggered when any participant is pinned or unpinned by any participant.
- The participant's
pinStateproperty is already updated when this event fires.
Event callback parameters
- participantId:
String- The participant who was pinned or unpinned. - state:
ParticipantPinState- Which of their streams are pinned now.- cam:
Boolean-truewhile the camera is pinned. - share:
Boolean-truewhile the screen share is pinned. - Both
falsemeans the participant is unpinned.
- cam:
- pinnedBy:
String?- The participant who made the change, ornullwhen the server did not say.
Example
override fun onPinStateChanged(participantId: String, state: ParticipantPinState, pinnedBy: String?) {
Log.d("onPinStateChanged", "$participantId cam=${state.cam} share=${state.share} by $pinnedBy")
}
onMediaRelayRequestReceived()
- Called when a request is received for media relay in the destination room.
- Nothing is relayed until you answer with
accept()orreject(). - Both need a joined room. If the room has ended or is reconnecting when you answer, the answer is not sent and the reason arrives on
onError.
Event callback parameters
-
participantId - (String): Specifies the participantId who requested the media relay. -
roomId - (String): The room the request comes from. -
displayName - (String): Specifies the display name of the participant who requested the media relay. -
accept - (() -> Unit): Approves the request. The requesting participant's media then arrives in this room as a participant: you getonParticipantJoinedfor them.onMediaRelayStartedis not called in this room; only the requester gets it. -
reject - (() -> Unit): Denies the media relay request. The requesting room is told.
Example
override fun onMediaRelayRequestReceived(
participantId: String,
roomId: String,
displayName: String,
accept: () -> Unit,
reject: () -> Unit,
) {
Log.d("MediaRelay", "$displayName wants to relay media from $roomId")
// approve the request, or call reject() to deny it
accept()
}
onMediaRelayRequestResponse()
- Called when a response is received for media relay request in the source room.
- An accepted request is followed by
onMediaRelayStartedon your side. A rejected one is followed by nothing.
Event callback parameters
-
decision - (RelayDecision?):RelayDecision.ACCEPTEDorRelayDecision.REJECTED. It isnullif the server sent neither. -
decidedBy - (String): Specifies the participantId who responded to the media relay request. -
roomId - (String): Specifies the room that answered. It is empty when the server did not say.
Example
override fun onMediaRelayRequestResponse(decision: RelayDecision?, decidedBy: String, roomId: String) {
super.onMediaRelayRequestResponse(decision, decidedBy, roomId)
Log.d("MediaRelay", "Room: $roomId, Decided by: $decidedBy, Decision: $decision")
}
onMediaRelayStarted()
- Called on the participant who requested the relay, once the destination room has accepted and the relay is running.
- The destination room doesn't get it. There, the relayed participant arrives on
onParticipantJoined.
Parameters
roomId - (String): The destination room.
Example
override fun onMediaRelayStarted(roomId: String) {
super.onMediaRelayStarted(roomId)
Log.d("MediaRelay", "Media relay started with room ID: $roomId")
}
onMediaRelayStopped()
- Called on the participant who requested the relay when it stops, for any reason.
- The destination room doesn't get it. There, the relayed participant leaves on
onParticipantLeftwithLeaveReason.REMOVE_PEER_MEDIA_RELAY_STOP.
Parameters
-
roomId - (String): The destination room. -
reason - (String?): The server's reason for the stop, for example"Requested by sdk."afterstopMediaRelay(). It isnullwhen the server sent none.
Example
override fun onMediaRelayStopped(roomId: String, reason: String?) {
super.onMediaRelayStopped(roomId, reason)
Log.d("MediaRelay", "Media relay stopped for room ID: $roomId, Reason: $reason")
}
onMediaRelayError()
- Called when the media relay fails.
- An error is not always followed by
onMediaRelayStopped, so clear your relay UI here too.
Parameters
-
roomId - (String): Specifies the room the media relay connected. -
error - (String): Specifies the error that occurred. It is empty when the server sent none.
Example
override fun onMediaRelayError(roomId: String, error: String) {
super.onMediaRelayError(roomId, error)
Log.e("MediaRelay", "Media relay error for room ID: $roomId, Error: $error")
}
onCodecChanged()
- Called when the video you send was switched to VP8 because a remote participant could not decode the codec you were using.
- It fires only while your camera is sending, and only when
codecSwitchEnabledinVideoSDK.createRoom()istrue, which is the default. - A local encoder problem does not fire this event. It is reported on
onErrorwith3045instead.
Event callback parameters
- info:
CodecChangeInfo- unsupportedRemoteCodec:
VideoCodec?- The codec the remote participant could not decode. - currentCodec:
VideoCodec- The codec you send now. It is alwaysVP8. - previousCodec:
VideoCodec?- The codec you sent before. - kind:
String- Always"video". - causedBy:
Participant?- The participant that could not decode. It isnullwhen the switch happened at join or when that participant is no longer in the room.
- unsupportedRemoteCodec:
Example
override fun onCodecChanged(info: CodecChangeInfo) {
Log.d("#room", "Video codec changed from ${info.previousCodec} to ${info.currentCodec}")
}
onQualityLimitation()
- Called when the media you send has to be reduced to fit the device's CPU or the network, and again when the limit clears.
- Each type is tracked on its own, so more than one can be active at a time.
- Quality problems on the media you receive are not reported here.
Event callback parameters
- type:
QualityLimitationType-CPU,BANDWIDTHorCONGESTION. - state:
QualityLimitationState-DETECTEDwhen the limit starts,RESOLVEDwhen it clears. - timestamp:
Long- When the change was detected, in epoch milliseconds.
Example
override fun onQualityLimitation(
type: QualityLimitationType,
state: QualityLimitationState,
timestamp: Long,
) {
if (state == QualityLimitationState.DETECTED) {
Log.w("#room", "Sending quality is limited by $type at $timestamp")
} else {
Log.d("#room", "$type limitation resolved")
}
}
onDataReceived()
- Called for each message another participant sends with
send(). - You don't receive your own messages. Messages have no topic and are not replayed to participants who join later.
Event callback parameters
- data:
DataMessage- senderId:
String- The participant who sent the message. - isBinary:
Boolean-truefor bytes,falsefor text. - text:
String?- The text, ornullwhenisBinaryistrue. - data:
ByteArray?- The bytes, ornullwhenisBinaryisfalse. - reliable:
Boolean-truewhen the message was sent reliably,falsewhen it was sent without delivery guarantees. - timestamp:
Long- When this device received the message, in epoch milliseconds.
- senderId:
Example
override fun onDataReceived(data: DataMessage) {
if (data.isBinary) {
Log.d("#room", "${data.senderId} sent ${data.data?.size ?: 0} bytes")
} else {
Log.d("#room", "${data.senderId}: ${data.text}")
}
}
Got a Question? Ask us on discord

