Kotlin Multiplatform Secure Room Operations
Build a complete MediaSFU room lifecycle with com.mediasfu:mediasfu-sdk:1.0.5: create or join through your backend, show participants, control microphone, camera, and screen sharing, observe remote media, and leave cleanly.
This guide uses the supplied MediasfuGeneric room and its MediasfuGenericState. It never puts a reusable MediaSFU credential in the application.
Before you start
Add the exact dependency to commonMain through the artifact source approved for your application, and confirm that Gradle resolves 1.0.5:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("com.mediasfu:mediasfu-sdk:1.0.5")
}
}
}
Your application also needs:
- an authenticated HTTPS backend that creates and joins rooms for the signed-in user;
- Android camera and microphone permissions before requesting those devices;
NSCameraUsageDescriptionandNSMicrophoneUsageDescriptionin every iOS target that uses them;- a real-device release test for camera, microphone, audio routing, backgrounding, and display capture on every platform you ship.
The backend decides which users may create or join, applies room type, duration and capacity limits, rate-limits requests, and keeps reusable MediaSFU credentials in server-side storage. Start with the secure backend pattern.
Inject secure create and join functions
MediasfuGenericOptions accepts createMediaSFURoom and joinMediaSFURoom. The SDK supplies a typed request to each callback. Forward only request.payload to your own backend:
import com.mediasfu.sdk.methods.utils.CreateJoinRoomResult
import com.mediasfu.sdk.methods.utils.CreateMediaSFUOptions
import com.mediasfu.sdk.methods.utils.CreateMediaSFURoomOptions
import com.mediasfu.sdk.methods.utils.JoinMediaSFUOptions
import com.mediasfu.sdk.methods.utils.JoinMediaSFURoomOptions
import com.mediasfu.sdk.ui.mediasfu.MediasfuGenericOptions
interface RoomBackend {
suspend fun create(payload: CreateMediaSFURoomOptions): CreateJoinRoomResult
suspend fun join(payload: JoinMediaSFURoomOptions): CreateJoinRoomResult
}
class SecureRoomGateway(private val backend: RoomBackend) {
suspend fun create(request: CreateMediaSFUOptions): CreateJoinRoomResult =
backend.create(request.payload)
suspend fun join(request: JoinMediaSFUOptions): CreateJoinRoomResult =
backend.join(request.payload)
}
fun secureRoomOptions(backend: RoomBackend): MediasfuGenericOptions {
val gateway = SecureRoomGateway(backend)
return MediasfuGenericOptions(
credentials = null,
createMediaSFURoom = gateway::create,
joinMediaSFURoom = gateway::join,
)
}
Do not forward request.apiKey or request.apiUserName. Do not put those values in source, Gradle properties packaged with the app, platform resources, logs, or crash reports.
Your RoomBackend implementation authenticates the current app session, calls your create or join endpoint, and maps the response to CreateJoinRoomResult. Return a successful CreateJoinRoomResponse in data when access is granted, or a CreateJoinRoomError when it is denied. Treat an expired app session as a new authorization request; do not replay an old room response.
Render the supplied room with the same options and state instance:
@Composable
fun KotlinRoom(backend: RoomBackend) {
val options = remember(backend) { secureRoomOptions(backend) }
val state = rememberMediasfuGenericState(options)
MediasfuGeneric(options = options, state = state)
}
The supplied pre-join screen calls the create or join adapter, then completes the socket-level room join. The room is authorized when state.validated.value becomes true. A successful backend response by itself is not proof that the socket joined.
For a denied request, show the returned application message and let the user correct the room ID or sign in again. For a timeout or network loss, keep the pre-join screen available and retry only after connectivity returns. Never silently fall back to a client credential.
Read participants and media state
Use one MediasfuGenericState as the source for your custom labels and controls:
data class RoomSnapshot(
val authorized: Boolean,
val devicePrepared: Boolean,
val participantCount: Int,
val microphoneActive: Boolean,
val cameraActive: Boolean,
val screenShareActive: Boolean,
val remoteMediaCount: Int,
)
fun snapshotRoom(state: MediasfuGenericState) = RoomSnapshot(
authorized = state.validated.value,
devicePrepared = state.connectivity.device != null,
participantCount = state.room.participants.size,
microphoneActive = state.media.audioAlreadyOn,
cameraActive = state.media.videoAlreadyOn,
screenShareActive = state.media.screenAlreadyOn || state.media.shareScreenStarted,
remoteMediaCount = state.streams.currentStreams.size +
state.streams.allAudioStreams.size + state.streams.remoteScreenStreams.size,
)
Render participant names from state.room.participants; the list updates as the room receives membership events. Keep the observations separate:
validatedmeans the room session is authorized.- A non-null
connectivity.devicemeans the WebRTC device was prepared. audioAlreadyOnandvideoAlreadyOndescribe local producer state.currentStreams,allAudioStreams, andremoteScreenStreamsdescribe media known to the consumer path.
Do not label the room “media ready” from permission alone. In a two-device test, require the sender's producer flag and observable playback on the receiver.
Toggle microphone, camera, and screen sharing
Use the state methods so the supplied room owns permission prompts, producer setup, and UI alerts:
fun toggleMicrophone(state: MediasfuGenericState) = state.toggleAudio()
fun toggleCamera(state: MediasfuGenericState) = state.toggleVideo()
fun toggleScreenShare(state: MediasfuGenericState) = state.toggleScreenShare()
After a successful microphone or camera action, the matching audioAlreadyOn or videoAlreadyOn flag changes. After screen capture starts, screenAlreadyOn or shareScreenStarted changes; call toggleScreenShare() again to stop it.
If permission is denied, keep the person in the room, name the missing permission, and offer a retry after the operating-system setting changes. If display capture is cancelled, keep microphone and camera controls available and leave the screen state inactive. If another device cannot consume the stream, show a reconnect action instead of claiming the media is live.
Leave and clean up
Participant leave is:
state.exitSession()
Wait for state.validated to become false, then remove your room route, stop app-owned timers or observers, clear cached room identifiers and discard the previous backend response. A new entry must obtain fresh backend authorization. On another connected device, confirm that the departing participant disappears from state.room.participants.
The 1.0.5 state provides semantic host-end behavior through exitSession(endRoomOnHostExit: true) and a host leave-without-ending path
through exitSession(endRoomOnHostExit: false). Label these controls separately,
show them only for the host, and clear app-owned state after the operation. The
SDK operation is a role-aware request; verify the resulting room state on a
second connected device before presenting the room as ended or preserved.
What success looks like
| Operation | Observable success |
|---|---|
| Create or join | validated changes to true after backend authorization and socket join. |
| Participant list | Both devices show the other participant in room.participants. |
| Microphone or camera | The local producer flag changes and the second device receives the media. |
| Consume media | The receiving state gains the expected stream and playback is audible or visible. |
| Screen share | The sharing state changes and the second device displays the selected surface; stopping reverses both. |
| Participant leave | validated changes to false, local state is cleared, and the other device removes the participant. |
Release checklist
- Gradle resolves
com.mediasfu:mediasfu-sdk:1.0.5from your approved source. - Create and join use the authenticated backend adapters and the shipped app contains no reusable MediaSFU credential.
- Denied authorization, expired sessions, invalid room IDs, and network loss have useful recovery paths.
- Camera and microphone permissions work on every shipping target and denial leaves the room usable.
- Two physical devices prove participant updates, microphone, camera, and remote consumption.
- Screen sharing starts, cancels, stops, and recovers on every platform where you expose it.
- Participant leave clears app-owned state and obtains fresh authorization on re-entry.
- Host end and host leave-without-ending are labelled separately and verified on a second connected device.
The companion example contains the complete gateway, state snapshot, controls, and room wrapper. Its local test checks payload-only forwarding and operation-state boundaries without a network, room, or device.