Stream Management

This page covers everything you need to manage audio and video streams in an active session — from publishing and muting your own stream, to switching devices on the fly, to receiving and controlling remote participant streams.

Local Stream

Publish a Local Stream

After connecting to a room, call room.publish() to make your audio and video available to all other participants. You can publish and unpublish multiple times within the same session. The stream-added event is broadcast to everyone — including yourself — when publishing succeeds.

var publishOpt = {
  minVideoBW: 150,   // Minimum video bitrate in kbps
  maxVideoBW: 1200   // Maximum video bitrate in kbps
};

room.publish(localStream, publishOpt, function(streamId) {
  console.log("Published with stream ID:", streamId);
});

room.addEventListener("stream-added", function(event) {
  if (localStream.getID() === event.stream.getID()) {
    // Your own stream confirmed published — update UI
  } else {
    // Someone else published — subscribe to their stream
    room.subscribe(event.stream, { audio: true, video: true, data: true });
  }
});

room.addEventListener("stream-failed", function(event) {
  console.error("Publishing failed");
});
Error CodeDescription
1170Feature not enabled — check room configuration or service subscription

Unpublish a Local Stream

room.unpublish() removes your stream from the room without releasing camera/microphone access. You can republish later without having to re-request device permissions.

room.unpublish(localStream, function(result, error) {
  if (result === undefined) {
    console.error("Unpublish failed:", error);
  } else {
    console.log("Stream unpublished");
  }
});

room.addEventListener("stream-removed", function(event) {
  if (localStream.getID() === event.stream.getID()) {
    // Your stream was removed — hide your video tile
  } else {
    // Another participant's stream was removed
  }
});

Close a Local Stream

stream.close() is a stronger action than unpublish. It stops capturing media from the camera and microphone, unpublishes the stream if it is published, and stops playback if it is being played. Use it to fully release device resources.

localStream.close(); // Stops capture, unpublishes, releases device
Mute & Unmute

Mute / Unmute Audio

Use these methods to control the audio track of your own published stream. When you mute, all other participants are notified via the user-audio-muted event so they can update their UI (e.g., show a muted microphone icon).

// Mute your microphone
localStream.muteAudio(function(res) {
  if (res.result === 0) {
    showMutedIcon();
  }
});

// Others are notified
room.addEventListener("user-audio-muted", function(event) {
  // event.streamId — who muted. Update their participant tile.
});

// Unmute your microphone
localStream.unmuteAudio(function(res) {
  if (res.result === 0) {
    hideMutedIcon();
  }
});

room.addEventListener("user-audio-unmuted", function(event) {
  // Update UI for the participant who unmuted
});
Error CodeDescription
1140Previous mute request still in progress — wait before sending another
1176Moderator controls media devices — self-unmute not permitted
1191Stream track has ended — user must rejoin to restore audio

Mute / Unmute Video

Controls the video track of your own published stream. Muting video turns off the camera but keeps the audio stream active. Others are notified so they can show a video-off placeholder.

// Turn off camera
localStream.muteVideo(function(res) {
  if (res.result === 0) { showCameraOffPlaceholder(); }
});

room.addEventListener("user-video-muted", function(event) {
  // Show camera-off placeholder for the affected participant
});

// Turn camera back on
localStream.unmuteVideo(function(res) {
  if (res.result === 0) { hideCameraOffPlaceholder(); }
});

room.addEventListener("user-video-unmuted", function(event) {
  // Remove camera-off placeholder
});
Switch Devices

Switch Camera & Microphone Simultaneously

EnxRtc.switchMediaDevice() swaps both the camera and microphone of a published stream at once. The stream remains published — participants experience a seamless transition to the new devices without a gap in audio/video.

// Switch both camera and mic using device IDs from getDevices()
EnxRtc.switchMediaDevice(
  localStream,
  newMicDeviceId,
  newCamDeviceId,
  function(stream) {
    if (stream && stream.getID) {
      localStream = stream;  // Update local reference
      console.log("Devices switched");
    } else {
      console.error("Switch failed");
    }
  }
);

// For mobile: switch to front camera using facingMode
EnxRtc.switchMediaDevice(
  localStream,
  micDeviceId,
  { facingMode: "user" },   // "user" = front, "environment" = rear
  function(stream) {
    if (stream && stream.getID) {
      localStream = stream;
    }
  }
);
Error CodeDescription
1140Previous switch request still in progress
1155Invalid parameter passed
1159Audio or video stream not available
1189Device switching not allowed while stream is muted

Switch Camera Only

EnxRtc.switchCamera(localStream, newCamDeviceId, function(stream) {
  if (stream && stream.getID) {
    localStream = stream;
  }
});

Switch Microphone Only

localStream.switchMicrophone(localStream, newMicDeviceId, function(stream) {
  if (stream && stream.getID) {
    localStream = stream;
  }
});

Switch Speaker

Switch to a different audio output device (speaker/headset) to play remote audio. Use the deviceId from getDevices().

room.switchSpeaker(newSpeakerId, function(res) {
  if (res.result === 0) {
    console.log("Speaker switched");
  }
});
Remote Streams

