Stream Management
Stream management is the heart of any EnableX video session. There are two kinds of streams you work with: a local stream — the audio and video your device captures and publishes to the room — and remote streams — the audio and video sent by other participants that you subscribe to and render on screen. This page walks through the full lifecycle of both: creating and publishing your own stream, subscribing to others, controlling media (mute, camera switching), managing active talkers, and tuning receive quality.
Before you can publish anything to the room, you need a local stream — an
EnxStream object that captures audio and/or video from the device's microphone and
camera. You create this by calling getLocalStream() on your EnxRoom
instance and passing a dictionary that describes which tracks to include and how the stream
should behave at join time.
The typical place to call this is inside your room:didConnect: delegate, immediately
after the connection is confirmed and before you call publish(). The SDK validates
the options, requests any required device permissions, and returns the stream object — which you
then pass to publish().
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(EnxStream *)getlocalStream:(NSDictionary *)publishStreamInfo |
| Returns | EnxStream — the local stream object, ready to publish |
publishStreamInfo Options
The publishStreamInfo dictionary controls which media tracks are included and the
initial mute state. You can also attach custom metadata to the stream using the
attributes object — this metadata is visible to all other participants.
| Key | Type | Description |
|---|---|---|
audio |
Boolean | Include the microphone audio track. |
video |
Boolean | Include the camera video track. |
data |
Boolean | Include a data channel for in-stream messaging. |
audioMuted |
Boolean | Join the room with audio muted from the start. |
videoMuted |
Boolean | Join the room with video muted from the start. |
attributes |
NSDictionary | Custom key-value metadata attached to the stream (visible to all participants). |
- (void)room:(EnxRoom *)room didConnect:(NSDictionary *)roomMetadata {
NSDictionary *streamInfo = @{
@"audio" : @YES,
@"video" : @YES,
@"data" : @YES,
@"audioMuted" : @NO,
@"videoMuted" : @NO,
@"attributes" : @{
@"custom1": @"value"
}
};
EnxStream *localStream = [room getlocalStream:streamInfo];
[room publish:localStream];
}
Error Codes
| Code | Meaning |
|---|---|
| 5015 | No media tracks — both audio and video are set to false. |
| 5016 | Invalid or malformed attributes object. |
| 5017 | Camera and microphone access denied by the user. |
| 5018 | Camera access failed; stream will be audio-only. |
| 5019 | Microphone access denied. |
| 5084 | Invalid video layer specified — maximum allowed is 3. |
| 5088 | Invalid frame rate — maximum allowed is 30 fps. |
Once you have a local stream, you publish it to the room so that other participants can see and
hear you. Calling publish() sends the stream to the EnableX media server, which
then distributes it to all connected subscribers. Until you publish, your audio and video exist
only on your device — no one else in the room receives them.
Publishing generates two delegate callbacks: one delivered privately to you as an acknowledgement, and one broadcast to everyone in the room (including yourself) announcing that a new stream is available.
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(void)publish:(EnxStream *)stream |
Delegate Methods
-
room:didPublishStream:(EnxStream *)stream— ACK sent only to the publisher. Confirms your stream was accepted by the server. This is a good place to attach your local player view. -
room:didAddedStream:(EnxStream *)stream— Broadcast to all participants, including yourself. Every participant (including the publisher) receives this event each time any stream becomes available in the room. Other participants should callsubscribe()on this stream in this callback.
// Publish the local stream after room connects
- (void)room:(EnxRoom *)room didConnect:(NSDictionary *)roomMetadata {
NSDictionary *streamInfo = @{
@"audio": @YES,
@"video": @YES,
@"data" : @YES
};
EnxStream *localStream = [room getlocalStream:streamInfo];
[room publish:localStream];
}
// ACK — only you receive this; attach your local preview here
- (void)room:(EnxRoom *)room didPublishStream:(EnxStream *)stream {
EnxPlayerView *playerView = [[EnxPlayerView alloc] initWithFrame:self.localView.bounds];
[stream attachRenderer:playerView];
[self.localView addSubview:playerView];
}
// Broadcast — all participants receive this for every stream
- (void)room:(EnxRoom *)room didAddedStream:(EnxStream *)stream {
// If this is not your own stream, subscribe to it
if (![stream.streamId isEqualToString:room.localStream.streamId]) {
[room subscribe:stream];
}
}
Error Codes
| Code | Meaning |
|---|---|
| 5013 | Room is not connected. |
| 5015 | Stream has no media tracks. |
| 5016 | Invalid stream attributes. |
| 5022 | No floor access — participant cannot publish in lecture mode without moderator grant. |
| 5023 | Stream is already published. |
| 5024 | Publish is already in progress. |
| 5025 | Cannot call publish on a remote stream. |
| 1170 | General publish failure. |
Unpublishing removes your stream from the room without disconnecting you from the session. Other participants stop receiving your audio and video, and the room notifies everyone that the stream has been removed. You remain connected and can publish again later if needed.
Call unpublish on the EnxStream object (not on the room). The room
instance does not need to be referenced directly for this operation.
| Detail | Value |
|---|---|
| Class | EnxStream |
| Method | -(void)unpublish |
| Delegate | room:didRemovedStream: — broadcast to all participants |
// Stop publishing your local stream
[localStream unpublish];
// All participants (including you) receive this when any stream is removed
- (void)room:(EnxRoom *)room didRemovedStream:(EnxStream *)stream {
// Remove the corresponding player view from your UI
NSLog(@"Stream removed: %@", stream.streamId);
}
Error Codes
| Code | Meaning |
|---|---|
| 5029 | Unpublish already in progress. |
| 5030 | Stream has not been published yet. |
During a live session you may need to change which camera is active, switch to a different
microphone input, or change audio output routing. The SDK provides dedicated methods on both
EnxStream and EnxRoom for each of these operations.
Switch Camera Preview
Use switchCameraPreview to toggle the camera view between front-facing and
rear-facing without interrupting the published stream. This is useful when you want the user to
preview the rear camera before switching the live feed.
| Detail | Value |
|---|---|
| Class | EnxStream |
| Method | -(void)switchCameraPreview |
// Switch the local camera preview (does not affect published stream)
[localStream switchCameraPreview];
Switch Front / Rear Camera
switchCamera flips the active camera between front-facing and rear-facing and
immediately updates the published stream. All subscribers in the room see the new camera angle
without re-subscribing.
| Detail | Value |
|---|---|
| Class | EnxStream |
| Method | -(NSException *)switchCamera |
NSException *exception = [localStream switchCamera];
if (exception) {
NSLog(@"Camera switch failed: %@", exception.reason);
}
| Error Code | Meaning |
|---|---|
| 5021 | Cannot switch camera — session is in audio-only mode. |
| 5097 | Cannot switch camera — room is in chat-only mode. |
Switch Microphone / Audio Output
Use switchMediaDevice on the room to route audio through a different input or
output device — for example switching from the speakerphone to a connected Bluetooth headset, or
from the earpiece to the speaker. Pass the device name string as returned by
getDevices.
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(void)switchMediaDevice:(NSString *)mediaName |
| Delegate | didNotifyDeviceUpdate:(NSString *)updates — fires with the new device name |
// Switch audio output to the speakerphone
[room switchMediaDevice:@"Speaker"];
// Delegate fires after the switch completes
- (void)didNotifyDeviceUpdate:(NSString *)updates {
NSLog(@"Audio device changed to: %@", updates);
// Refresh your device selection UI to highlight the new active device
}
Participants can mute their own audio or video at any time without unpublishing their stream. When you mute yourself, the track is silenced or blacked out on all subscribers' screens. Unmuting restores the track. The SDK fires delegate events both to you (as an acknowledgement) and to the rest of the room (as a notification), so every participant's UI can reflect the change.
Mute / Unmute Audio
Call muteSelfAudio: with YES to mute or NO to unmute your
microphone. Pass the boolean based on the desired end-state, not a toggle.
| Detail | Value |
|---|---|
| Class | EnxStream |
| Method | -(void)muteSelfAudio:(BOOL)isMuted |
Delegate methods:
didAudioEvent:(NSDictionary *)data— ACK to the local participant.stream:didRemoteStreamAudioMute:(NSArray *)data— broadcast when muted.stream:didRemoteStreamAudioUnMute:(NSArray *)data— broadcast when unmuted.
// Mute your microphone
[localStream muteSelfAudio:YES];
// Unmute your microphone
[localStream muteSelfAudio:NO];
// ACK for the local user
- (void)didAudioEvent:(NSDictionary *)data {
NSLog(@"Audio mute state changed: %@", data);
}
// Room-wide notifications
- (void)stream:(EnxStream *)stream didRemoteStreamAudioMute:(NSArray *)data {
// Update the mute indicator for this participant in your UI
}
- (void)stream:(EnxStream *)stream didRemoteStreamAudioUnMute:(NSArray *)data {
// Remove the mute indicator for this participant in your UI
}
Mute / Unmute Video
muteSelfVideo: follows the same pattern as audio muting. Subscribers see a
blacked-out frame while video is muted. The stream remains subscribed and active; only the video
track is suppressed.
| Detail | Value |
|---|---|
| Class | EnxStream |
| Method | -(void)muteSelfVideo:(BOOL)isMuted |
Delegate methods:
didVideoEvent:(NSDictionary *)data— ACK to the local participant.stream:didRemoteStreamVideoMute:(NSArray *)data— broadcast when muted.stream:didRemoteStreamVideoUnMute:(NSArray *)data— broadcast when unmuted.
// Mute your camera video
[localStream muteSelfVideo:YES];
// Unmute your camera video
[localStream muteSelfVideo:NO];
- (void)didVideoEvent:(NSDictionary *)data {
NSLog(@"Video mute state changed: %@", data);
}
- (void)stream:(EnxStream *)stream didRemoteStreamVideoMute:(NSArray *)data {
// Show a camera-off placeholder in the participant tile
}
- (void)stream:(EnxStream *)stream didRemoteStreamVideoUnMute:(NSArray *)data {
// Restore the video frame in the participant tile
}
When a participant publishes a stream, the room notifies all connected clients via
room:didAddedStream:. At this point the stream exists on the server, but you are
not yet receiving media for it. To start receiving audio and video from that participant, you
must explicitly call subscribe() on the stream object. This is a deliberate design
— it lets you choose which streams to receive, which is important for bandwidth management in
larger rooms.
After subscribing, the SDK fires room:didSubscribeStream: with the fully
initialised stream. This is where you create an EnxPlayerView, attach the stream
to it, and add it to your view hierarchy.
subscribe() individually for each remote stream. There is no
"subscribe all" shortcut. Screen share streams arrive with StreamID 101 and canvas
streams with StreamID 102 — handle these separately if your app supports them.
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(void)subscribe:(EnxStream *)stream |
| Delegate | room:didSubscribeStream: — fires when subscription is confirmed |
// Subscribe when the room notifies you of a new stream
- (void)room:(EnxRoom *)room didAddedStream:(EnxStream *)stream {
[room subscribe:stream];
}
// Once subscribed, attach a player view and render the stream
- (void)room:(EnxRoom *)room didSubscribeStream:(EnxStream *)stream {
CGRect frame = CGRectMake(0, 0, 160, 120);
EnxPlayerView *playerView = [[EnxPlayerView alloc] initWithFrame:frame];
[stream attachRenderer:playerView];
[self.view addSubview:playerView];
}
Error Codes
| Code | Meaning |
|---|---|
| 5014 | Room is not in a connected state. |
| 5026 | Already subscribed to this stream. |
| 5027 | Subscription is already in progress. |
| 5028 | Cannot call subscribe on a local stream. |
In a room with many participants, it is not practical (or efficient) to render every stream simultaneously. EnableX addresses this with the Active Talker List — the SDK continuously monitors audio levels and maintains a ranked list of up to 12 participants who are currently speaking. Your app receives updates to this list automatically and should refresh its layout in response.
The way you receive and display active talkers depends on the activeviews setting
you passed when connecting:
-
activeviews: "list"— The SDK gives you a stream list and you control the layout entirely. Implementroom:didActiveTalkerList:to receive updates and rebuild your video grid. -
activeviews: "view"— The SDK builds and manages aUIViewgrid internally. You receive it once viaroom:didActiveTalkerView:and add it to your view hierarchy. The SDK handles all subsequent updates.
Active Talker Delegates
-
room:didActiveTalkerList:(NSArray *)streams— Fires whenever the ranked list changes (only whenactiveviews: "list"). Rebuild your video grid using the streams in this array. -
room:didActiveTalkerView:(UIView *)view— Fires once (only whenactiveviews: "view"). Add this view to your layout; the SDK manages it from there. -
room:didAvailable:(NSDictionary *)data— Fires whenever the count of available (connected) participants changes.
// activeviews: "list" — you manage the layout
- (void)room:(EnxRoom *)room didActiveTalkerList:(NSArray *)streams {
// Clear current video tiles and rebuild from the updated stream list
[self rebuildVideoGrid:streams];
}
// activeviews: "view" — add the SDK-managed view once
- (void)room:(EnxRoom *)room didActiveTalkerView:(UIView *)view {
view.frame = self.videoContainerView.bounds;
[self.videoContainerView addSubview:view];
}
// Available participant count changed
- (void)room:(EnxRoom *)room didAvailable:(NSDictionary *)data {
NSLog(@"Participants available: %@", data);
}
Get Maximum Talker Count
getMaxTalkers queries the server for the maximum number of active video streams the
room configuration allows. This is a room-level setting and does not change during a session.
Use this value to cap your UI layout or inform the user of the room's maximum concurrent video
feeds.
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(void)getMaxTalkers |
| Delegate | room:didGetMaxTalkers:(NSDictionary *)data |
[room getMaxTalkers];
- (void)room:(EnxRoom *)room didGetMaxTalkers:(NSDictionary *)data {
// data = { "result": 0, "maxTalkers": 4 }
NSInteger maxTalkers = [data[@"maxTalkers"] integerValue];
NSLog(@"Room max talkers: %ld", (long)maxTalkers);
}
Get Current Talker Count
getTalkerCount queries how many active talker slots are currently configured for
your session. This is the number you (or the room settings) have chosen to display, which may be
less than the room maximum.
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(void)getTalkerCount |
| Delegate | room:didGetTalkerCount:(NSDictionary *)data |
[room getTalkerCount];
- (void)room:(EnxRoom *)room didGetTalkerCount:(NSDictionary *)data {
// data = { "result": 0, "numTalkers": 4 }
NSInteger numTalkers = [data[@"numTalkers"] integerValue];
NSLog(@"Current talker count: %ld", (long)numTalkers);
}
Set Talker Count
Use setTalkerCount: to change how many simultaneous video streams the active talker
list includes. The value can be between 0 and 12. Setting it to 0 is a special case — it means
the SDK will maintain 3 audio-only streams instead of video.
This is particularly useful in response to bandwidth alerts: when available bandwidth drops, you can reduce the talker count to lower the total video bitrate in the room.
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(void)setTalkerCount:(NSInteger)numTalkers |
| Valid Range | 0–12 (0 = 3 audio-only streams) |
| Delegate | room:didSetTalkerCount:(NSDictionary *)data |
| Error | 5055 — invalid talker count value |
// Reduce active video streams to 4 (e.g. in response to a bandwidth alert)
[room setTalkerCount:4];
- (void)room:(EnxRoom *)room didSetTalkerCount:(NSDictionary *)data {
NSLog(@"Talker count updated: %@", data);
}
When activeviews: "view" is active, the SDK manages the video grid layout
internally. These utilities let you interact with that managed layout — highlight specific
participants, change background colours, force a refresh, or switch between layout modes. All
methods in this section are available from iOS SDK 2.1.3 unless noted otherwise.
activeviews: "view" is set during
connect(). They have no effect in list mode, where you control the layout yourself.
getPlayer
Retrieve the EnxPlayerView for a specific participant by their
clientID. Use this when you need to apply custom styling or overlay to a single
participant's tile without affecting others.
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(EnxPlayerView *)getPlayer:(NSString *)clientID |
| Returns | EnxPlayerView for the given client, or nil if not found |
EnxPlayerView *playerView = [room getPlayer:@"CLIENT_ID"];
if (playerView) {
// Apply custom styling, border, or overlay to this participant's tile
}
highlightBorderForClient
Highlight the border of one or more participant tiles — useful for indicating who is speaking, who has raised their hand, or who has been selected as the spotlight speaker. Pass an array of client ID strings.
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(void)highlightBorderForClient:(NSArray *)clientIDs |
// Highlight the speaking indicator for specific participants
[room highlightBorderForClient:@[@"CLIENT_ID_1", @"CLIENT_ID_2"]];
changeBgColorForClients
Change the background colour of specific participant tiles — for example to visually distinguish the moderator, indicate a raised hand, or apply role-based colour coding.
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(void)changeBgColorForClients:(NSArray *)clientIDs withColor:(UIColor *)color |
// Highlight the moderator's tile with a distinct background colour
[room changeBgColorForClients:@[@"MODERATOR_CLIENT_ID"]
withColor:[UIColor colorWithRed:0.2 green:0.6 blue:1.0 alpha:0.4]];
forceUpdateATList
Force the SDK to reload and re-render the active talker view immediately. Call this after making programmatic changes to the layout or after returning from a different view controller to ensure the grid is up to date.
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(void)forceUpdateATList |
// Force a refresh of the active talker grid
[room forceUpdateATList];
switchATView
Switch the active talker grid between two layout modes: "leader" — which shows one
prominent speaker tile with smaller tiles for others — and "gallery" — which shows
all active participants at equal size. Available from iOS SDK 2.1.2.
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(void)switchATView:(NSString *)viewString |
| Valid Values | "leader" or "gallery" |
// Switch to leader layout (one prominent + smaller tiles)
[room switchATView:@"leader"];
// Switch to gallery layout (equal-size tiles)
[room switchATView:@"gallery"];
By default, EnableX automatically selects the best video quality for incoming streams based on available bandwidth. However, you can override this and pin the receive quality to a specific level — useful when you know the device or network conditions warrant a fixed setting, or when building a user-facing quality selector in your settings UI.
Call setReceiveVideoQuality: on the room object with one of the four quality
levels. The change applies to all incoming remote streams. The SDK confirms the change via
delegate.
| Detail | Value |
|---|---|
| Class | EnxRoom |
| Method | -(void)setReceiveVideoQuality:(NSString *)videoQuality |
| Delegate | room:didSetVideoQuality:(NSDictionary *)data |
| Error | 5057 — the requested quality level is already active |
Quality Levels
| Value | Description |
|---|---|
Auto |
SDK selects quality automatically based on bandwidth conditions (default). |
HD |
High-definition video. Requires strong network conditions. |
SD |
Standard-definition video. Balanced quality and bandwidth use. |
LD |
Low-definition video. Minimal bandwidth consumption. |
// Pin incoming video quality to standard-definition
[room setReceiveVideoQuality:@"SD"];
- (void)room:(EnxRoom *)room didSetVideoQuality:(NSDictionary *)data {
NSLog(@"Receive video quality updated: %@", data);
}
LD when responding to a bandwidth alert
(room:didRoomBandwidthAlert:), and restore it to Auto once bandwidth
recovers. This keeps call quality as high as conditions allow without manually tracking
thresholds.