Skip to content

Mobile Integration

Examples for React Native and native mobile platforms.

React Native

Installation

bash
npm install react-native-video axios

Stream 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
    }
}

Released under the MIT License.