Red5 iOS SDK
Overview
Build low-latency live streaming apps with the Red5 iOS SDK. Stream video using whip, and subscribe (play) streams via whep. Compatible with both Red5Pro Cloud (Stream Manager) and standalone Red5Pro servers.
Key Features
- Publishing: Low-latency video and audio streaming
- Subscription: Real-time stream playback
- Cloud & Standalone Support: Works with Stream Manager and standalone servers
- Camera Control: Switch cameras, toggle video on/off
- Audio Control: Mute/unmute microphone
- Conferencing: Multi-user room support
- Chat Support: Built-in messaging via PubNub
- SwiftUI Compatible: Works with modern iOS development patterns
Installation
Add the Red5 iOS SDK framework to your Xcode project.
Using Swift Package Manager
dependencies: [
.package(url: "https://github.com/red5pro/red5pro-ios-sdk", from: "latest")
]
Configuration Notes
Ensure you have the necessary permissions in your Info.plist for publishing:
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to stream video</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to stream audio</string>
Requirements
- Red5Pro SDK license key
- Camera and microphone permissions for publishing
- iOS 13.0 or later
- Xcode 14.0 or later
- Swift 5.5 or later
Quick Start
Here’s a minimal example to get you started with publishing:
import WebRTC
import Red5WebRTCKit
// Create video renderer
let videoRenderer = RTCMTLVideoView()
videoRenderer.contentMode = .scaleAspectFill
videoRenderer.videoContentMode = .scaleAspectFill
// Create configuration
let config = Red5WebrtcClientConfig()
config.streamManagerHost = "userid-xxx-xxx.cloud.red5.net"
config.appName = "live"
config.streamName = "myStream"
config.userName = "username" // If auth enabled
config.password = "password" // If auth enabled
config.videoEnabled = true
config.audioEnabled = true
config.videoWidth = 640
config.videoHeight = 480
config.videoFps = 30
config.videoBitrate = 750
config.videoRenderer = videoRenderer
// Build client
let webrtcClient = Red5WebrtcClientBuilder()
.setStreamManagerHost(config.streamManagerHost ?? "")
.setPort(443)
.setAppName(config.appName)
.setStreamName(config.streamName ?? "")
.setVideoEnabled(config.videoEnabled)
.setAudioEnabled(config.audioEnabled)
.setVideoWidth(config.videoWidth)
.setVideoHeight(config.videoHeight)
.setVideoFps(config.videoFps)
.setVideoBitrate(config.videoBitrate)
.setEventListener(self)
.build()
// Set renderer on client
webrtcClient.setVideoRenderer(videoRenderer)
// Start preview
webrtcClient.startPreview()
// Publish a stream
webrtcClient.publish()
// Subscribe to a stream
webrtcClient.subscribe()
Basic Usage
The SDK supports both publishing to Red5 Cloud (Stream Manager deployments) and standalone Red5Pro servers, as well as subscription (playback) for all streams on both cloud and standalone deployments.
To publish: Request camera/microphone permissions, create a Red5WebrtcClient using Red5WebrtcClientBuilder(), set up a video renderer, start preview, and call webrtcClient.publish().
To subscribe: Create a Red5WebrtcClient using Red5WebrtcClientBuilder(), set up a video renderer, and call webrtcClient.subscribe().
Publishing to Red5 Cloud and Standalone
Step 1: Import the SDK
Import the necessary frameworks in your Swift file:
import SwiftUI
import AVFoundation
import WebRTC
import Red5WebRTCKit
Step 2: Request Publish Permissions
These permissions are required for publishing. Request them before starting the stream:
import AVFoundation
func checkPermissions(completion: @escaping (Bool) -> Void) {
let cameraStatus = AVCaptureDevice.authorizationStatus(for: .video)
let micStatus = AVCaptureDevice.authorizationStatus(for: .audio)
if cameraStatus == .authorized && micStatus == .authorized {
completion(true)
return
}
// Request camera permission
AVCaptureDevice.requestAccess(for: .video) { cameraGranted in
guard cameraGranted else {
completion(false)
return
}
// Request microphone permission
AVCaptureDevice.requestAccess(for: .audio) { micGranted in
completion(micGranted)
}
}
}
Step 3: Create Red5WebrtcClient with Red5WebrtcClientBuilder()
Create the WebRTC client when publish permissions are granted. This single object handles all streaming configuration.
- For Red5 Cloud (Stream Manager): Use
setStreamManagerHost()with your stream manager host address (e.g.,userid-xxx-xxx.cloud.red5.net) - For Standalone Server: Use
setServerIp()with your server IP address andsetPort()if different from default (443)
// Create configuration
let config = Red5WebrtcClientConfig()
config.streamManagerHost = "userid-xxx-xxx.cloud.red5.net" // For cloud
// config.serverIp = "192.168.1.100" // For standalone
config.port = 443 // Default is 443
config.appName = "live"
config.streamName = "myStreamName"
config.userName = "username" // If username/password auth enabled
config.password = "password" // If username/password auth enabled
config.token = "authToken" // If token auth enabled
config.videoEnabled = true
config.audioEnabled = true
config.videoWidth = 640
config.videoHeight = 480
config.videoFps = 30
config.videoBitrate = 750
config.nodeGroup = "default" // Optional: specify node group for cloud
// Build the client
let webrtcClient = Red5WebrtcClientBuilder()
.setStreamManagerHost(config.streamManagerHost ?? "")
.setPort(config.port)
.setAppName(config.appName)
.setStreamName(config.streamName ?? "")
.setUserName(config.userName ?? "")
.setPassword(config.password ?? "")
.setToken(config.token ?? "")
.setVideoEnabled(config.videoEnabled)
.setAudioEnabled(config.audioEnabled)
.setVideoWidth(config.videoWidth)
.setVideoHeight(config.videoHeight)
.setVideoFps(config.videoFps)
.setVideoBitrate(config.videoBitrate)
.setNodeGroup(config.nodeGroup ?? "default")
.setEventListener(self)
.build()
Step 4: Setup Video Renderer
Create and configure the video renderer for displaying the camera preview:
// Create the video renderer
let videoRenderer = RTCMTLVideoView()
videoRenderer.contentMode = .scaleAspectFill
videoRenderer.videoContentMode = .scaleAspectFill
// Set the renderer on the client
webrtcClient.setVideoRenderer(videoRenderer)
For SwiftUI:
struct VideoRendererView: UIViewRepresentable {
let renderer: RTCMTLVideoView
func makeUIView(context: Context) -> RTCMTLVideoView {
return renderer
}
func updateUIView(_ uiView: RTCMTLVideoView, context: Context) {
// No updates needed
}
}
// Usage in SwiftUI
struct ContentView: View {
@StateObject private var publishManager = PublishManager()
var body: some View {
if let renderer = publishManager.localVideoRenderer {
VideoRendererView(renderer: renderer)
.edgesIgnoringSafeArea(.all)
}
}
}
Step 5: Start Preview
When webrtcClient is created, it performs a license check. Implement Red5ProWebrtcEventDelegate in your class and override onLicenseValidated:
extension YourClass: Red5ProWebrtcEventDelegate {
func onLicenseValidated(validated: Bool, message: String) {
if validated {
webrtcClient.startPreview()
print("License check success")
} else {
print("License check failed: \(message)")
}
}
}
After successful validation, call webrtcClient.startPreview() to see the camera preview rendering on the video view.
Step 6: Start Publishing
Call webrtcClient.publish("streamName") to start publishing:
webrtcClient.publish("streamName")
Subscribing to Red5 Cloud and Standalone Streams
Step 1: Import the SDK
Import the necessary frameworks in your Swift file:
import SwiftUI
import WebRTC
import Red5WebRTCKit
Step 2: Create Red5WebrtcClient with Red5WebrtcClientBuilder()
Configure the client for subscription. Use the same host configuration as publishing:
// Create configuration
let config = Red5WebrtcClientConfig()
config.streamManagerHost = "userid-755-2ccfa36e4c.cloud.red5.net" // For cloud
// config.serverIp = "192.168.1.100" // For standalone
config.port = 443
config.appName = "live"
config.streamName = "myStreamName"
config.userName = "username" // If auth enabled
config.password = "password" // If auth enabled
config.token = "authToken" // If token auth enabled
// Build the client
let webrtcClient = Red5WebrtcClientBuilder()
.setStreamManagerHost(config.streamManagerHost ?? "")
.setPort(config.port)
.setAppName(config.appName)
.setStreamName(config.streamName ?? "")
.setUserName(config.userName ?? "")
.setPassword(config.password ?? "")
.setToken(config.token ?? "")
.setEventListener(self)
.build()
Step 3: Setup Video Renderer
Create and configure the video renderer for displaying the remote stream:
// Create the video renderer
let videoRenderer = RTCMTLVideoView()
videoRenderer.contentMode = .scaleAspectFill
videoRenderer.videoContentMode = .scaleAspectFill
// Set the renderer on the client
webrtcClient.setVideoRenderer(videoRenderer)
Step 4: Start Subscribing
Call webrtcClient.subscribe("streamName") to start subscribing:
webrtcClient.subscribe("streamName")
Events
The iOS SDK uses an event-driven architecture. Implement Red5ProWebrtcEventDelegate in your class to handle SDK events.
Event Types
The SDK emits the following events through the Red5ProWebrtcEventDelegate protocol:
| Event | Description | Parameters |
|---|---|---|
onPublishStarted() |
Publishing started successfully | None |
onPublishStopped() |
Publishing stopped | None |
onPublishFailed(error:) |
Publishing failed | error: String |
onSubscribeStarted() |
Subscription started successfully | None |
onSubscribeStopped() |
Subscription stopped | None |
onSubscribeFailed(error:) |
Subscription failed | error: String |
onIceConnectionStateChanged(state:) |
ICE connection state changed | state: IceConnectionState |
onConnectionStateChanged(state:) |
Peer connection state changed | state: PeerConnectionState |
onError(error:) |
General error occurred | error: String |
onPreviewStarted() |
Camera preview started | None |
onPreviewStopped() |
Camera preview stopped | None |
onLicenseValidated(validated:message:) |
License validation completed | validated: Bool, message: String |
onChatMessageReceived(channel:message:) |
Chat message received | channel: String, message: JSONCodable |
onChatConnected() |
Chat connection established | None |
onChatDisconnected() |
Chat connection closed | None |
onChatSendError(channel:errorMessage:) |
Failed to send chat message | channel: String, errorMessage: String |
onChatSendSuccess(channel:timetoken:) |
Chat message sent successfully | channel: String, timetoken: NSNumber |
Connection State Handling
Handle connection state changes using IceConnectionState:
extension YourClass: Red5ProWebrtcEventDelegate {
func onIceConnectionStateChanged(state: IceConnectionState) {
DispatchQueue.main.async {
switch state {
case .connected:
print("Connected to server")
case .disconnected, .failed:
print("Connection lost")
default:
break
}
}
}
}
Full Working Example
import SwiftUI
import AVFoundation
import WebRTC
import Red5WebRTCKit
class PublishManager: NSObject, ObservableObject {
private var webrtcClient: Red5WebrtcClient?
@Published var localVideoRenderer: RTCMTLVideoView?
@Published var isReady: Bool = false
func setup() {
// Create renderer
self.localVideoRenderer = RTCMTLVideoView()
self.localVideoRenderer?.contentMode = .scaleAspectFill
self.localVideoRenderer?.videoContentMode = .scaleAspectFill
// Configure client
let config = Red5WebrtcClientConfig()
config.streamManagerHost = "userid-xxx-xxx.cloud.red5.net"
config.port = 443
config.appName = "live"
config.streamName = "myStream"
config.videoEnabled = true
config.audioEnabled = true
config.videoWidth = 640
config.videoHeight = 480
config.videoFps = 30
config.videoBitrate = 750
config.videoRenderer = self.localVideoRenderer
// Build client
webrtcClient = Red5WebrtcClientBuilder()
.setStreamManagerHost(config.streamManagerHost ?? "")
.setPort(config.port)
.setAppName(config.appName)
.setStreamName(config.streamName ?? "")
.setVideoEnabled(config.videoEnabled)
.setAudioEnabled(config.audioEnabled)
.setVideoWidth(config.videoWidth)
.setVideoHeight(config.videoHeight)
.setVideoFps(config.videoFps)
.setVideoBitrate(config.videoBitrate)
.setEventListener(self)
.build()
// Set renderer
webrtcClient?.setVideoRenderer(self.localVideoRenderer!)
}
func startPreview() {
webrtcClient?.startPreview()
}
func startPublish() {
webrtcClient?.publish()
}
func stopPublish() {
webrtcClient?.stopPublish()
}
func stopPreview() {
webrtcClient?.stopPreview()
}
func release() {
webrtcClient?.stopPublish()
webrtcClient?.stopPreview()
webrtcClient = nil
localVideoRenderer = nil
}
}
// MARK: - Red5ProWebrtcEventDelegate
extension PublishManager: Red5ProWebrtcEventDelegate {
func onPublishStarted() {
DispatchQueue.main.async {
print("Publish started")
}
}
func onPublishStopped() {
DispatchQueue.main.async {
print("Publish stopped")
}
}
func onPublishFailed(error: String) {
DispatchQueue.main.async {
print("Publish failed: \(error)")
}
}
func onSubscribeStarted() {
DispatchQueue.main.async {
print("Subscribe started")
}
}
func onSubscribeStopped() {
DispatchQueue.main.async {
print("Subscribe stopped")
}
}
func onSubscribeFailed(error: String) {
DispatchQueue.main.async {
print("Subscribe error: \(error)")
}
}
func onIceConnectionStateChanged(state: IceConnectionState) {
DispatchQueue.main.async {
print("ICE connection state: \(state)")
}
}
func onConnectionStateChanged(state: PeerConnectionState) {
DispatchQueue.main.async {
print("Connection state: \(state)")
}
}
func onError(error: String) {
DispatchQueue.main.async {
print("Error: \(error)")
}
}
func onPreviewStarted() {
DispatchQueue.main.async {
print("Preview started")
self.isReady = true
}
}
func onPreviewStopped() {
DispatchQueue.main.async {
print("⏹️ Preview stopped")
}
}
func onLicenseValidated(validated: Bool, message: String) {
DispatchQueue.main.async {
if validated {
self.webrtcClient?.startPreview()
} else {
print("License invalid: \(message)")
}
}
}
}
Advanced Usage
Turn Off/On Camera
Toggle camera on/off during streaming:
private var isCameraEnabled = true
func toggleCamera() {
isCameraEnabled.toggle()
webrtcClient.setVideoEnabled(isCameraEnabled)
}
Switch Camera
Switch between front and back cameras:
func switchCamera() {
webrtcClient.switchCamera()
}
Mute/Unmute Microphone
Toggle microphone on/off during streaming:
private var isMicEnabled = true
func toggleMic() {
isMicEnabled.toggle()
webrtcClient.setAudioEnabled(isMicEnabled)
}
Conferencing (Multi-user Rooms)
Join multi-user conference rooms to publish and subscribe to multiple participants.
Step 1: Join a Room
let roomId = "myConferenceRoom"
let userId = "user123"
let role = "publisher" // or "subscriber"
let metadata = "{\"displayName\": \"John Doe\"}"
webrtcClient.join(roomId: roomId, streamName: userId, role: role, metadata: metadata)
Step 2: Handle Room Events
Implement ConferenceDelegate to be notified when participants join or leave:
| Event | Description | Parameters |
|---|---|---|
onJoinRoomSuccess |
Successfully joined room | roomId: String, participants: [Red5ConferenceParticipant] |
onJoinRoomFailed |
Failed to join room | statusCode: Int, message: String |
onParticipantJoined |
New participant joined | uid: String, role: String, metaData: String, videoEnabled: Bool, audioEnabled: Bool, renderer: RTCVideoRenderer? |
onParticipantLeft |
Participant left room | uid: String |
onParticipantMediaUpdate |
Participant’s media state changed | uid: String, videoEnabled: Bool, audioEnabled: Bool, timestamp: Int64 |
onParticipantRendererUpdate |
Participant’s video renderer updated | uid: String, renderer: RTCVideoRenderer |
extension YourManager: ConferenceDelegate {
func onJoinRoomSuccess(roomId: String, participants: [Red5ConferenceParticipant]) {
print("Joined room: \(roomId)")
}
func onParticipantJoined(uid: String, role: String, metaData: String, videoEnabled: Bool, audioEnabled: Bool, renderer: RTCVideoRenderer?) {
print("Participant joined: \(uid)")
// Attach renderer if available
}
}
Step 3: Leave Room
webrtcClient.leave()
Chat Support
The SDK includes built-in chat support via PubNub.
Step 1: Configure Chat
Use Red5WebrtcClientBuilder to set your PubNub keys:
let webrtcClient = Red5WebrtcClientBuilder()
.setPubnubPublishKey("your_publish_key")
.setPubnubSubscribeKey("your_subscribe_key")
// ... other config
.build()
Step 2: Join and Send Messages
// Subscribe to a channel
webrtcClient.subscribeChatChannel(channelName: "main-chat")
// Send a text message
webrtcClient.sendChatTextMessage(channelName: "main-chat", message: "Hello World!", metaData: nil)
Step 3: Receive Messages
Implement onChatMessageReceived in your Red5ProWebrtcEventDelegate:
func onChatMessageReceived(channel: String, message: JSONCodable) {
print("Received message in \(channel): \(message)")
}
Chat Events
| Event | Description |
|---|---|
onChatConnected() |
Successfully connected to chat service |
onChatDisconnected() |
Disconnected from chat service |
onChatMessageReceived(channel:message:) |
New chat message received |
onChatSendSuccess(channel:timetoken:) |
Message sent successfully |
onChatSendError(channel:errorMessage:) |
Failed to send message |