Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/moq/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"label": "MoQ",
"customProps": {
"topNavSection": "moq"
}
}
5 changes: 5 additions & 0 deletions docs/moq/concepts/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"label": "Concepts",
"position": 2,
"collapsed": false
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
type: explanation
sidebar_position: 6
title: Media over QUIC (MoQ)
sidebar_position: 1
title: MoQ with Fishjam
description: Understand how Media over QUIC (MoQ) works in Fishjam — the relay model, publish/subscribe architecture, paths, and token-based access control.
---

# MoQ Streaming with Fishjam
# MoQ with Fishjam

_How Media over QUIC (MoQ) works in Fishjam_

Expand All @@ -22,7 +22,7 @@ A few properties make MoQ stand out:
- **Relay-based architecture.** The relay is a first-class part of the MoQ protocol, not an add-on. Because relaying is built into the protocol's design, scaling delivery to large audiences is a native capability.

:::info
Fishjam also supports WebRTC-based livestreaming (WHIP/WHEP). See [Livestreams](./livestreams) for that approach.
Fishjam also supports WebRTC-based livestreaming (WHIP/WHEP). See [Livestreams](../../explanation/livestreams) for that approach.
:::

## How MoQ Works in Fishjam
Expand Down Expand Up @@ -116,7 +116,7 @@ To get a subscriber token, call:
GET https://fishjam.io/api/v1/connect/{FISHJAM_ID}/room-manager/moq/{SUBSCRIBER-PATH}/subscriber
```

The Sandbox API is **not intended for production** — it has no authentication and is only available in the Sandbox environment. See [What is the Sandbox API?](./sandbox-api-concept) for more context.
The Sandbox API is **not intended for production** — it has no authentication and is only available in the Sandbox environment. See [What is the Sandbox API?](../../explanation/sandbox-api-concept) for more context.

### Fishjam Server SDK (production)

Expand All @@ -129,11 +129,11 @@ The SDK's `createMoqToken` method accepts either a `publishPath` or a `subscribe

Your backend then delivers each token to the appropriate client (publisher or viewer), which uses it to connect to the relay.

See the [MoQ Streaming tutorial](../tutorials/moq) for working code examples of both approaches.
See the [Web Publishing](../tutorials/web-publishing) and [Web Subscribing](../tutorials/web-subscribing) tutorials (or their [React Native](../tutorials/react-native-publishing) [counterparts](../tutorials/react-native-subscribing)) for working code examples.

## See also

- [MoQ Streaming tutorial](../tutorials/moq) — step-by-step guide to publishing and subscribing
- [What is the Sandbox API?](./sandbox-api-concept) — when and why to use the Sandbox API
- [Security & Token Model](./security-tokens) — broader overview of Fishjam's token system
- [Livestreams](./livestreams) — WebRTC-based livestreaming with WHIP/WHEP
- [Web Publishing](../tutorials/web-publishing) / [Web Subscribing](../tutorials/web-subscribing) — step-by-step guides to publishing and subscribing
- [What is the Sandbox API?](../../explanation/sandbox-api-concept) — when and why to use the Sandbox API
- [Security & Token Model](../../explanation/security-tokens) — broader overview of Fishjam's token system
- [Livestreams](../../explanation/livestreams) — WebRTC-based livestreaming with WHIP/WHEP
5 changes: 5 additions & 0 deletions docs/moq/tutorials/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"label": "Tutorials",
"position": 1,
"collapsed": false
}
230 changes: 230 additions & 0 deletions docs/moq/tutorials/react-native-publishing.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
---
type: tutorial
sidebar_position: 3
title: React Native Publishing
description: Publish a live video and audio broadcast over Media over QUIC (MoQ) from a React Native mobile app with Fishjam, from sandbox prototyping to production.
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

# React Native Publishing

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: the react native publishing/subscribing title is not self explanatory. i'd add the moq or stream keyword to that

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I encourage you to start it locally, there is a separate tab called moq in which these tutorials are located


This tutorial explains how to **publish** a live stream from a **React Native mobile app** using [Media over QUIC (MoQ)](https://datatracker.ietf.org/wg/moq/about/) with Fishjam. To watch a stream instead, see [React Native Subscribing](./react-native-subscribing).

It uses [`react-native-moq`](https://github.com/software-mansion-labs/react-native-moq) — React Native bindings for MoQKit, with a small, reactive hooks-based API. For the web equivalent, see [Web Publishing](./web-publishing).

:::info
If you're new to MoQ, then we recommend getting familiar with the [MoQ with Fishjam](../concepts/moq-with-fishjam) explanation.
:::

## Requirements

`react-native-moq` targets the React Native **New Architecture** (Fabric / TurboModules):

- iOS 16+
- Android API 30+

## Installation

```bash npm2yarn
npm install react-native-moq
```

Then install the iOS pods:

```sh
cd ios && pod install
```

Publishing captures the camera and microphone, so the host app is responsible for runtime permissions: request `CAMERA` / `RECORD_AUDIO` on Android, and add `NSCameraUsageDescription` / `NSMicrophoneUsageDescription` to `Info.plist` on iOS. The library does not request these for you.

:::tip
MoQ is a protocol with a well-defined negotiation, so a publisher and a subscriber don't need to use the same client library. A React Native app published with `react-native-moq` can be watched in the browser with [`@moq/watch`](./web-subscribing#connecting-and-subscribing), and vice versa.
:::

## Quickstart with the Sandbox API

If you don't have a backend server set up, you can prototype publishing using the [Sandbox API](../../explanation/sandbox-api-concept).

### Obtaining a publisher token

For more on what the Sandbox API is and its limitations, see [What is the Sandbox API?](../../explanation/sandbox-api-concept).

:::info
To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you don't have them already, see [Sandbox API URL and Fishjam ID](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id).
:::

<Tabs groupId="sandbox-token">
<TabItem value="react" label="React Native">

The `useSandbox` hook from `@fishjam-cloud/react-native-client` wraps the Sandbox API request for you:

```tsx
// @noErrors
import { useSandbox } from "@fishjam-cloud/react-native-client";

const FISHJAM_ID = "YOUR_FISHJAM_ID";
const PUBLISHER_PATH = "stream-alice";
const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL";

// Inside a React component:
const { getSandboxMoqPublisherToken } = useSandbox({
sandboxApiUrl: SANDBOX_API_URL,
});

// Request a publisher token scoped to the publisher path
const publishToken = await getSandboxMoqPublisherToken(PUBLISHER_PATH);
```

</TabItem>

<TabItem value="ts" label="TS">

If you don't want to pull in the whole client library just for the `useSandbox` hook, you can call the Sandbox API directly with `fetch`:

```ts
// @noErrors
const FISHJAM_ID = "YOUR_FISHJAM_ID";
const PUBLISHER_PATH = "stream-alice";
const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL";

const response = await fetch(
`${SANDBOX_API_URL}/moq/${PUBLISHER_PATH}/publisher`,
);
const { token: publishToken } = await response.json();
```

</TabItem>
</Tabs>

### Connecting and publishing

`react-native-moq` is hooks-based. You open a session against the relay, capture the camera and microphone, and hand them to a publisher.

Build the relay URL using the publisher token, open the session, and publish the camera + microphone tracks:

```tsx
// @noErrors
import { useEffect } from "react";
import { Button, PermissionsAndroid, Platform } from "react-native";
import {
PublisherView,
useCamera,
useMicrophone,
usePublisher,
useSession,
} from "react-native-moq";

const FISHJAM_ID = "YOUR_FISHJAM_ID";
const PUBLISHER_PATH = "stream-alice";
const publishToken = ""; // from the step above

// Build the relay URL using the publisher token
const relayUrl = `https://relay.fishjam.io/${FISHJAM_ID}?jwt=${publishToken}`;

function PublishScreen() {
// Open the MoQ session against the Fishjam relay
const session = useSession(relayUrl, (s) => s.connect());
const camera = useCamera({ position: "front" });
const microphone = useMicrophone();
const publisher = usePublisher(session);

// Request camera + mic permissions on Android (iOS handles it automatically when Info.plist is configured)
useEffect(() => {
if (Platform.OS === "android") {
PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.CAMERA,
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
]);
}
}, []);

const isPublishing = publisher.state === "publishing";

return (
<>
<PublisherView camera={camera} style={{ aspectRatio: 9 / 16 }} />
<Button title="Flip" onPress={camera.flip} />
<Button
title={isPublishing ? "Stop" : "Publish"}
disabled={session.state !== "connected" && !isPublishing}
onPress={() => {
if (isPublishing) publisher.stop();
else
publisher.publish({
path: PUBLISHER_PATH,
tracks: [camera, microphone],
});
}}
/>
</>
);
}
```

Once `publisher.state === "publishing"`, the stream is live on the MoQ relay! Viewers can start watching by following [React Native Subscribing](./react-native-subscribing).

:::info
**Why a separate `useSession` and `publish()`?** \
The session owns the connection to the relay; the publisher reuses it. Because publishing rides on top of an open session, the same connection can subscribe and publish at once — pair `usePublisher(session)` with `useBroadcasts(session, prefix)` to do both.
:::

## Production with Server SDKs

The [Quickstart](#quickstart-with-the-sandbox-api) gets you publishing quickly. In production, your backend generates tokens with proper authorization, so you control who can publish.

A **publisher token** grants write access to a specific path. Generate one on your backend and deliver it to the broadcasting client:

<Tabs groupId="language">
<TabItem value="ts" label="TypeScript">

```ts
const fishjamId = '';
const managementToken = '';

// ---cut---
import { FishjamClient } from '@fishjam-cloud/js-server-sdk';

const fishjamClient = new FishjamClient({
fishjamId,
managementToken,
});

const streamPath = 'stream-alice';

// Generate a token that allows publishing to 'stream-alice'
const { token: publishToken } = await fishjamClient.createMoqToken({
publishPath: streamPath,
});
```

</TabItem>

<TabItem value="python" label="Python">

```python
from fishjam import FishjamClient

fishjam_client = FishjamClient(
fishjam_id=fishjam_id,
management_token=management_token,
)

stream_path = 'stream-alice'

# Generate a token that allows publishing to 'stream-alice'
publish_token = fishjam_client.create_moq_token(publish_path=stream_path)
```

</TabItem>
</Tabs>

Deliver this token to the mobile client, then use it to build the relay URL and connect as described in [Connecting and publishing](#connecting-and-publishing).

## See also

- [React Native Subscribing](./react-native-subscribing) — watch a MoQ stream on mobile
- [Web Publishing](./web-publishing) — publish from the browser instead
- [MoQ with Fishjam](../concepts/moq-with-fishjam) — how MoQ works in Fishjam
- [Livestreaming](../../tutorials/livestreaming) — the WebRTC (WHIP/WHEP) approach
Loading
Loading