Skip to content
Info:
This is beta documentation for Stream Video JavaScript SDK v2. For the latest stable version, see the latest version (v1).

Call & Participant State

The call, participant and client states are reactive. Their value is updated on WebSocket events and API calls. We use the RxJS library for this, each state property is an Observable.

Let's see an example, this is how you can subscribe to changes related to participants:

const { participants$ } = call.state;
const subscription = participants$.subscribe((participants) => {
  console.log(participants);
});

// dispose the subscription when you don't need it anymore
subscription.unsubscribe();

The console.log method will be called each time there is a change to the list of participants in the call.

This approach makes it possible to access the state and be notified about changes anywhere in your application without having to manually subscribe to WebSocket events.

Alternatively, you can also access the current value of each state variable:

const { participants } = call.state;
console.log(participants);

It's useful when you need to know the current value, but you don't need to be notified about future changes.

Call state

The call state can be accessed through call.state object as in the examples above. In this object, you'll find properties that are reactive and static as well. The reactive/observable properties are suffixed with $ and the static ones are without the suffix.

Note:

For the best experience, please make sure that the call instance is loaded and connected to our backend: Load Call.

Otherwise, call.state observables will emit empty values and you won't get real time updates.

Here is an excerpt of the call state properties:

Reactive value Static value Description
backstage$ backstage true when the call runs in backstage mode
blockedUserIds$ blockedUserIds The list of blocked user IDs.
callingState$ callingState Provides information about the call state. For example, RINGING, JOINED or RECONNECTING.
callStatsReport$ callStatsReport When stats gathering is enabled, this observable will emit a new value at a regular (configurable) interval.
captioning$ captioning Provides information whether closed captioning is running for this call or not.
closedCaptions$ closedCaptions The closed captions state of the call.
createdAt$ createdAt The time the call was created.
createdBy$ createdBy The user who created the call.
custom$ custom Custom data attached to the call.
dominantSpeaker$ dominantSpeaker The participant that is the current dominant speaker of the call.
e2eeEnabled$ e2eeEnabled true when end-to-end encryption is active for the call.
egress$ egress The egress data of the call (for broadcasting and livestreaming).
endedAt$ endedAt The time the call was ended.
endedBy$ endedBy The user who ended the call.
hasOngoingScreenShare$ hasOngoingScreenShare It will return true if at least one participant is sharing their screen.
individualRecording$ individualRecording It will return true if "individual track recording" is running.
ingress$ ingress The ingress data of the call (for broadcasting and livestreaming).
members$ members The list of call members
ownCapabilities$ ownCapabilities The capabilities of the local participant.
rawRecording$ rawRecording Will return true if "raw" recording is running.
recording$ recording The recording state of the call.
session$ session The data for the current call session.
settings$ settings The settings of the call.
startedAt$ startedAt The actual start time of the current call session.
startsAt$ startsAt The time the call is scheduled to start.
thumbnails$ thumbnails The thumbnails of the call.
transcribing$ transcribing The transcribing state of the call.
updatedAt$ updatedAt The time the call was updated.
Note:

Your IDE of choice may help you to discover the other properties of the call state.

Participant state

If you want to display information about the joined participants of the call you can use the following properties in call.state:

Observable properties

Reactive value Static value Description
anonymousParticipantCount$ anonymousParticipantCount The approximate participant count of anonymous users in the active call.
localParticipant$ localParticipant The local participant is the logged-in user.
participantCount$ participantCount The approximate participant count of the active call. This includes the anonymous users as well, it is computed on the server-side.
participants$ participants All participants, including local and remote participants.
pinnedParticipants$ pinnedParticipants All participants that are currently pinned, either locally or server-side.
rawParticipants$ rawParticipants A more stable version of participants$ that is not affected by participant sort settings and thus emits less often.
remoteParticipants$ remoteParticipants All participants except the local participant.
Warning:

Warning: In a call with many participants, the value of the participants$ call state observable is truncated to 250 participants.

The participants who are publishing video, audio or screen sharing have priority over the other participants in the list. This means, for example, that in a livestream with one host and many viewers, the host is guaranteed to be in the list.

Participant data

The StreamVideoParticipant object contains the following information:

Name Description
audioLevel The audio level of the participant (determined on the server).
audioStream The published audio MediaStream.
audioVolume The audio volume level of the participant (overridable local audioVolume level).
connectionQuality The participant's connection quality.
custom The participant's custom data. Comes from the custom field of the user object.
image The image of the participant.
interruptedTracks Tracks the participant intends to publish but that are currently not producing media (system mute, OS-level kill switch, Bluetooth disconnect, iOS audio interruption, sustained RTP stalls). Orthogonal to publishedTracks.
isDominantSpeaker It's true if the participant is the current dominant speaker in the call.
isLocalParticipant It's true if the participant is the local participant.
isSpeaking It's true if the participant is currently speaking.
joinedAt The time the participant joined the call.
name The name of the participant.
pausedTracks The tracks that are currently server-side paused for the local participant.
pin Holds pinning information.
publishedTracks The track types the participant is currently publishing
reaction The last reaction this user has sent to this call.
roles The roles of the participant in this call.
screenShareAudioStream The published screen share audio MediaStream.
screenShareStream The published screen share MediaStream.
sessionId The identifier of the participant within the existing call session
source The participant source: WebRTC (default), RTMP (OBS), WHIP, SIP, RTSP, SRT...
userId The user ID of the participant.
videoStream The published video MediaStream.
viewportVisibilityState The viewport visibility state of the participant.

Utility functions

The SDK also provides a few utility functions that help you to work with participants:

import {
  Call,
  hasAudio,
  hasInterruptedTrack,
  hasScreenShare,
  hasScreenShareAudio,
  hasVideo,
  isPinned,
  SfuModels,
} from "@stream-io/video-client";

let call: Call;

// example usage
const subscription = call.state.participants$.subscribe((participants) => {
  for (let participant of participants) {
    // check if the participant has audio, video, screen share or screen share audio
    const hasAudioOn = hasAudio(participant);
    const hasVideoOn = hasVideo(participant);
    const hasScreenShareOn = hasScreenShare(participant);
    const hasScreenShareAudioOn = hasScreenShareAudio(participant);
    const isPinnedOn = isPinned(participant);
    const isAudioInterrupted = hasInterruptedTrack(
      participant,
      SfuModels.TrackType.AUDIO,
    );
  }

  // participants with a specific role
  const hosts = participants.filter((p) => p.roles.includes("host"));

  // participants that publish video and audio
  const videoParticipants = participants.filter(
    (p) => hasVideo(p) && hasAudio(p),
  );
});

subscription.unsubscribe();

Detecting participant source

Participants can be created from different sources (WebRTC, RTMP/OBS, WHIP, SIP, etc...). The source property of the StreamVideoParticipant object indicates the source of the participant.

import { SfuModels } from "@stream-io/video-client";

const subscription = call.state.participants$.subscribe((participants) => {
  // participants joining through OBS have RTMP source
  const rtmpParticipants = participants.filter(
    (p) => p.source === SfuModels.ParticipantSource.RTMP,
  );
});

// remember to unsubscribe when you don't need it anymore
subscription.unsubscribe();

Detecting interrupted tracks

A participant's published audio or video can be temporarily silenced by something outside the participant's control — the operating system muting the microphone, a Bluetooth headset disconnecting, an iOS audio session interruption, or transient SFU/RTP issues. Use the hasInterruptedTrack(participant, trackType) helper to detect this and surface a hint in the UI:

import { Call, hasInterruptedTrack, SfuModels } from "@stream-io/video-client";

let call: Call;

const subscription = call.state.localParticipant$.subscribe((participant) => {
  if (!participant) return;
  const isMicInterrupted = hasInterruptedTrack(
    participant,
    SfuModels.TrackType.AUDIO,
  );
  // toggle a "Microphone is paused by your system" hint in your UI
});

// remember to unsubscribe when you don't need it anymore
subscription.unsubscribe();

The helper intersects interruptedTracks with publishedTracks, so the indicator only fires while the participant is actively publishing the track. This avoids a stale "interrupted" indicator if a remote sender unpublishes the track while it was interrupted.

For remote participants, interruptedTracks currently only surfaces TrackType.AUDIO; remote video and screen-share interruption are not tracked. For the local participant, it covers both audio and video.

Client state

The client state can be accessed by client.state.

Here is the list of client state properties:

Reactive value Static value Description
connectedUser$ connectedUser Returns the connected user. Holds the server-side data of the connected user.
calls$ calls A list of all tracked calls. These calls can be outgoing (I have called somebody) or incoming (somebody has called me). Loaded calls (call.get()) are also part of this list.

The connectedUser object contains the following properties:

Name Description
created_at The time the user was created.
custom Custom user data.
deleted_at The time the user was deleted.
devices The registered push notification devices of the user.
id The id of the user.
image The profile image of the user.
name The name of the user.
role The role of the user.
teams The teams the user belongs to.
updated_at The time when the user was updated.