📱 Mobile SDK Integration

Complete guide for integrating Pubfuse streaming into iOS and Android applications

🔌 WebSocket Connection

Real-time chat, reactions, and video signaling

wss://www.pubfuse.com/ws/streams/{id}/watch

📺 HLS Streaming

High-quality video playback

https://www.pubfuse.com/hls/{id}.m3u8

🚀 LiveKit SFU

Scalable streaming with 100+ participants

wss://your-project.livekit.cloud

🚀 Quick Start

1. Get Stream ID

# Get available streams
curl -X GET "https://www.pubfuse.com/api/sessions" \
  -H "X-API-Key: your-api-key"

# Or from metrics dashboard (recommended)
curl -X GET "https://www.pubfuse.com/api/metrics/dashboard" \
  -H "X-API-Key: your-api-key"

2. Construct WebSocket URL

🔧 Dynamic Protocol Detection: The WebSocket URL must be constructed based on your base URL:

// JavaScript Example - Dynamic Protocol Detection
function constructWebSocketURL(streamId, baseURL) {
    // Determine protocol based on base URL
    const scheme = baseURL.startsWith('https://') ? 'wss://' : 'ws://';
    const host = baseURL.replace(/^https?:\/\//, '');
    
    return `${scheme}${host}/ws/streams/${streamId}/watch`;
}

// Usage Examples:
const productionURL = constructWebSocketURL(
    "123e4567-e89b-12d3-a456-426614174000", 
    "https://www.pubfuse.com"
);
// Results in: wss://www.pubfuse.com/ws/streams/123e4567-e89b-12d3-a456-426614174000/watch

const developmentURL = constructWebSocketURL(
    "123e4567-e89b-12d3-a456-426614174000", 
    "http://localhost:8080"
);
// Results in: ws://localhost:8080/ws/streams/123e4567-e89b-12d3-a456-426614174000/watch

3. Connect to WebSocket

// JavaScript Example
const streamId = "123e4567-e89b-12d3-a456-426614174000";
const ws = new WebSocket(`wss://www.pubfuse.com/ws/streams/${streamId}/watch`);

ws.onopen = () => {
    // Send viewer-hello to join stream
    ws.send(JSON.stringify({
        type: 'viewer-hello',
        clientId: 'your-client-id',
        role: 'viewer'
    }));
};

ws.onmessage = (event) => {
    const message = JSON.parse(event.data);
    // Handle chat, reactions, video signals, etc.
};

3. Play HLS Stream


🍎 iOS Implementation

import Foundation
import Network

class PubfuseStreamingSDK {
    private let baseURL: String
    private let clientId = UUID().uuidString
    
    init(baseURL: String = "https://www.pubfuse.com") {
        self.baseURL = baseURL
    }
    
    private func constructWebSocketURL(streamId: String) -> String {
        // Determine protocol based on base URL
        let scheme = baseURL.hasPrefix("https://") ? "wss://" : "ws://"
        let host = baseURL.replacingOccurrences(of: "https://", with: "")
                          .replacingOccurrences(of: "http://", with: "")
        
        return "\(scheme)\(host)/ws/streams/\(streamId)/watch"
    }
    
    func connect(to streamId: String) async {
        let webSocketURL = URL(string: constructWebSocketURL(streamId: streamId))!
        let webSocketTask = URLSession.shared.webSocketTask(with: webSocketURL)
        webSocketTask.resume()
        
        // Send viewer-hello
        let message = [
            "type": "viewer-hello",
            "clientId": clientId,
            "role": "viewer"
        ]
        // ... implementation details
    }
}

🤖 Android Implementation

import okhttp3.*
import org.json.JSONObject

class PubfuseStreamingSDK {
    private val baseURL: String
    private val clientId = UUID.randomUUID().toString()
    
    constructor(baseURL: String = "https://www.pubfuse.com") {
        this.baseURL = baseURL
    }
    
    private fun constructWebSocketURL(streamId: String): String {
        // Determine protocol based on base URL
        val scheme = if (baseURL.startsWith("https://")) "wss://" else "ws://"
        val host = baseURL.removePrefix("https://").removePrefix("http://")
        
        return "$scheme$host/ws/streams/$streamId/watch"
    }
    
    fun connect(streamId: String) {
        val url = constructWebSocketURL(streamId)
        val request = Request.Builder().url(url).build()
        
        webSocket = client.newWebSocket(request, object : WebSocketListener() {
            override fun onOpen(webSocket: WebSocket, response: Response) {
                // Send viewer-hello
                val message = JSONObject().apply {
                    put("type", "viewer-hello")
                    put("clientId", clientId)
                    put("role", "viewer")
                }
                webSocket.send(message.toString())
            }
        })
    }
}

📨 Message Protocol

All WebSocket messages use JSON format:

{
  "type": "message-type",
  "clientId": "unique-client-identifier",
  "message": "optional-content",
  "timestamp": 1234567890.123
}

Supported Message Types

Type Direction Purpose
viewer-hello Client → Server Join stream as viewer
welcome Server → Client Join confirmation
chat Client ↔ Server Chat messages
reaction Client ↔ Server Live reactions (heart, like, etc.)
sdp-offer Publisher → Viewers WebRTC SDP offer
sdp-answer Viewer → Publisher WebRTC SDP answer
ice-candidate Both WebRTC ICE candidate
ad-trigger Publisher → Viewers Synchronized ad trigger
ping Client → Server Heartbeat
pong Server → Client Heartbeat response

🔧 Connection Flow

1
Connect - Establish WebSocket connection to wss://www.pubfuse.com/ws/streams/{id}/watch
2
Join - Send viewer-hello message to join the stream
3
Confirm - Receive welcome message from server
4
Interact - Send/receive chat, reactions, video signals
5
Maintain - Send ping every 30 seconds to keep connection alive

🚀 LiveKit SFU Integration

✨ NEW: LiveKit SFU Streaming

Pubfuse now supports LiveKit SFU streaming for better scalability and performance. LiveKit supports 100+ concurrent participants with built-in recording capabilities.

LiveKit Integration Benefits

iOS LiveKit Integration

import Foundation
import LiveKit

class PubfuseLiveKitSDK {
    private let baseURL: String
    private var room: Room?
    
    init(baseURL: String = "https://www.pubfuse.com") {
        self.baseURL = baseURL
    }
    
    func connect(to streamId: String) async throws {
        // 1. Get streaming provider configuration
        let providers = try await getStreamingProviders()
        guard let activeProvider = providers.first(where: { $0.isConfigured }) else {
            throw PubfuseError.noProviderConfigured
        }
        
        // 2. Get LiveKit access token
        let tokenResponse = try await getLiveKitToken(streamId: streamId)
        
        // 3. Create LiveKit room
        let room = Room()
        room.delegate = self
        
        // 4. Connect to LiveKit room
        try await room.connect(
            url: tokenResponse.serverUrl,
            token: tokenResponse.token,
            connectOptions: ConnectOptions(
                autoManageVideo: true,
                autoManageAudio: true
            )
        )
        
        self.room = room
    }
    
    private func getStreamingProviders() async throws -> [StreamingProvider] {
        guard let url = URL(string: "\(baseURL)/api/streaming/providers") else {
            throw PubfuseError.invalidURL
        }
        
        let (data, _) = try await URLSession.shared.data(from: url)
        return try JSONDecoder().decode([StreamingProvider].self, from: data)
    }
    
    private func getLiveKitToken(streamId: String) async throws -> LiveKitTokenResponse {
        guard let url = URL(string: "\(baseURL)/api/streaming/sessions/\(streamId)/token") else {
            throw PubfuseError.invalidURL
        }
        
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        let tokenRequest = LiveKitTokenRequest(
            userId: UUID().uuidString,
            role: "subscriber"
        )
        
        request.httpBody = try JSONEncoder().encode(tokenRequest)
        
        let (data, _) = try await URLSession.shared.data(for: request)
        return try JSONDecoder().decode(LiveKitTokenResponse.self, from: data)
    }
}

extension PubfuseLiveKitSDK: RoomDelegate {
    func room(_ room: Room, didConnect isReconnect: Bool) {
        print("✅ Connected to LiveKit room")
    }
    
    func room(_ room: Room, participant: RemoteParticipant, didSubscribeTo publication: RemoteTrackPublication) {
        if let videoTrack = publication.track as? VideoTrack {
            // Add video track to your video view
            videoTrack.addRenderer(videoView)
        }
    }
    
    func room(_ room: Room, participant: RemoteParticipant, didUnsubscribeFrom publication: RemoteTrackPublication) {
        // Handle participant leaving
    }
}

// Data Models
struct StreamingProvider: Codable {
    let provider: String
    let isConfigured: Bool
    let displayName: String
    let description: String
}

struct LiveKitTokenRequest: Codable {
    let userId: String
    let role: String
}

struct LiveKitTokenResponse: Codable {
    let room: String
    let serverUrl: String
    let token: String
    let expiresAt: String
}

enum PubfuseError: Error {
    case invalidURL
    case noProviderConfigured
}

Android LiveKit Integration

import livekit.*
import kotlinx.coroutines.*
import retrofit2.*

class PubfuseLiveKitSDK {
    private val baseURL: String
    private var room: Room? = null
    
    constructor(baseURL: String = "https://www.pubfuse.com") {
        this.baseURL = baseURL
    }
    
    suspend fun connect(streamId: String) {
        try {
            // 1. Get streaming provider configuration
            val providers = getStreamingProviders()
            val activeProvider = providers.firstOrNull { it.isConfigured }
                ?: throw PubfuseException("No streaming provider configured")
            
            // 2. Get LiveKit access token
            val tokenResponse = getLiveKitToken(streamId)
            
            // 3. Create LiveKit room
            val room = Room()
            room.addListener(this)
            
            // 4. Connect to LiveKit room
            room.connect(
                url = tokenResponse.serverUrl,
                token = tokenResponse.token,
                options = ConnectOptions(
                    autoManageVideo = true,
                    autoManageAudio = true
                )
            )
            
            this.room = room
        } catch (e: Exception) {
            throw PubfuseException("Failed to connect to LiveKit: ${e.message}")
        }
    }
    
    private suspend fun getStreamingProviders(): List {
        val response = apiService.getStreamingProviders()
        return response.body() ?: emptyList()
    }
    
    private suspend fun getLiveKitToken(streamId: String): LiveKitTokenResponse {
        val request = LiveKitTokenRequest(
            userId = UUID.randomUUID().toString(),
            role = "subscriber"
        )
        
        val response = apiService.getLiveKitToken(streamId, request)
        return response.body() ?: throw PubfuseException("Failed to get LiveKit token")
    }
}

class PubfuseLiveKitSDK : Room.Listener {
    override fun onConnected(room: Room) {
        Log.d("PubfuseLiveKitSDK", "✅ Connected to LiveKit room")
    }
    
    override fun onParticipantConnected(room: Room, participant: RemoteParticipant) {
        Log.d("PubfuseLiveKitSDK", "Participant connected: ${participant.identity}")
    }
    
    override fun onTrackSubscribed(
        room: Room,
        publication: RemoteTrackPublication,
        track: Track,
        participant: RemoteParticipant
    ) {
        if (track is VideoTrack) {
            // Add video track to your video view
            track.addRenderer(videoView)
        }
    }
}

// Data Models
data class StreamingProvider(
    val provider: String,
    val isConfigured: Boolean,
    val displayName: String,
    val description: String
)

data class LiveKitTokenRequest(
    val userId: String,
    val role: String
)

data class LiveKitTokenResponse(
    val room: String,
    val serverUrl: String,
    val token: String,
    val expiresAt: String
)

class PubfuseException(message: String) : Exception(message)

LiveKit API Endpoints

Method Endpoint Purpose
GET /api/streaming/providers Get available streaming providers
POST /api/streaming/sessions Create streaming session
POST /api/streaming/sessions/{id}/token Generate LiveKit access token
GET /api/streaming/ice-config Get ICE server configuration
GET /api/livekit/health LiveKit server health check

Testing LiveKit Integration

Use these test pages to verify your LiveKit setup:

📚 Additional Resources

📖 Documentation

🔗 Endpoints

  • GET /api/sessions - Available streams
  • GET /api/metrics/dashboard - Top sessions
  • WS /ws/streams/{id}/watch - Real-time connection

🆘 Support

Ready to integrate Pubfuse streaming into your mobile app?

Get started with our comprehensive SDK and start building amazing streaming experiences.

Get API Key