Skip to content

feat(react-native): createCameraEffectMiddleware and release-safe model loading - #4

Merged
MiloszFilimowski merged 3 commits into
mainfrom
feat/camera-effect-middleware
Sep 11, 2026
Merged

MiloszFilimowski merged 3 commits into
mainfrom
feat/camera-effect-middleware

Conversation

@MiloszFilimowski

@MiloszFilimowski MiloszFilimowski commented Sep 11, 2026 •

Copy link
Copy Markdown
Collaborator

Camera effect as a plain middleware, plus model loading that works in release builds

Three commits:

  1. createCameraEffectMiddleware(effect, options?) in ./fishjam-react-native. It returns a Fishjam TrackMiddleware, so an app switches an effect on with one call, no provider or hook:
const blur = createCameraEffectMiddleware(createBackgroundBlurEffect(() => ({ segmentation })));
const { currentCameraMiddleware, setCameraTrackMiddleware } = useCamera();
setCameraTrackMiddleware(isBlurOn ? null : blur);

It builds the effect session and the camera frame processor on the raw track and tears both down in onClear. onStatus reports loading and failures. Also exports getCameraWebGpuDevice(), the shared device it uses.

  1. The parsed segmentation model is shared between sessions. It is fetched and parsed once per model URL, so switching the effect on again or restarting the camera skips the download and the parse. A failed load is not cached.

  2. typeGpuPersonSegmentation({ loadModel }) supplies the model bytes instead of fetching modelUrl. An Android release build exposes a bundled asset as a file:///android_res/... URL that React Native's fetch cannot read, so the effect silently fell back to passthrough there. The parsed model is cached per loader as well.

README and CHANGELOG are updated. useFishjamCameraEffect is unchanged.

Checked

yarn test (tsc, eslint, prettier) and yarn build. On devices through the fishjam-chat example (fishjam-cloud/web-client-sdk#606): Galaxy S23 debug and release builds, iPhone, blur on and off on the preview and in the room, and two phones in one room seeing each other blurred.

Releasing this as 0.1.3 unblocks the example PR.

https://claude.ai/code/session_01MUzB8Nef5pJqJMBoPHgJNJ

A camera-track middleware for useCamera().setCameraTrackMiddleware. It
acquires the GPU device, builds the effect session and the frame kernel,
and disposes them when cleared, so an app can switch an effect on from any
screen with no provider and no hook.
The plan is fetched and parsed once per model URL and reused by every
later session, so switching an effect on again or restarting the camera
skips the download and the parse. A failed load is not cached.
…d by URL

An asset embedded in an Android release build is exposed as a
file:///android_res URL that fetch cannot read. The option supplies the
bytes instead, and the parsed model is cached per loader like it is per
URL.
@MiloszFilimowski
MiloszFilimowski force-pushed the feat/camera-effect-middleware branch from a489aed to 836bfff Compare September 11, 2026 11:57
@MiloszFilimowski MiloszFilimowski changed the title feat(react-native): createCameraEffectMiddleware feat(react-native): createCameraEffectMiddleware and release-safe model loading Sep 11, 2026
@MiloszFilimowski
MiloszFilimowski merged commit 17adc23 into main Sep 11, 2026
1 check passed
@MiloszFilimowski
MiloszFilimowski deleted the feat/camera-effect-middleware branch September 11, 2026 12:33
MiloszFilimowski added a commit to fishjam-cloud/web-client-sdk that referenced this pull request Sep 14, 2026
## Description

Background blur in the fishjam-chat example, plus the react-client fixes
it needs:

1. **Re-apply the camera middleware to new device tracks**
(react-client). A middleware set before the camera started was never
applied, and a camera switch published the raw track for a moment. Now
every new device track goes through the middleware before it is
published, and a middleware that finishes setting up after it was
replaced releases itself.
2. **Release the previous middleware only after the published track is
swapped** (react-client). Switching blur off used to release the blurred
track first and swap it out of the published stream second. On React
Native, releasing disposes the track natively, so the swap could no
longer remove it: the dead track stayed in the stream and the local tile
froze on every toggle (Android). `applyMiddleware` now builds the new
track, publishes it through a callback from the caller, and releases the
old one last. The latest request wins: one that is replaced while it
sets up releases itself.
3. **Background blur in fishjam-chat** (example). A blur button on the
preview and room screens hands a middleware from
`@fishjam-cloud/video-effects` to
`useCamera().setCameraTrackMiddleware`. No provider, no custom source:

```ts
export const backgroundBlur = createCameraEffectMiddleware(
  createBackgroundBlurEffect(() => ({ segmentation, radius: 24 })),
);

const { currentCameraMiddleware, setCameraTrackMiddleware } = useCamera();
const isBlurOn = currentCameraMiddleware === backgroundBlur;
setCameraTrackMiddleware(isBlurOn ? null : backgroundBlur);
```

The model is read through `expo-asset` and `expo-file-system`, because
an Android release build cannot `fetch` a bundled asset. The example
moves to Expo SDK 57 / React Native 0.86.3, which react-native-worklets
0.12 requires, and sets `iphoneDeploymentTarget: "16.4"` in its Fishjam
plugin options because Expo SDK 57 pods need it. The plugin default
stays 15.1.

## Motivation and Context

Camera effects on Fishjam's own camera track, shown in the example with
the smallest possible app code. The react-client fixes came out of
testing it on devices.

Uses `@fishjam-cloud/video-effects` 0.1.3
(fishjam-cloud/video-effects#4), which adds
`createCameraEffectMiddleware`.

Related fork fix, released in `@fishjam-cloud/react-native-webrtc`
0.30.4 and not required by this PR:
fishjam-cloud/fishjam-react-native-webrtc#91 (a released track leaves
every local stream).

## Checked

- react-client unit tests: 77 pass; `cameraMiddleware.spec.ts` covers
cold start, camera off and on, replacement and clearing while pending,
onClear running once, and the previous middleware being released after
`replaceTrack`.
- Galaxy S23, debug and release builds: blur on and off on the preview
and in the room, published tile blurred, room tile stays live across
toggles.
- iPhone: same on the preview and in the room.
- Two phones in one room: each side's blur shows on the other phone.

## Documentation impact

- [ ] Documentation update required
- [ ] Documentation updated [in another PR](_)
- [x] No documentation update required

## Types of changes

- [x] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to
      not work as expected)

https://claude.ai/code/session_01MUzB8Nef5pJqJMBoPHgJNJ
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant