Skip to main content

Livestreaming

This guide shows you how to implement livestreaming in React Native using Fishjam. You'll learn how to broadcast video streams and receive them in your app.

📋 Prerequisites

Before you begin, ensure you have:

  • React Native project set up with Expo or bare React Native
  • Fishjam SDK installed: npm install @fishjam-cloud/react-native-client
  • A room of livestream type created in Fishjam
  • Camera and microphone permissions configured

See the livestreaming overview for more information about Fishjam's livestreaming in general.

🏗️ Architecture Overview

Fishjam livestreaming follows a one-to-many broadcasting model:

Key characteristics:

  • One streamer per room (broadcasts video/audio)
  • Unlimited viewers per room (receive the stream)
  • Single video track and single audio track per stream

📱 Broadcasting a Stream

Prerequisites

Before you start broadcasting, you need:

  • Camera and microphone permissions configured
  • A room of livestream type
  • A peer token for that room

Setup Steps

  1. Join the room using the connection guide
  2. Start streaming following the streaming guide
note

Livestreaming rooms accept only one video track and one audio track. Fishjam ignores additional tracks to maintain broadcast quality.

📺 Receiving a Stream

Prerequisites

To receive a stream, you need:

  • An active streamer in a livestream room
  • A viewer token for that room

Setup Steps

  1. Use the useLivestream hook to connect to the stream
  2. Pass the viewer token to the connect method
  3. Display the stream using the LivestreamView component

The LivestreamView automatically renders the video once the connection establishes.

🖼️ Picture-in-Picture Support

The LivestreamView component supports Picture-in-Picture (PiP) mode. Users can continue watching the stream while using other apps.

Enable PiP Support

Add this configuration to your app.json:

{
"expo": {
"ios": {
"supportsPictureInPicture": true
}
}
}

Configure PiP Behavior

The LivestreamView component accepts these PiP-related props:

PropTypeDescription
pipEnabledbooleanEnables Picture-in-Picture mode
autoStartPipbooleanStarts PiP automatically when app backgrounds
autoStopPipbooleanStops PiP automatically when app foregrounds (iOS only)
pipSizeobjectSets PiP window size with width and height properties
note

Android only supports PiP when the app is in the background. The autoStopPip prop is always enabled on Android.

Basic PiP Usage

<LivestreamView
pipEnabled
autoStartPip
autoStopPip
pipSize={{ width: 1920, height: 1080 }}
/>

Control PiP Programmatically

You can control PiP behavior using the component ref:

import { useRef } from "react";
import {
LivestreamView,
LivestreamViewRef,
} from "@fishjam-cloud/react-native-client";

function LivestreamPlayer() {
const livestreamRef = useRef<LivestreamViewRef>(null);

// Control PiP programmatically
livestreamRef.current?.startPip();
livestreamRef.current?.stopPip();
livestreamRef.current?.togglePip();

return (
<LivestreamView
ref={livestreamRef}
style={{ width: "90%", aspectRatio: 1, backgroundColor: "black" }}
/>
);
}

Platform Differences

PlatformForeground SupportBackground SupportAuto-stop Behavior
iOS✅ Yes✅ YesControlled by autoStopPip prop
Android❌ No✅ YesAlways enabled

💻 Complete Examples

These examples show complete implementations for both streaming and viewing.

info

This guide assumes you completed either the Quick Setup or Installation guide.

Required Permissions

Configure permissions before livestreaming

Your app needs to have permissions configured in order to use the microphone and camera.

Android

Permissions below are required to stream audio and video with Fishjam on Android.

  • android.permission.CAMERA
  • android.permission.RECORD_AUDIO
  • android.permission.MODIFY_AUDIO_SETTINGS

Add required permissions to the app.json file.

app.json
{
"expo": {
...
"android": {
...
"permissions": [
"android.permission.CAMERA",
"android.permission.RECORD_AUDIO",
"android.permission.MODIFY_AUDIO_SETTINGS"
]
}
}
}

iOS

You don't have to make any changes to run app on iOS. To update default content of permission alert, you can add these settings to app.json:

app.json
{
"expo": {
...
"ios": {
...
"infoPlist": {
"NSCameraUsageDescription": "Allow $(PRODUCT_NAME) to access your camera.",
"NSMicrophoneUsageDescription": "Allow $(PRODUCT_NAME) to access your microphone."
}
},
}
}

Streamer Example

This component automatically starts streaming when mounted:

import { useEffect } from "react";
import {
useCamera,
useConnection,
VideoPreviewView,
} from "@fishjam-cloud/react-native-client";

const ROOM_MANAGER_ID = "";

export default function Streamer() {
const { prepareCamera } = useCamera();
const { joinRoom, leaveRoom } = useConnection();

useEffect(() => {
const startStreaming = async () => {
console.log("Getting room details...");
const { url, peerToken } = await getRoomDetails(
"TestRoom",
"StreamerUserName",
);

console.log("Preparing camera...");
await prepareCamera({ cameraEnabled: true });

await joinRoom({ url, peerToken });
console.log("Streaming...");
};

startStreaming();
return () => {
leaveRoom();
};
}, [prepareCamera, joinRoom, leaveRoom]);

return (
<VideoPreviewView
style={{ width: "90%", aspectRatio: 1, alignSelf: "center" }}
/>
);
}

async function getRoomDetails(roomName: string, peerName: string) {
// This will work ONLY for the Sandbox App
const response = await fetch(
`https://fishjam.io/api/v1/connect/${ROOM_MANAGER_ID}/room-manager/?roomName=${roomName}&peerName=${peerName}&roomType=livestream`,
);
return response.json();
}

Viewer Example

This component receives and displays a livestream:

import { useCallback, useEffect } from "react";
import { View } from "react-native";
import {
useLivestream,
LivestreamView,
} from "@fishjam-cloud/react-native-client";

const ROOM_MANAGER_ID = "";

export function Receiver() {
const { connect, disconnect } = useLivestream();

const handleConnect = useCallback(async () => {
try {
console.log("Getting stream details...");
const { token } = await getViewerToken("TestRoom");
await connect(`https://fishjam.io/api/v1/live`, token);
console.log("Receiving...");
} catch (err) {
console.log(err);
}
}, [connect]);

useEffect(() => {
handleConnect();

return () => {
disconnect();
};
}, [handleConnect, disconnect]);

return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<LivestreamView
style={{ width: "90%", aspectRatio: 1, backgroundColor: "black" }}
/>
</View>
);
}

async function getViewerToken(roomName: string) {
// This will work ONLY for the Sandbox App
// see https://fishjam.io/docs/livestreaming
const response = await fetch(
`https://fishjam.io/api/v1/connect/${ROOM_MANAGER_ID}/room-manager/${roomName}/broadcast-viewer-token`,
);
return response.json();
}

🚨 Troubleshooting

Common Issues

Stream not appearing for viewers:

  • Verify the streamer successfully joined the room. If no one is streaming, there is nothing to join to.
  • Ensure the room type is livestream, when streaming.

PiP not working:

  • Confirm PiP support is enabled in app.json
  • Check that pipEnabled prop is set to true

📚 Additional Resources