From b24dc8a847fe102cac5a5ca3ec49ea0e94bdc734 Mon Sep 17 00:00:00 2001 From: Karol Konkol Date: Thu, 18 Jun 2026 16:54:44 +0200 Subject: [PATCH 1/9] Add react native tutorials --- .claude/settings.json | 13 + docs/moq/_category_.json | 6 + docs/moq/concepts/_category_.json | 5 + .../concepts/moq-with-fishjam.mdx} | 20 +- docs/moq/tutorials/_category_.json | 5 + .../moq/tutorials/react-native-publishing.mdx | 201 +++++++++++ .../tutorials/react-native-subscribing.mdx | 315 ++++++++++++++++++ docs/moq/tutorials/web-publishing.mdx | 156 +++++++++ .../tutorials/web-subscribing.mdx} | 138 ++------ docusaurus.config.ts | 25 +- packages/python-server-sdk | 2 +- redirects/index.ts | 34 ++ sidebars/docs.ts | 9 + 13 files changed, 796 insertions(+), 133 deletions(-) create mode 100644 .claude/settings.json create mode 100644 docs/moq/_category_.json create mode 100644 docs/moq/concepts/_category_.json rename docs/{explanation/moq-streaming.mdx => moq/concepts/moq-with-fishjam.mdx} (88%) create mode 100644 docs/moq/tutorials/_category_.json create mode 100644 docs/moq/tutorials/react-native-publishing.mdx create mode 100644 docs/moq/tutorials/react-native-subscribing.mdx create mode 100644 docs/moq/tutorials/web-publishing.mdx rename docs/{tutorials/moq.mdx => moq/tutorials/web-subscribing.mdx} (63%) diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..cadf6912 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,13 @@ +{ + "permissions": { + "allow": [ + "Bash(npm --version)", + "Bash(npm install *)", + "Bash(npm run *)", + "WebFetch(domain:moq.dev)" + ], + "additionalDirectories": [ + "/Users/konkol/Documents/tmp" + ] + } +} diff --git a/docs/moq/_category_.json b/docs/moq/_category_.json new file mode 100644 index 00000000..3abe1549 --- /dev/null +++ b/docs/moq/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "MoQ", + "customProps": { + "topNavSection": "moq" + } +} diff --git a/docs/moq/concepts/_category_.json b/docs/moq/concepts/_category_.json new file mode 100644 index 00000000..74b831d1 --- /dev/null +++ b/docs/moq/concepts/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Concepts", + "position": 2, + "collapsed": false +} diff --git a/docs/explanation/moq-streaming.mdx b/docs/moq/concepts/moq-with-fishjam.mdx similarity index 88% rename from docs/explanation/moq-streaming.mdx rename to docs/moq/concepts/moq-with-fishjam.mdx index 18133b80..c70b049b 100644 --- a/docs/explanation/moq-streaming.mdx +++ b/docs/moq/concepts/moq-with-fishjam.mdx @@ -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_ @@ -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 @@ -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) @@ -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 diff --git a/docs/moq/tutorials/_category_.json b/docs/moq/tutorials/_category_.json new file mode 100644 index 00000000..e0472874 --- /dev/null +++ b/docs/moq/tutorials/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Tutorials", + "position": 1, + "collapsed": false +} diff --git a/docs/moq/tutorials/react-native-publishing.mdx b/docs/moq/tutorials/react-native-publishing.mdx new file mode 100644 index 00000000..e656375e --- /dev/null +++ b/docs/moq/tutorials/react-native-publishing.mdx @@ -0,0 +1,201 @@ +--- +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 + +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/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. + +## Quickstart with the Sandbox API + +If you don't have a backend server set up, you can prototype publishing using the Sandbox API. + +:::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. +::: + +### Obtaining a publisher token + +Fetch a sandbox publisher token from the Room Manager: + +```ts +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(); +``` + +### 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 uses Info.plist) + useEffect(() => { + if (Platform.OS === "android") { + PermissionsAndroid.requestMultiple([ + PermissionsAndroid.PERMISSIONS.CAMERA, + PermissionsAndroid.PERMISSIONS.RECORD_AUDIO, + ]); + } + }, []); + + const isPublishing = publisher.state === "publishing"; + + return ( + <> + {/* On-screen preview of the camera being captured */} + +