react-native-moq

v2026.09.24

Software Mansion's react-native-moq — Media over QUIC (MoQ) live streaming for React Native: sub-second-latency video/audio playback, camera/mic/screen publishing, and realtime data tracks over a MoQ relay. MUST USE before writing, reviewing, or debugging ANY code that uses react-native-moq or react-native-moq-ui. Trigger on: 'react-native-moq', 'MoQ', 'Media over QUIC', 'moq relay', 'moq-kit', 'useSession', 'useBroadcasts', 'useVideoPlayer', 'useAudioPlayer', 'usePublisher', 'useCamera', 'useMultiCamera', 'useMicrophone', 'useScreenBroadcast', 'useDataTrack', 'useDataMessages', 'useAudioChunks', 'useAudioSource', 'useVideoSource', 'VideoView', 'PublisherView', 'VideoPlayerView', 'BroadcastPickerView', 'createSession', 'createPublisher', 'subscribeBroadcasts'.

GitHub
安装命令
npx skhub add software-mansion-labs/react-native-moq
Markdown
SKILL.md

react-native-moq

React Native bindings for Media over QUIC (MoQ): live video, audio, and data tracks with sub-second latency, published to and consumed from a MoQ relay. Built on Software Mansion's moq-kit native library.

Requirements: React Native New Architecture (Fabric + TurboModules), iOS 16+, Android API 30+.

npm install react-native-moq
cd ios && pod install

Optional player chrome (fullscreen modal, controls, volume slider):

npm install react-native-moq-ui @react-native-vector-icons/material-icons

iOS also needs MaterialIcons.ttf under UIAppFonts in Info.plist, then pod install again. Skip this package if composing your own player UI on <VideoView>.

Mental model

  • A Session is one connection to a MoQ relay (useSession(url)). It does not auto-connect — call session.connect() yourself. One session serves both subscribing and publishing.
  • Subscribing: useBroadcasts(session, prefix) lists live broadcasts under a path prefix → pick a BroadcastInfo → useVideoPlayer / useAudioPlayer → render with <VideoView player={player}>. Raw-media alternatives: useAudioChunks (encoded or PCM to JS) and useDataMessages (string payloads).
  • Publishing: capture hooks produce tracks (useCamera, useMicrophone, useMultiCamera) or you generate them (useDataTrack, useAudioSource, useVideoSource) → usePublisher(session).publish({ path, tracks }). Screen sharing (useScreenBroadcast) is separate and out-of-process.
  • Every hook has a hook-free counterpart — create* (returns the object plus destroy()) for the stateful ones, subscribe* (returns start()/stop()) for the subscription hooks — see references/imperative.md.
  • Objects expose reactive state (session.state, publisher.state, player.isPlaying, …); hooks re-render on changes. Subscribe manually with addListener or the useEvent/useEventListener helpers.

Quick start — watch

function Watch() {
  const session = useSession('https://relay.example.com', (s) => s.connect());
  const broadcasts = useBroadcasts(session, ''); // '' = all paths
  return broadcasts.map((b) => <BroadcastPlayer key={b.path} broadcast={b} />);
}

function BroadcastPlayer({ broadcast }: { broadcast: BroadcastInfo }) {
  const player = useVideoPlayer(broadcast, (p) => p.play());
  return <VideoView player={player} style={{ width: '100%', aspectRatio: 16 / 9 }} />;
}

useVideoPlayer/useAudioPlayer require a non-null broadcast — mount them in a child rendered only once one exists.

Quick start — go live

const session = useSession(url, (s) => s.connect());
const camera = useCamera();
const microphone = useMicrophone();
const publisher = usePublisher(session);

<PublisherView camera={camera} style={styles.preview} />
// when session.state === 'connected':
publisher.publish({ path: 'live/test', tracks: [camera, microphone] });

Critical rules

  1. Errors are strings, not exceptions. States are unions like 'connected' | 'error:...'; check state.startsWith('error:'). Nothing throws on media failure. Sessions carry the message only in the state string (state.slice(6)); capture tracks and the publisher additionally expose a lastError field. Players have no error surface at all — watch session and publisher state instead.
  2. publish() requires session.state === 'connected', otherwise the publisher goes straight to error:session is not connected. Gate the go-live button on session state.
  3. Gate codecs with getSupportedVideoCodecs() / getSupportedAudioCodecs(). On Android an unsupported encoder (often h265) makes publishing start then silently stop with no error. Default to h264/opus.
  4. Permissions are the host app's job — request CAMERA/RECORD_AUDIO on Android, add NSCameraUsageDescription/NSMicrophoneUsageDescription on iOS. A black preview usually means a missing permission.
  5. <VideoView> takes no children (the Android native view is not a ViewGroup). Render overlays as absolutely-positioned siblings.
  6. Capture hooks are refcounted device singletons — all consumers share the physical camera/mic; flip() affects everyone. Use enabled: false to keep hardware off while mounted instead of calling hooks conditionally.
  7. Broadcast paths are relative to the subscription prefix (broadcast live/test appears as path: 'test' under prefix 'live'). Subscribe with '' to see full paths. A listed broadcast that won't play (relay error code=13 NotFound) means a path/prefix mismatch.
  8. Data tracks are not in the catalog — publisher and subscriber must agree on the track name out of band (default 'data').
  9. An idle iOS microphone capture holds the audio session in playAndRecord and can block other audio libraries (insufficientPriority). Disable the mic (enabled: false) when not publishing.

References

TaskRead
Sessions, broadcast discovery, video/audio playback, <VideoView>, stats, track switching, events, react-native-moq-uireferences/playback.md
Camera, multi-camera, microphone, publisher lifecycle, screen broadcasting, codec queriesreferences/publishing.md
Data tracks & messages, audio chunks to JS, push-your-own audio (PCM) and video (frame buffers)references/custom-tracks.md
Using without React: create* handles, subscribe* functions, cleanupreferences/imperative.md
发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

Sep 24, 2026

分类

未分类

许可证

未指定

源路径

skills/react-native-moq

默认分支

main

最新提交

e3f00cd

Tree SHA

de91b28