Conference SDK
Red5 Pro Conference SDK allows you to build advanced conference call applications with unlimited number of participants on top of your Red5 infrastructure, supporting screen sharing, subscribe only participants, audio output/input device switching, publish quality changing and audio level monitoring.
Overview
The Red5 Pro Conference SDK provides a high-level API for building video conferencing applications. It abstracts the complexity of WebRTC management, media handling, and connection management while providing robust features for production use.
Key Features
- Real-time Video Conferencing: Full-duplex audio/video communication
- Screen Sharing: Built-in screen capture and sharing capabilities
- Audio Level Monitoring: Real-time audio level detection for participants
- Media Device Management: Easy camera/microphone switching
- Role-based Access: Support for publisher and subscriber roles
- Chat Messaging: Built-in text chat functionality
- Mute/Unmute Controls: Audio and video muting capabilities
- Cross-browser Support: Works across modern browsers
Installation
Prerequisites
- Red5 Javascript WebRTC SDK
- ConferenceClient.ts and MediaStreamManager.js
- Red5 Pro Stream Manager(Cloud) deployment
NPM Installation
npm install red5pro-webrtc-sdk
Include the SDK Files
Include ConferenceClient.ts and MediaStreamManager.js in same directory:
import { ConferenceClient } from './ConferenceClient';
Quick Start
Here’s a minimal example to get you started:
import { ConferenceClient } from './ConferenceClient';
// Initialize the client
client = new ConferenceClient({
host: 'your-cloud-deployment-host.red5pro.net',
nodeGroup: 'default',
maxVideoBitrateKbps: 3000,
iceServers: [{ urls: 'stun:stun2.l.google.com:19302' }]
});
// Get user media
const mediaStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
// Join a room
await client.join('room-123', 'user-456', '', 'publisher', mediaStream, true, true);
// Listen for events
client.on('user-published', (data) => {
console.log('Joined successfully:', data.participants);
});
client.on('new-participant', async (data) => {
console.log('New participant:', data.participant);
if (data.participant.role === 'publisher') {
await client.subscribe(data.participant);
}
});
Basic Usage
Joining a Room
Get user media stream through media stream manager. We will pass it to join()
var video = true
var audio = true
var mediaStream = client.mediaStreamManager.getCurrentStream(video, audio)
join(roomId, userId, token, role, mediaStream, videoEnabled, audioEnabled, metaData)
Join a conference room. If successful, conference SDK will emit user-published event with already existing participants in the room. You can join as publisher or subscriber. Publishers can publish and subscribe(play), meanwhile subscriber role can only subscribe streams.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
roomId |
string | – | Unique room identifier |
userId |
string | – | Unique user identifier (stream name) |
token |
string | ” | Authentication token |
role |
string | ‘publisher’ | User role (‘publisher’ or ‘subscriber’) |
mediaStream |
MediaStream | null | User’s media stream |
videoEnabled |
boolean | true | Initial video state |
audioEnabled |
boolean | true | Initial audio state |
metaData |
string | null | Additional user metadata |
Returns: Promise<boolean>
Example:
client.on('user-published', handleUserPublished);
client.on('subscribe-success', handleSubscribeSuccess);
await client.join(
'conference-room-id',
'user-id-123',
'auth-token',
'publisher',
mediaStream,
true, // video enabled
true, // audio enabled
{ name: 'John Doe', avatar: 'https://...' }
);
const handleUserPublished = async (data) => {
console.log('User published/joined room success', data);
await subscribeToParticipants(data.participants);
};
const subscribeToParticipants = async (participantsObj) => {
for (const [userId, participant] of Object.entries(participantsObj)) {
if(participant.role === "publisher") {
await subscribeToParticipant(participant);
}
}
};
const subscribeToParticipant = async (participant) => {
try {
console.log(`Subscribing to participant: ${participant.uid}`);
await client.subscribe(participant);
} catch (error) {
console.error(`Failed to subscribe to ${participant.uid}:`, error);
}
};
// Add user to a state for rendering their video on screen.
const handleSubscribeSuccess = (data) => {
console.log('Subscribe success:', data.uid);
setSubscribedParticipants(prev => ({
...prev,
[data.uid]: {
participant: data.participant,
mediaStream: data.mediaStream
}
}));
};
Handling New Participants
When a new participant joins, conference SDK will emit a new event new-participant. Listen for this event and subscribe to new participant.
client.on('new-participant', handleNewParticipant);
const handleNewParticipant = async (data) => {
console.log('New participant:', data.participant);
setParticipants(prev => ({
...prev,
[data.participant.uid]: data.participant
}));
if(data.participant.role === "publisher") {
setTimeout(async () => {
await subscribeToParticipant(data.participant);
}, 3000);
}
};
Handle Disconnected Participants
When someone leaves the room, conference client will emit participant-disconnected event. Listen for it and remove the user from your participant lists.
client.on('participant-disconnected', handleParticipantDisconnected);
const handleParticipantDisconnected = (data) => {
console.log('Participant disconnected:', data.participant);
setParticipants(prev => {
const newParticipants = { ...prev };
delete newParticipants[data.participant.uid];
return newParticipants;
});
setSubscribedParticipants(prev => {
const newSubscribed = { ...prev };
delete newSubscribed[data.participant.uid];
return newSubscribed;
});
setAudioLevels(prev => { // if you handle audio levels, remove from that list also.
const newLevels = { ...prev };
delete newLevels[data.participant.uid];
return newLevels;
});
};
Leave a Room
Leave a room by calling leave() method on conference client.
client.leave()
Mute Video/Audio
You can turn off/on camera or mute/unmute your microphone.
const handleMuteAudio = () => {
if (isAudioMuted) {
client.unmuteAudio();
} else {
client.muteAudio();
}
};
const handleMuteVideo = () => {
if (isVideoMuted) {
client.unmuteVideo();
} else {
client.muteVideo();
}
};
Handle Participant Media Updates
When a participant in room turn on/turn off their camera or mute/unmute their microphone conference SDK will emit participant-media-update event. By listening to this event you can update UI accordingly like showing a mic muted icon on participant or putting an overlay to their video if they turned off their camera.
client.on('participant-media-update', handleParticipantMediaUpdate);
const handleParticipantMediaUpdate = (data) => {
console.log('Participant media update:', data);
const { streamName, videoEnabled, audioEnabled } = data;
setParticipantMuteStates(prev => ({
...prev,
[streamName]: {
videoMuted: !videoEnabled,
audioMuted: !audioEnabled
}
}));
};
Advanced Usage
Screen Sharing
Call startScreenShare() method on conference client.
const handleStartScreenShare = async () => {
try {
await client.startScreenShare({
width: 1920,
height: 1080,
frameRate: 30,
includeAudio: true
});
} catch (error) {
console.error('Failed to start screen share:', error);
setIsStartingScreenShare(false);
alert('Failed to start screen share: ' + error.message);
}
};
SDK will emit events screen-share-started, screen-share-stopped. You may use such events like below:
client.on('screen-share-started', handleScreenShareStarted);
client.on('screen-share-stopped', handleScreenShareStopped);
const handleScreenShareStarted = (data) => {
console.log('Screen share started:', data);
setIsScreenSharing(true);
setIsStartingScreenShare(false);
};
const handleScreenShareStopped = () => {
console.log('Screen share stopped');
setIsScreenSharing(false);
setIsStartingScreenShare(false);
};
Audio Level Monitoring
Conference client will emit audio level event for each subscribed participant periodically. You can use this event to display audio levels of participants on screen or as more real world example, you can use it to make a talking indicator.
client.on('audio-level', handleAudioLevel);
const handleAudioLevel = (data) => {
setAudioLevels(prev => ({
...prev,
[data.userId]: data.level.normalized
}));
};
Audio level data includes normalized, rms, dcb. You can use the one you want.
Sending/Receiving Chat Message
You can send and receive chat messages through Red5 Pro Stream Manager without relying on any 3rd party messaging service. You can use this to exchange any message, build an emoji functionality or room chat box.
client.sendChatMessage('Hello world!')
client.on('chat-message', handleChatMessage);
const handleChatMessage = (chatMessage) => {
console.log("Received chat message", chatMessage);
};
Change Video Publish Quality
You can set maximum video publish bitrate before joining a room or when you are in room and publishing.
While creating conference client, you can set maximum publish quality by passing maxVideoBitrateKbps.
client = new ConferenceClient({
host: 'your-cloud-deployment-host.red5pro.net',
nodeGroup: 'default',
maxVideoBitrateKbps: 3000,
iceServers: [{ urls: 'stun:stun2.l.google.com:19302' }]
});
If you have inited the SDK but not started to publish, you can still change it by reaching config object of conference client and set quality by:
client.config.maxVideoBitrateKbps = 1000
During publishing, you can change publish quality by calling:
client.setMaxVideoPublishKbps(1500)
Remote participants should see quality increase/drop as soon as you call this.
Switch Video/Audio Output Devices
Through conference client media stream manager, you can allow users to switch audio input/output and camera devices.
Switch Video Device
Use switchVideoDevice(), refreshPublisherStream(), cleanupCurrentMediaStream() and setCurrentStream() methods to achieve this on air or before publishing.
See this React example:
const cameraSelected = React.useCallback((value) => {
if (selectedCamera !== value) {
setSelectedDevices({ videoDeviceId: value })
try {
conferenceClient.current.mediaStreamManager.switchVideoDevice(value).then(mediaStream => {
const tempLocalVideo = document.getElementById('red5pro-publisher')
tempLocalVideo.srcObject = mediaStream
conferenceClient.current.refreshPublisherStream(mediaStream).then(r => {
conferenceClient.current.mediaStreamManager.cleanupCurrentMediaStream()
conferenceClient.current.mediaStreamManager.setCurrentStream(mediaStream)
})
})
} catch (e) {
log.log('Local stream is not ready yet.')
}
}
}, [selectedCamera, setSelectedDevices])
Switch Microphone Device
Call switchAudioDevice() and refresh users stream.
React example:
const microphoneSelected = React.useCallback((value) => {
if (selectedMicrophone !== value) {
setSelectedDevices({ audioDeviceId: value })
conferenceClient.current.mediaStreamManager.switchAudioDevice(value).then(mediaStream => {
const tempLocalVideo = document.getElementById('red5pro-publisher')
tempLocalVideo.srcObject = mediaStream
conferenceClient.current.refreshPublisherStream(mediaStream).then(r => {
conferenceClient.current.mediaStreamManager.cleanupCurrentMediaStream()
conferenceClient.current.mediaStreamManager.setCurrentStream(mediaStream)
})
})
}
}, [selectedMicrophone, setSelectedDevices])
Switch Audio Output Device
You can even switch audio output device of conference while in a room.
React example:
const speakerSelected = React.useCallback((value) => {
if (selectedSpeaker !== value) {
setSelectedDevices({ speakerDeviceId: value })
updateAudioOutput(value)
}
}, [selectedSpeaker, setSelectedDevices])
const updateAudioOutput = React.useCallback(async (selectedAudioOutput = selectedSpeaker) => {
console.log('updateAudioOutput', selectedAudioOutput)
if (!('setSinkId' in HTMLMediaElement.prototype) || isNull(selectedAudioOutput)) return
for (let key of Object.keys(participants)) {
const videoElement = document.getElementById(`red5pro-subscriber-${key}`);
if (!videoElement) {
console.warn(`No video element found for participant ${key}`);
subscribeToParticipants({ [key]: participants[key] })
continue;
}
if (typeof videoElement.setSinkId !== 'function') {
console.error('setSinkId is not supported in this browser.');
continue;
}
try {
await videoElement.setSinkId(selectedAudioOutput);
console.log('Audio output set for', key, 'to', selectedAudioOutput);
} catch (error) {
console.error('Error setting audio output for', key, ':', error);
}
}
}, [selectedSpeaker, participants])
Events
The Conference SDK uses an event-driven architecture. Here’s how to set up event listeners:
import ConferenceClient from './ConferenceClient';
const client = new ConferenceClient({
host: 'your-host.com',
nodeGroup: 'default',
iceServers: [{ urls: 'stun:stun2.l.google.com:19302' }]
});
// Set up event listeners
client.on('join-failed', handleJoinFail);
client.on('user-published', handleUserPublished);
client.on('new-participant', handleNewParticipant);
client.on('participant-disconnected', handleParticipantDisconnected);
client.on('subscribe-success', handleSubscribeSuccess);
client.on('subscribe-failed', handleSubscribeFailed);
client.on('subscribe-stop', handleSubscribeStop);
client.on('audio-level', handleAudioLevel);
client.on('audio-muted', handleAudioMuted);
client.on('video-muted', handleVideoMuted);
client.on('participant-media-update', handleParticipantMediaUpdate);
client.on('connection-closed', handleConnectionClosed);
client.on('connect-fail', handleConnectFail);
client.on('chat-message', handleChatMessage);
client.on('screen-share-started', handleScreenShareStarted);
client.on('screen-share-stopped', handleScreenShareStopped);
Connection Events
join-failed
Emitted when joining a room fails.
Data:
error– Error messagestatusCode– HTTP status code (401 for unauthorized)
client.on('join-failed', (data) => {
console.log('Join failed:', data.error);
if (data.statusCode === 401) {
// Handle unauthorized access
}
});
user-published
Emitted when the user successfully joins and publishes to the room.
Data:
participants– Object containing other participants in the roomroomState– Current room state information
client.on('user-published', (data) => {
setIsPublished(true);
setParticipants(data.participants);
});
connection-closed
Emitted when the publisher connection is closed.
client.on('connection-closed', () => {
// Handle connection loss
handleLeaveFromRoom();
});
connect-fail
Emitted when initial connection fails.
client.on('connect-fail', () => {
setIsJoining(false);
// Show connection error
});
Participant Events
new-participant
Emitted when a new participant joins the room.
Data:
participant– Participant information objectroomState– Updated room state
client.on('new-participant', (data) => {
setParticipants(prev => ({
...prev,
[data.participant.uid]: data.participant
}));
});
participant-disconnected
Emitted when a participant leaves the room.
Data:
participant– Participant information objectroomState– Updated room state
client.on('participant-disconnected', (data) => {
setParticipants(prev => {
const newParticipants = { ...prev };
delete newParticipants[data.participant.uid];
return newParticipants;
});
});
participant-media-update
Emitted when a participant’s media state changes (mute/unmute).
Data:
streamName– Participant’s stream namevideoEnabled– Whether video is enabledaudioEnabled– Whether audio is enabledtimestamp– Update timestamp
client.on('participant-media-update', (data) => {
setParticipants(prev => ({
...prev,
[data.streamName]: {
...prev[data.streamName],
videoEnabled: data.videoEnabled,
audioEnabled: data.audioEnabled
}
}));
});
Subscription Events
subscribe-success
Emitted when successfully subscribing to a participant’s stream.
Data:
uid– Participant’s unique identifiermediaStream– MediaStream objectparticipant– Participant information
client.on('subscribe-success', (data) => {
setSubscribedParticipants(prev => ({
...prev,
[data.uid]: {
participant: data.participant,
mediaStream: data.mediaStream
}
}));
});
subscribe-failed
Emitted when subscription to a participant fails.
Data:
user– User object withuidpropertyerror– Error message
client.on('subscribe-failed', (data) => {
console.error('Subscribe failed for:', data.user.uid, data.error);
// Handle retry logic or remove participant
});
subscribe-stop
Emitted when a subscription stops.
Data:
uid– Participant’s unique identifier
client.on('subscribe-stop', (data) => {
setSubscribedParticipants(prev => {
const newSubscribed = { ...prev };
delete newSubscribed[data.uid];
return newSubscribed;
});
});
Audio/Video Events
audio-level
Emitted periodically with audio level data for participants.
Data:
userId– Participant’s user IDlevel– Audio level object containing:normalized– Level from 0-100rms– Root Mean Square valuedb– Decibel level
client.on('audio-level', (data) => {
if (data.level.normalized > 75) {
// Participant is talking
setTalkers(prev => [...prev, data.userId]);
}
});
audio-muted
Emitted when local audio is muted/unmuted.
Data:
muted– Boolean indicating mute state
client.on('audio-muted', (data) => {
setIsMyMicMuted(data.muted);
});
video-muted
Emitted when local video is muted/unmuted.
Data:
muted– Boolean indicating mute state
client.on('video-muted', (data) => {
setIsMyCamTurnedOff(data.muted);
});
Screen Share Events
screen-share-started
Emitted when screen sharing starts successfully.
Data:
streamName– Screen share stream name (usually ends with ‘-screenshare’)stream– MediaStream object (optional)
client.on('screen-share-started', (data) => {
setIsScreenShared(true);
if (data.stream) {
// Handle screen share stream
}
});
screen-share-stopped
Emitted when screen sharing stops.
client.on('screen-share-stopped', () => {
setIsScreenShared(false);
});
Chat Events
chat-message
Emitted when receiving a chat message from another participant.
Data:
senderStreamName– Name of the message senderchatMessageText– JSON string containing message data
client.on('chat-message', (chatMessage) => {
const message = JSON.parse(chatMessage.chatMessageText);
if (message.eventType === 'MESSAGE_RECEIVED') {
setMessages(prev => [...prev, message]);
} else if (message.eventType === 'REACTIONS') {
showReactions(message.senderStreamId, message.reaction);
} else if (message.eventType === 'RAISED_HAND') {
handleRaisedHand(message);
}
});
Event Handler Examples
function handleJoinFail(data) {
setIsJoining(false);
if (data.statusCode === 401) {
setUnAuthorizedDialogOpen(true);
}
}
function handleUserPublished(data) {
setIsJoining(false);
setIsPublished(true);
setParticipants(data.participants);
setlobbyOrMeetingPage('meeting');
}
function handleNewParticipant(data) {
setParticipants(prev => ({
...prev,
[data.participant.uid]: data.participant
}));
}
function handleParticipantDisconnected(data) {
// Clean up UI references
clearRemoteSubscriber(data.participant.uid);
// Remove from participants
setParticipants(prev => {
const newParticipants = { ...prev };
delete newParticipants[data.participant.uid];
return newParticipants;
});
}
function handleAudioLevel(data) {
// Update talking indicators
if (data.level.normalized > 75) {
setTalkers(prev => [...prev, data.userId]);
}
}
function handleChatMessage(chatMessage) {
const message = JSON.parse(chatMessage.chatMessageText);
switch (message.eventType) {
case 'MESSAGE_RECEIVED':
if (!messageDrawerOpen) {
setNumberOfUnReadMessages(prev => prev + 1);
// Show notification
enqueueSnackbar(message.message, {
sender: message.name,
variant: 'message'
});
}
setMessages(prev => [...prev, message]);
break;
case 'REACTIONS':
showReactions(message.senderStreamId, message.reaction);
break;
case 'RAISED_HAND':
if (message.isRaisedHand) {
setRaisedHands(prev => [...prev, message.senderStreamId]);
} else {
setRaisedHands(prev =>
prev.filter(id => id !== message.senderStreamId)
);
}
break;
}
}
Removing Event Listeners
// Remove specific listener
client.off('join-failed', handleJoinFail);
// Clean up when component unmounts
useEffect(() => {
return () => {
// Remove all listeners by calling leave
client.leave();
};
}, []);
Common Patterns
Error Handling
client.on('subscribe-failed', (data) => {
console.error('Subscribe failed:', data.error);
// Implement retry logic or remove participant from UI
});
client.on('connect-fail', () => {
setIsJoining(false);
displayMessage('Failed to connect to the server');
});
State Management
// Update participants when they join/leave
client.on('new-participant', handleNewParticipant);
client.on('participant-disconnected', handleParticipantDisconnected);
// Track media states
client.on('participant-media-update', handleParticipantMediaUpdate);
client.on('audio-muted', (data) => setIsMyMicMuted(data.muted));
client.on('video-muted', (data) => setIsMyCamTurnedOff(data.muted));
UI Updates
// Show loading states
client.on('user-published', () => {
setIsJoining(false);
setlobbyOrMeetingPage('meeting');
});
// Handle talking indicators
client.on('audio-level', (data) => {
updateTalkerLevel(data.userId, data.level.normalized);
});