Skip to content

Instantly share code, notes, and snippets.

Last active January 27, 2023 05:29
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
What would you like to do?
Signaling Client WebSocket
// Copyright 2023 Stream.IO, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
class SignalingClient {
private val logger by taggedLogger("Call:SignalingClient")
private val signalingScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private val client = OkHttpClient()
private val request = Request
// opening web socket with signaling server
private val ws = client.newWebSocket(request, SignalingWebSocketListener())
// session flow to send information about the session state to the subscribers
private val _sessionStateFlow = MutableStateFlow(WebRTCSessionState.Offline)
val sessionStateFlow: StateFlow<WebRTCSessionState> = _sessionStateFlow
// signaling commands to send commands to value pairs to the subscribers
private val _signalingCommandFlow = MutableSharedFlow<Pair<SignalingCommand, String>>()
val signalingCommandFlow: SharedFlow<Pair<SignalingCommand, String>> = _signalingCommandFlow
fun sendCommand(signalingCommand: SignalingCommand, message: String) {
logger.d { "[sendCommand] $signalingCommand $message" }
ws.send("$signalingCommand $message")
private inner class SignalingWebSocketListener : WebSocketListener() {
override fun onMessage(webSocket: WebSocket, text: String) {
when {
text.startsWith(SignalingCommand.STATE.toString(), true) ->
text.startsWith(SignalingCommand.OFFER.toString(), true) ->
handleSignalingCommand(SignalingCommand.OFFER, text)
text.startsWith(SignalingCommand.ANSWER.toString(), true) ->
handleSignalingCommand(SignalingCommand.ANSWER, text)
text.startsWith(SignalingCommand.ICE.toString(), true) ->
handleSignalingCommand(SignalingCommand.ICE, text)
private fun handleStateMessage(message: String) {
val state = getSeparatedMessage(message)
_sessionStateFlow.value = WebRTCSessionState.valueOf(state)
private fun handleSignalingCommand(command: SignalingCommand, text: String) {
val value = getSeparatedMessage(text)
logger.d { "received signaling: $command $value" }
signalingScope.launch {
_signalingCommandFlow.emit(command to value)
private fun getSeparatedMessage(text: String) = text.substringAfter(' ')
fun dispose() {
_sessionStateFlow.value = WebRTCSessionState.Offline
enum class WebRTCSessionState {
Active, // Offer and Answer messages has been sent
Creating, // Creating session, offer has been sent
Ready, // Both clients available and ready to initiate session
Impossible, // We have less than two clients connected to the server
Offline // unable to connect signaling server
enum class SignalingCommand {
STATE, // Command for WebRTCSessionState
OFFER, // to send or receive offer
ANSWER, // to send or receive answer
ICE // to send and receive ice candidates
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment