SDKs
Android SDK reference: methods, events, options and errors
Every StreamClient method, property and event in the VoltKit RTC Android SDK, its options, RemoteTrack, and each error code with what to do about it.
The complete surface of in.voltkit:voltkit-rtc 0.9.4. For how to use it, start with the Android SDK page and the voice call and audio room guides.
Errors
Every call fails with RpcError (code, message, data). Decide on
code, log message. The codes are the same in every VoltKit SDK; info
(from the shared registry) says whether a code is terminal (retrying cannot
help) or retryable.
try {
client.publish(PublishOptions(video = false))
} catch (e: RpcError) {
when (e.code) {
"media-unavailable" -> askForPermission(e.data?.get("permission"))
"forbidden" -> showListenerOnly()
"media-disabled" -> showNotAllowed()
"closed" -> startNewCall()
else -> if (e.info?.retryable == true) retryLater() else showError(e.message)
}
}
scope.launch { client.onError.collect { e -> log("voltkit ${e.code}: ${e.message}") } }
onError carries every problem that is not the answer to a call you made:
quality warnings, a subscription or republish that failed, a microphone or
camera failure, and the endings replaced and reconnect-gave-up.
onPublishWarning repeats the quality warnings as text; onSubscribeError
the failed subscriptions with their producer.
Codes this SDK raises itself:
| Code | When |
|---|---|
closed | the client was closed (or the session ended); create a new one |
disconnected | the connection dropped while the call waited; the SDK reconnects. A publish() cut off this way is kept and done again after the rejoin |
timeout | the server did not answer in 15 s |
connect-failed | the WebSocket could not be opened (no network, wrong URL, a proxy); data["status"] for an HTTP answer |
media-unavailable | no permission (data["permission"]), a media call before connect(), a microphone that failed, no video codec on this room's media server |
media-disabled | the project switched that kind of media off |
forbidden | your role cannot do that (a listener publishing) |
force-muted | a moderator holds that device off |
unpublished | unpublish(), stopMusic(), unpublishScreen() or stop() ran while that start was under way |
produce-failed | the media library did not deliver a producer (see logcat) |
consume-failed | a remote track could not be subscribed to (onSubscribeError); the rest of the room works |
no-camera | the device reports no camera |
camera-failed | the camera could not start, or could not switch |
music-unavailable | in-app music is not possible on this WebRTC build |
audio-quality-not-allowed | HD audio in a project limited to standard audio |
unknown-producer | setReceiveQuality named a video you do not receive |
quality-unavailable | that publisher describes no video layers; use automatic |
screen-share-active | a screen share is already running |
screen-share-failed | screen capture could not start |
screen-share-service-required | start a foreground service of type mediaProjection first |
bad-params | an argument is not valid (an unknown device id, a frame rate other than 30 or 60) |
internal | the server answered with something malformed |
replaced | (on onError) this identity connected elsewhere; the session is closed |
reconnect-gave-up | (on onError) every reconnect attempt failed; the session is closed |
Warnings on onError (publishing goes on): codec-fallback,
degradation-preference-failed, frame-rate-fallback,
frame-rate-not-allowed, video-quality-not-allowed,
audio-quality-not-allowed, video-subscription-limit.
Codes from the server you may meet: bad-token (expired or wrong token: mint
a new one), no-room and room-ended, kicked, quota-exhausted,
daily-limit-reached, monthly-limit-reached and tenant-suspended (the
account), participant-quota / viewer-quota, slot-taken, slot-blocked,
slot-occupied, chat-muted, rate-limited, source-already-published,
token-mismatch (updateToken with a token for another room or identity).
Every code: Errors.
API reference
StreamClient
StreamClient(context, wsUrl, token, options = StreamOptions()); also
StreamClient(context, wsUrl, token, connection: ConnectionOptions).
| Lifecycle | |
|---|---|
connect(metadata) | join; returns JoinResult. Collect flows first |
close() | leave and release everything; idempotent |
updateToken(token) | a newer token from your backend; returns its expiry (ms) |
preflight(context, wsUrl, token, audio, video, durationMillis) | (companion) a pre-call check |
| Publishing | |
|---|---|
publish(options) | microphone and/or camera (PublishOptions(audio, video, camera, encoding, audioQuality)) |
unpublish() | stop and release microphone, camera, music and screen |
setMicEnabled(on) / setCamEnabled(on) | mute / camera off without releasing |
switchCamera() | front and back, same track |
setAudioQuality(q) / setVideoQuality(q) / setVideoFrameRate(fps) | change while live |
prioritizePerformance(on) / setPublishFallback(on) | lighten an overloaded or starved publisher |
publishMusic(source, volume) / setMusicEnabled(on) / setMusicVolume(v) / stopMusic() | in-app music |
publishScreen(consent, options) / unpublishScreen() | screen sharing |
| Receiving | |
|---|---|
attachVideo(track, renderer, cover) | draw a remote video with adaptive stream; returns a VideoAttachment |
setVideoView(producerId, VideoView) / clearVideoView(producerId) | report a view yourself |
setReceiveQuality(producerId, quality) | a quality for one remote video; null is automatic |
setPreferredLayers(consumerId, spatial, temporal) | cap one subscription's layer |
setAudioOnlyFallback(on) | pause remote cameras when the audio breaks up (on by default) |
| Devices and audio | |
|---|---|
listDevices(kind) | cameras, microphones, audio outputs |
switchDevice(kind, deviceId) | use one |
setSpeakerphoneOn(on) | loudspeaker or earpiece when no headset is connected |
| Statistics and support | |
|---|---|
getVideoStats() / getReceiveStats(producerId) | numbers for the camera / one received video |
getDiagnostics() | a support snapshot (toJson()) |
Room and chat calls: raiseHand, lowerHand, claim, release,
blockSlot, unblockSlot, setMetadata, setRoomMetadata,
muteParticipant, requestMute, forceMute, promote, kick, endRoom,
getRoomState, sendMessage, getMessages, deleteMessage, muteChat,
setTyping, getChatState (see above).
| Properties | |
|---|---|
connectionState | StateFlow<ConnectionState> |
joinResult, role, identity, roomId | the room as this client knows it, kept current |
participants | StateFlow<List<ParticipantInfo>>, kept current |
remoteTracks | what is received now |
localVideoTrack, localMedia | your preview; what you send (LocalMediaState) |
publishing, micEnabled, camEnabled, musicPublishing, musicEnabled, screenSharing | the same, as values |
audioQuality, videoQuality, videoFrameRate, performancePrioritized | what is sent |
mediaPermissions, billingMode | what the project allows; how it bills |
audioOutput | StateFlow<MediaDevice?>: where call audio plays |
closedReason, tokenExpiresAt | why it ended; when the token expires |
Events (SharedFlow) | |
|---|---|
onTrack / onTrackEnded | a remote track arrived or went |
onTrackMuted | a track was paused or resumed, your own included (identity) |
onSubscribeError | one remote track could not be received |
onParticipantJoined / onParticipantLeft / onParticipantUpdated | the roster; ParticipantLeft.reason is left, disconnected, kicked, room-ended or presence-lost |
onParticipantMuted | a moderator switched someone's device off or back on |
onRoleChanged / onHandRaised / onForceMuted | roles, hands, your own moderation |
onClaimChanged / onRoomMetadataChanged | seats and room metadata |
onStreamInterrupted | a publisher's media stopped while it reconnects |
onConnectionStateChanged / onReconnected / onMediaReconnecting / onMustReconnect | the connection |
onUsageLimit / onWalletLow | the account behind the room is running out (the session ends at endsAt unless topped up) |
onTokenExpiring | the token lapses in a minute and was not refreshed |
onKicked / onRoomClosed / onClosed | the endings |
onError / onPublishWarning | problems and warnings |
onNetworkQuality / onAudioLevels / onActiveSpeakers / onMediaWarning / onCpuConstrained | observability (sampled while collected) |
onStreamFallback / onPublishFallback | videos held back for bandwidth |
onDevicesChanged | headsets and devices came or went |
onScreenShareStarted / onScreenShareEnded | screen sharing |
onMessage / onMessageDeleted / onChatMuted / onTyping | chat |
RemoteTrack
identity, producerId, consumerId, kind (audio / video), source
(mic, music, camera, screen), isAudio, isVideo, isMusic,
isEnded, track; addSink(sink), removeSink(sink),
setVolume(0.0..10.0) (this device only; 0 on a music track mutes the music
and keeps the voice), setEnabled(on).
StreamOptions
| Option | Default | |
|---|---|---|
connection | ConnectionOptions() | timeouts, reconnect attempts and backoff, onWire (a token-free wire log for debugging) |
manageAudio | true | the SDK runs call audio (mode, focus, routing) |
speakerphone | true | loudspeaker (true) or earpiece when no headset |
audioOnlyFallback | true | pause remote cameras when the audio breaks up |
pauseCameraInBackground | false | camera off while the app is in the background |
pauseMicInBackground | false | microphone off while the app is in the background |
Threads
Every suspend function is main-safe; media work runs on the client's own
thread. Flows are hot SharedFlows: collect before connect() so nothing is
missed (onClosed replays to a late collector). The same client may be used
from any thread.
Last updated . Something unclear or wrong? Tell us.