Red5 Documentation

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

bash
npm install red5pro-webrtc-sdk

Include the SDK Files

Package Status: The SDK package is not published yet. In the meantime, please reach out to our support team to obtain the package..

Include ConferenceClient.ts and MediaStreamManager.js in same directory:

javascript
import { ConferenceClient } from './ConferenceClient';

Quick Start

Here’s a minimal example to get you started:

javascript
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()

javascript
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:

javascript
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.

javascript
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.

javascript
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.

javascript
client.leave()

Mute Video/Audio

You can turn off/on camera or mute/unmute your microphone.

javascript
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.

javascript
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.

javascript
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:

javascript
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.

javascript
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.

javascript
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.

javascript
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:

javascript
client.config.maxVideoBitrateKbps = 1000

During publishing, you can change publish quality by calling:

javascript
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:

javascript
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:

javascript
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:

javascript
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:

javascript
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 message
  • statusCode – HTTP status code (401 for unauthorized)
javascript
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 room
  • roomState – Current room state information
javascript
client.on('user-published', (data) => {
  setIsPublished(true);
  setParticipants(data.participants);
});

connection-closed

Emitted when the publisher connection is closed.

javascript
client.on('connection-closed', () => {
  // Handle connection loss
  handleLeaveFromRoom();
});

connect-fail

Emitted when initial connection fails.

javascript
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 object
  • roomState – Updated room state
javascript
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 object
  • roomState – Updated room state
javascript
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 name
  • videoEnabled – Whether video is enabled
  • audioEnabled – Whether audio is enabled
  • timestamp – Update timestamp
javascript
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 identifier
  • mediaStream – MediaStream object
  • participant – Participant information
javascript
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 with uid property
  • error – Error message
javascript
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
javascript
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 ID
  • level – Audio level object containing:
    • normalized – Level from 0-100
    • rms – Root Mean Square value
    • db – Decibel level
javascript
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
javascript
client.on('audio-muted', (data) => {
  setIsMyMicMuted(data.muted);
});

video-muted

Emitted when local video is muted/unmuted.

Data:

  • muted – Boolean indicating mute state
javascript
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)
javascript
client.on('screen-share-started', (data) => {
  setIsScreenShared(true);
  if (data.stream) {
    // Handle screen share stream
  }
});

screen-share-stopped

Emitted when screen sharing stops.

javascript
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 sender
  • chatMessageText – JSON string containing message data
javascript
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

javascript
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

javascript
// 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

javascript
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

javascript
// 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

javascript
// 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);
});