Skip to main content
Version: Next

Stream middleware

Stream middleware in Fishjam allows you to intercept and manipulate media tracks before they are sent to the Fishjam server. This feature is powerful for applying effects, custom encodings, or any other transformations to the media stream.

The camera and microphone keep being managed by Fishjam: device selection, starting and stopping, and publishing work as usual. The middleware only decides which track goes out in place of the raw device track. Other peers receive the result as your regular cameraTrack or microphoneTrack.

Overviewโ€‹

Define a TrackMiddleware function (Web ยท React Native) which takes a MediaStreamTrack and returns, directly or as a promise, an object containing the modified MediaStreamTrack and an optional onClear function, which is called when the middleware is removed or replaced, or when the device track it processes goes away.

Type definitionโ€‹

type MiddlewareResult = { track: MediaStreamTrack; onClear?: () => void; }; export type TrackMiddleware = | ((track: MediaStreamTrack) => MiddlewareResult | Promise<MiddlewareResult>) | null;

Setting middlewareโ€‹

You can set the middleware for your media tracks using the setCameraTrackMiddleware and setMicrophoneTrackMiddleware methods of useCamera and useMicrophone. These methods accept a TrackMiddleware or null for removing previously set middleware. currentCameraMiddleware and currentMicrophoneMiddleware return the middleware that is currently set.

How middleware is appliedโ€‹

  • It is applied to every new device track. A middleware set before the device starts is applied when it starts. It is applied again when the device restarts or you switch to another device, so the published track is never the raw one while a middleware is set.
  • The old track is published until the new one is ready. An asynchronous middleware can take a moment to set up. Until it resolves, peers keep receiving the previous track. The previous middleware's onClear runs only after the new track has replaced it in the published stream.
  • The latest request wins. If you set another middleware, or null, while one is still setting up, the one that was replaced is released as soon as it finishes setting up and is never published.
  • Tracks returned by the middleware are stopped for you. When a middleware is removed, the track it returned is stopped, unless it is the input track itself. Release anything else your middleware created in onClear.

Example: Applying a video effectโ€‹

In React Native, a camera middleware typically runs a GPU effect on every frame of the camera track. @fishjam-cloud/video-effects creates such middleware for you. The example below toggles a background blur; see Background effects for the setup.

import React from "react"; import { Button } from "react-native"; import { useCamera } from "@fishjam-cloud/react-native-client"; import { createBackgroundBlurEffect } from "@fishjam-cloud/video-effects/background-blur"; import { createCameraEffectMiddleware } from "@fishjam-cloud/video-effects/fishjam-react-native"; // Create the middleware once, so its identity stays stable. const backgroundBlur = createCameraEffectMiddleware( createBackgroundBlurEffect(() => ({ segmentation, radius: 24 })), ); export function BlurToggle() { const { currentCameraMiddleware, setCameraTrackMiddleware } = useCamera(); const isBlurOn = currentCameraMiddleware === backgroundBlur; return ( <Button title={isBlurOn ? "Disable Blur" : "Enable Blur"} onPress={() => setCameraTrackMiddleware(isBlurOn ? null : backgroundBlur)} /> ); }

To write your own middleware, see Render WebGPU effects into the camera, which returns a WebGPU-rendered track, and Process camera frames in a worklet, which reads frames and returns the camera track unchanged.