Mobile Integration
Examples for React Native and native mobile platforms.
React Native
Installation
bash
npm install react-native-video axiosStream Player Component
jsx
import React, { useState, useEffect, useRef } from 'react';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import Video from 'react-native-video';
const LiveStreamPlayer = ({ streamKey }) => {
const videoRef = useRef(null);
const [streamData, setStreamData] = useState(null);
const [currentSourceIndex, setCurrentSourceIndex] = useState(0);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchStreamInfo();
}, [streamKey]);
const fetchStreamInfo = async () => {
try {
const response = await fetch(
`https://api.lunarstream.kozow.com/api/v1/livestreams/stream-info/${streamKey}`
);
const { data } = await response.json();
setStreamData(data);
setLoading(false);
} catch (err) {
setError('Failed to load stream');
setLoading(false);
}
};
const handleError = (err) => {
console.error('Video error:', err);
if (streamData?.hlsSources && currentSourceIndex < streamData.hlsSources.length - 1) {
setCurrentSourceIndex(prev => prev + 1);
} else {
setError('Unable to play stream');
}
};
if (loading) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" color="#6366f1" />
</View>
);
}
if (error) {
return (
<View style={styles.center}>
<Text style={styles.errorText}>{error}</Text>
</View>
);
}
if (streamData?.status !== 'STARTED') {
return (
<View style={styles.center}>
<Text style={styles.statusText}>Stream is {streamData?.status}</Text>
</View>
);
}
const currentSource = streamData.hlsSources[currentSourceIndex];
return (
<View style={styles.container}>
<Video
ref={videoRef}
source={{ uri: currentSource.url }}
style={styles.video}
resizeMode="contain"
onError={handleError}
onLoad={() => setLoading(false)}
onBuffer={({ isBuffering }) => setLoading(isBuffering)}
controls
/>
{loading && (
<View style={styles.bufferingOverlay}>
<ActivityIndicator size="large" color="#fff" />
</View>
)}
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#000' },
video: { flex: 1 },
center: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#000' },
errorText: { color: '#ef4444', fontSize: 16 },
statusText: { color: '#fff', fontSize: 16 },
bufferingOverlay: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,0.5)'
}
});
export default LiveStreamPlayer;Usage
jsx
import LiveStreamPlayer from './LiveStreamPlayer';
const App = () => {
return <LiveStreamPlayer streamKey="ee04affe2a669854052102fe762bd715" />;
};iOS (Swift)
swift
import AVKit
import AVFoundation
class StreamPlayerViewController: UIViewController {
private var player: AVPlayer?
private var playerViewController: AVPlayerViewController?
func playStream(hlsUrl: String) {
guard let url = URL(string: hlsUrl) else { return }
player = AVPlayer(url: url)
playerViewController = AVPlayerViewController()
playerViewController?.player = player
if let playerVC = playerViewController {
present(playerVC, animated: true) {
self.player?.play()
}
}
}
}Android (Kotlin + ExoPlayer)
kotlin
// build.gradle
implementation 'com.google.android.exoplayer:exoplayer:2.19.1'
// StreamPlayer.kt
class StreamPlayer(private val context: Context) {
private var player: ExoPlayer? = null
fun play(hlsUrl: String, playerView: PlayerView) {
player = ExoPlayer.Builder(context).build().apply {
playerView.player = this
setMediaItem(MediaItem.fromUri(hlsUrl))
prepare()
play()
}
}
fun release() {
player?.release()
player = null
}
}