Subscribe to Remote Streams

To receive audio and video from another participant, call room.subscribe() on their stream object. You must subscribe individually to each stream you want to receive. After a successful subscription, you can play the stream in a DOM element.

var subsOptions = {
  audio: true,
  video: true,
  data:  true
};

room.subscribe(remoteStream, subsOptions, function(err) {
  if (err) console.error("Subscribe failed:", err);
});

room.addEventListener("stream-subscribed", function(event) {
  // Now safe to play the stream
  event.stream.play("video-container-" + event.stream.getID(), {
    player: { width: "100%", height: "150px" },
    toolbar: { displayMode: false }
  });
});
Screen share and canvas streams

Screen share is always available as Stream ID 101. Canvas streaming is Stream ID 102. Subscribe to these stream IDs from room.remoteStreams if your app supports those features.

Active Talker List

EnableX limits the active stream set to the most recently speaking participants (up to 12 by default). The active-talkers-updated event fires every time the active list changes — a new speaker joins, someone goes silent, or a pinned user is added. Rebuild your video grid on each event.

room.addEventListener("active-talkers-updated", function(event) {
  var talkers = event.message.activeList;
  // Each entry: { clientId, name, streamId, mediatype, videomuted, pinned }

  talkers.forEach(function(talker) {
    if (!talker.streamId) return;
    var stream = room.remoteStreams.get(talker.streamId);
    if (stream) {
      // Show/refresh this tile in your grid
      stream.play("slot-" + talker.streamId, { player: { width: "100%", height: "150px" } });
    }
  });
});

Control the Talker Count

By default, up to 12 remote streams are delivered. You can reduce this to save bandwidth, or query the current limit programmatically.

Get Current Talker Count

room.getTalkerCount(function(response) {
  // response: { result: 0, maxTalkers: 4 }
  console.log("Currently receiving up to", response.maxTalkers, "streams");
});

Set Talker Count

// Limit to 4 remote streams (saves bandwidth on slow connections)
room.setTalkerCount(4, function(response) {
  // response: { result: 0, maxTalkers: 4 }
});

// Setting to 0 delivers 3 audio-only streams, no video
Range limit

The maximum value is capped by max_active_talkers set during room creation (default: 12). Passing a value above this cap is rejected with error code 4106.

Get Maximum Permissible Talker Count

room.getMaxTalkers(function(response) {
  // response: { result: 0, maxTalkers: 6 }
  console.log("Room allows up to", response.maxTalkers, "active talkers");
});

Set Receive Video Quality

When the publisher sends multiple video layers (maxVideoLayers: 3), each subscriber can independently choose which quality tier to receive. Use this to let users trade bandwidth for quality.

var qualityOpt = {
  videoQuality: "SD",       // Receive SD to save bandwidth
  streamType:   "talker"    // Apply to active talker streams
};

room.setReceiveVideoQuality(qualityOpt, function(result) {
  if (result.result === 0) {
    console.log("Quality set to SD");
  }
});
Stream Information & Playback

Stream Information Methods

These methods work on any EnxStream object — local or remote. Use them to inspect or update stream metadata.

Get Stream ID

var streamId = localStream.getID();  // Returns the unique stream ID string

Get Stream Attributes

Returns the custom attributes set during stream initialization or via setAttributes().

var attrs = stream.getAttributes();
// Returns: { name: "Alice", custom_key: "value", ... }

Update Stream Attributes

Update custom attributes on your local stream at any time during the session. All subscribers of your stream are notified via the stream-attributes-updated event.

localStream.setAttributes({
  name:     "Alice (presenting)",
  role:     "presenter",
  slide_no: 3
});

room.addEventListener("stream-attributes-updated", function(event) {
  // event.attributes — updated attributes. Refresh participant name label, etc.
});

Update Stream Bandwidth Configuration

Dynamically change the max audio/video bitrate of a stream without republishing it.

localStream.updateConfiguration({
  maxVideoBW: 400,   // kbps
  maxAudioBW: 64
}, function(result) {
  console.log("Bandwidth reconfigured");
});

Check Available Media Tracks

if (stream.ifVideo())  { /* has video track  */ }
if (stream.ifAudio())  { /* has audio track  */ }
if (stream.ifData())   { /* has data channel */ }
if (stream.ifScreen()) { /* is screen share  */ }

Play a Stream

stream.play() renders the stream inside a DOM element. The SDK creates an <audio> or <video> element inside the target div and begins playback. Call this after successfully subscribing to a remote stream, or after publishing your local stream to show a self-preview.

var playerOpt = {
  player: {
    width:     "100%",
    height:    "150px",
    minHeight: "inherit",
    minWidth:  "inherit"
  },
  toolbar: {
    displayMode: false,       // Hide SDK toolbar overlay
    branding: { display: false }
  }
};

stream.play("video-slot-42", playerOpt);

Stop Playing a Stream

Stops the audio/video playback in the HTML player without removing the subscription or closing the stream.

stream.stop();  // Stops rendering; stream remains subscribed
← Connecting to a Session In-Session Communication →