Native iOS, Android, Flutter & React Native SDKs
Select the SDK that matches your development stack
Add WAVE SDK to your mobile project
// Package.swift
dependencies: [
.package(url: "https://github.com/wave-streaming/wave-ios", from: "2.0.0")
]
// Or in Xcode: File → Add Package Dependencies
// URL: https://github.com/wave-streaming/wave-ios# Podfile pod 'WaveStreamingSDK', '~> 2.0' # Then run: pod install
<key>NSCameraUsageDescription</key>
<string>Camera access is required for live streaming</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is required for live streaming</string>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>import SwiftUI
import WaveStreamingSDK
struct StreamView: View {
@StateObject private var wave = WaveClient(apiKey: "wave_live_xxxxx")
@StateObject private var streamer: WaveStreamer
@State private var isStreaming = false
@State private var streamInfo: StreamInfo?
init() {
_streamer = StateObject(wrappedValue: WaveStreamer(
camera: .front,
resolution: .hd1080,
fps: 30,
orientation: .portrait,
audio: AudioConfig(
enabled: true,
echoCancellation: true,
noiseSuppression: true,
sampleRate: 48000
),
video: VideoConfig(
stabilization: .cinematic,
hdr: true,
codec: .h265,
bitrate: .adaptive(min: 1_000_000, max: 6_000_000)
)
))
}
var body: some View {
ZStack {
// Camera preview
WaveCameraPreview(streamer: streamer)
.ignoresSafeArea()
// Controls overlay
VStack {
Spacer()
HStack(spacing: 20) {
// Switch camera
Button(action: { streamer.switchCamera() }) {
Image(systemName: "camera.rotate")
.font(.title2)
.foregroundColor(.white)
.padding()
.background(.ultraThinMaterial)
.clipShape(Circle())
}
// Start/Stop streaming
Button(action: { toggleStream() }) {
Image(systemName: isStreaming ? "stop.fill" : "record.circle")
.font(.largeTitle)
.foregroundColor(isStreaming ? .red : .white)
.padding()
.background(.ultraThinMaterial)
.clipShape(Circle())
}
// Beauty filter toggle
Button(action: { streamer.beautyFilter.toggle() }) {
Image(systemName: "sparkles")
.font(.title2)
.foregroundColor(streamer.beautyFilter.isEnabled ? .yellow : .white)
.padding()
.background(.ultraThinMaterial)
.clipShape(Circle())
}
}
.padding(.bottom, 40)
}
// Stream stats overlay
if isStreaming, let info = streamInfo {
VStack {
HStack {
Circle()
.fill(.red)
.frame(width: 8, height: 8)
Text("LIVE")
.font(.caption.bold())
Text("\(info.viewerCount) viewers")
.font(.caption)
}
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(.ultraThinMaterial)
.cornerRadius(20)
Spacer()
}
.padding(.top, 60)
}
}
.onAppear {
Task {
await streamer.prepare()
}
}
}
private func toggleStream() {
Task {
if isStreaming {
await streamer.stop()
isStreaming = false
streamInfo = nil
} else {
let stream = try await wave.streams.create(
title: "Mobile Stream",
protocol: .webrtc,
visibility: .public
)
try await streamer.start(streamId: stream.id)
isStreaming = true
// Listen for viewer updates
for await info in wave.streams.observe(stream.id) {
streamInfo = info
}
}
}
}
}Platform-specific camera features and recommendations
| Feature | iOS | Android | Recommended | Impact |
|---|---|---|---|---|
| Resolution | Up to 4K@60fps | Up to 4K@60fps (device dependent) | 1080p@30fps for streaming | Higher = more bandwidth + battery |
| HDR | Dolby Vision, HDR10 | HDR10, HDR10+ (flagship) | Enable on supported devices | Better color range, +20% bandwidth |
| Stabilization | Cinematic, Standard, Off | OIS, EIS, Hybrid | Cinematic/Hybrid for walking | Professional look, slight delay |
| Low Light | Night Mode, Deep Fusion | Night Sight (Pixel), varies | Enable in low-light scenarios | Better visibility, increased noise |
| Focus | Phase detection, LiDAR | PDAF, Laser AF, Dual Pixel | Face tracking for streamers | Keeps subject sharp |
Handle poor mobile networks gracefully
// Configure network adaptation
const networkConfig = {
// Bitrate limits
minBitrate: 300_000, // 300 Kbps - never go below
maxBitrate: 8_000_000, // 8 Mbps - cap for mobile
startBitrate: 1_500_000, // 1.5 Mbps - initial
// Network-type presets
presets: {
wifi: { resolution: '1080p', fps: 30 },
cellular_5g: { resolution: '1080p', fps: 30 },
cellular_4g: { resolution: '720p', fps: 30 },
cellular_3g: { resolution: '480p', fps: 24 },
},
// Adaptation behavior
adaptationSpeed: 'aggressive', // 'smooth' | 'aggressive' | 'hybrid'
connectionMonitoring: true,
// Callbacks
onNetworkChange: (quality) => {
console.log(`Adapted to: ${quality.resolution}@${quality.bitrate}bps`);
// Update UI indicator
},
onConnectionLost: () => {
// Show reconnecting indicator
},
};
streamer.setNetworkConfig(networkConfig);Stream longer without draining the battery
| Technique | Savings | Description | Implementation | Trade-off |
|---|---|---|---|---|
| Hardware Encoding | 40-60% | Use device GPU instead of CPU for encoding | Enabled by default in WAVE SDK | None - always recommended |
| Adaptive Resolution | 20-30% | Lower resolution when battery is low | batteryAwareMode: true | Quality reduction below 20% battery |
| Frame Rate Throttling | 15-25% | Reduce FPS from 30 to 24 when needed | minFrameRate: 24 | Slightly less smooth video |
| Thermal Management | 10-20% | Reduce quality when device is hot | thermalThrottling: true | Prevents overheating shutdowns |
| Background Audio Only | 50-70% | Stop video when app backgrounded | backgroundMode: "audio" | No video in background |
// Enable battery-aware mode
const streamer = new WaveStreamer({
// ... camera config
battery: {
// Enable all battery optimizations
batteryAwareMode: true,
// Quality reduction thresholds
lowBatteryThreshold: 20, // Start reducing at 20%
criticalThreshold: 10, // Audio-only below 10%
// Thermal management
thermalThrottling: true,
maxTemperature: 45, // Celsius - reduce quality above this
// Frame rate adaptation
minFrameRate: 24, // Never go below 24fps
// Background behavior
backgroundMode: 'audio', // 'audio' | 'video' | 'pause'
},
// Callbacks
onBatteryWarning: (level) => {
alert(`Battery at ${level}%. Quality will be reduced.`);
},
onThermalWarning: (temp) => {
console.log(`Device at ${temp}°C. Cooling down...`);
},
});Enhance streams with real-time face tracking and effects
// Beauty filter configuration
streamer.beautyFilter.configure({
enabled: true,
level: 0.5, // 0-1 intensity
// Individual controls
skinSmoothing: 0.6,
faceSlimming: 0.3,
eyeEnlargement: 0.2,
teethWhitening: 0.4,
});
// Face masks and effects
await streamer.effects.add({
type: 'faceMask',
asset: 'cat_ears',
tracking: 'face', // 'face' | 'head' | 'body'
});
// Virtual backgrounds
await streamer.effects.add({
type: 'background',
mode: 'blur', // 'blur' | 'replace' | 'bokeh'
intensity: 0.8,
// Or replace with image:
// mode: 'replace',
// image: require('./background.jpg'),
});
// Lighting effects
await streamer.effects.add({
type: 'lighting',
preset: 'ring_light', // 'ring_light' | 'studio' | 'warm' | 'cool'
intensity: 0.5,
});
// Performance tip: Limit to 3 simultaneous effects
console.log(`Active effects: ${streamer.effects.count}/3`);How leading companies use WAVE mobile SDKs
"WAVE's mobile SDK let us ship our streaming feature in 3 weeks instead of 3 months. The AR effects library alone saved us a year of development."
"Our fitness instructors stream from iPhones and Android devices seamlessly. The battery optimization means they can teach a full 90-minute class without plugging in."
"We process $2M in sales per live stream. The sub-second latency means viewers can ask questions about products and get answers in real-time."
Common issues and how to resolve them