📱 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
- Scalability: Support for 100+ concurrent participants
- Recording: Built-in stream recording capabilities
- Adaptive Streaming: Automatic quality adjustment
- Professional Grade: Enterprise-ready infrastructure
- Fallback Support: Automatic fallback to WebRTC if needed
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:
- SDK Test - Tests LiveKit SDK loading
- Integration Test - Comprehensive testing
- Connection Test - Connection flow testing
- Provider Test - Provider detection testing
📚 Additional Resources
📖 Documentation
🔗 Endpoints
GET /api/sessions
- Available streamsGET /api/metrics/dashboard
- Top sessionsWS /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