MOQ Beta | Now Open For Enterprises / Learn More

Red5 Documentation

iOS SDK

Build low-latency live streaming apps with the Red5 iOS SDK. Stream video using WHIP, and subscribe (play) streams via WHEP. Compatible with both Red5 Pro Cloud (Stream Manager) and standalone Red5 Pro 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

  • Red5 Pro 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

// Build client
let webrtcClient = Red5WebrtcClientBuilder()
    .setStreamManagerHost("userid-xxx-xxx.cloud.red5.net")
    .setPort(443)
    .setAppName("live")
    .setStreamName("myStream")
    .setVideoEnabled(true)
    .setAudioEnabled(true)
    .setVideoWidth(640)
    .setVideoHeight(480)
    .setVideoFps(30)
    .setVideoBitrate(750)
    .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 Red5 Pro 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 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
    }

    AVCaptureDevice.requestAccess(for: .video) { cameraGranted in
        guard cameraGranted else {
            completion(false)
            return
        }

        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.

Configuration Options:

  • 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 and setPort() if different from default (443)
let webrtcClient = Red5WebrtcClientBuilder()
    .setStreamManagerHost("userid-xxx-xxx.cloud.red5.net") // For cloud
    // .setServerIp("192.168.1.100") // For standalone
    .setPort(443)
    .setAppName("live")
    .setStreamName("myStreamName")
    .setUserName("username") // If username/password auth enabled
    .setPassword("password") // If username/password auth enabled
    .setToken("authToken")   // If token auth enabled
    .setVideoEnabled(true)
    .setAudioEnabled(true)
    .setVideoWidth(640)
    .setVideoHeight(480)
    .setVideoFps(30)
    .setVideoBitrate(750)
    .setNodeGroup("default") // Optional: specify node group for cloud
    .setEventListener(self)
    .build()

Step 4: Setup Video Renderer

Create and configure the video renderer for displaying the camera preview:

let videoRenderer = RTCMTLVideoView()
videoRenderer.contentMode = .scaleAspectFill
videoRenderer.videoContentMode = .scaleAspectFill

webrtcClient.setVideoRenderer(videoRenderer)

For SwiftUI:

struct VideoRendererView: UIViewRepresentable {
    let renderer: RTCMTLVideoView

    func makeUIView(context: Context) -> RTCMTLVideoView {
        return renderer
    }

    func updateUIView(_ uiView: RTCMTLVideoView, context: Context) {}
}

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() to start publishing:

webrtcClient.publish()

Subscribing to Red5 Cloud and Standalone Streams

Step 1: Import the SDK

import SwiftUI
import WebRTC
import Red5WebRTCKit

Step 2: Create Red5WebrtcClient with Red5WebrtcClientBuilder()

Configure the client for subscription. Use the same host configuration as publishing:

let webrtcClient = Red5WebrtcClientBuilder()
    .setStreamManagerHost("userid-755-2ccfa36e4c.cloud.red5.net") // For cloud
    // .setServerIp("192.168.1.100") // For standalone
    .setPort(443)
    .setAppName("live")
    .setStreamName("myStreamName")
    .setUserName("username") // If auth enabled
    .setPassword("password") // If auth enabled
    .setToken("authToken")   // If token auth enabled
    .setEventListener(self)
    .build()

Step 3: Setup Video Renderer

let videoRenderer = RTCMTLVideoView()
videoRenderer.contentMode = .scaleAspectFill
videoRenderer.videoContentMode = .scaleAspectFill

webrtcClient.setVideoRenderer(videoRenderer)

Step 4: Start Subscribing

webrtcClient.subscribe()

Events

The iOS SDK uses an event-driven architecture. Implement Red5ProWebrtcEventDelegate in your class to handle SDK events.

Event Types

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

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() {
        self.localVideoRenderer = RTCMTLVideoView()
        self.localVideoRenderer?.contentMode = .scaleAspectFill
        self.localVideoRenderer?.videoContentMode = .scaleAspectFill

        webrtcClient = Red5WebrtcClientBuilder()
            .setStreamManagerHost("userid-xxx-xxx.cloud.red5.net")
            .setPort(443)
            .setAppName("live")
            .setStreamName("myStream")
            .setVideoEnabled(true)
            .setAudioEnabled(true)
            .setVideoWidth(640)
            .setVideoHeight(480)
            .setVideoFps(30)
            .setVideoBitrate(750)
            .setEventListener(self)
            .build()

        webrtcClient?.setVideoRenderer(self.localVideoRenderer!)
    }

    func startPublish() { webrtcClient?.publish() }
    func stopPublish() { webrtcClient?.stopPublish() }
    func stopPreview() { webrtcClient?.stopPreview() }

    func release() {
        webrtcClient?.stopPublish()
        webrtcClient?.stopPreview()
        webrtcClient = nil
        localVideoRenderer = nil
    }
}

extension PublishManager: Red5ProWebrtcEventDelegate {
    func onLicenseValidated(validated: Bool, message: String) {
        DispatchQueue.main.async {
            if validated {
                self.webrtcClient?.startPreview()
            } else {
                print("License invalid: (message)")
            }
        }
    }

    func onPreviewStarted() {
        DispatchQueue.main.async { self.isReady = true }
    }

    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 failed: (error)") } }
    func onError(error: String) { DispatchQueue.main.async { print("Error: (error)") } }
    func onPreviewStopped() { DispatchQueue.main.async { print("Preview stopped") } }
    func onIceConnectionStateChanged(state: IceConnectionState) { DispatchQueue.main.async { print("ICE state: (state)") } }
    func onConnectionStateChanged(state: PeerConnectionState) { DispatchQueue.main.async { print("Connection state: (state)") } }
}

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

Examples

Each example is a self-contained iOS app demonstrating one specific feature. Copy the two files (Config.swift + the example file) into a fresh Xcode project, edit Config.swift with your server details, and run.

API Reference

  • API Reference — complete reference for all classes, protocols, methods, and enums