Skip to content

Watch a camera live on mobile

The NovaVMS mobile app streams live video in the same end-to-end pipeline as the web app, but with a codec-split transport optimized for phones and tablets:

  • H.264 cameras stream over WebRTC via WHEP — a single HTTP POST signals the offer/answer pair, and react-native-webrtc decodes natively on iOS and Android.
  • H.265 cameras stream over LL-HLS single-rendition CMAF played by AVPlayer (iOS) / ExoPlayer (Android) through expo-video. Single-rendition avoids Android’s rendition-climb cold-start delay.

You do not have to pick. The mobile app reads the camera’s codec from the cloud and mounts the right player automatically.

Open a camera

  1. Sign in. The app lands on the Live tab (the grid).
  2. The grid shows every camera in your default layout or “All cameras” if no layout is active. Tap the Site chip at the top to filter to one site; tap a layout pill (“All cameras”, “Front of house”, etc.) to switch grids.
  3. Tap any tile to open the full-screen single-camera view.

The single-camera view opens with controls visible. Tap anywhere on the video to reveal them again. Controls auto-fade after 3 seconds of inactivity to maximise the picture; double-tap raises them back. (If you have “Reduce Motion” turned on in OS accessibility, controls snap rather than fade.)

What you can do from the single-camera view

ControlWhereWhat it does
CloseTop-leftReturns to the Live grid
Camera name + LIVE/REC pulse + clockTop-centreWall clock advances every second; “REC” pulse appears briefly when you’ve just triggered a clip
Settings (⋮)Top-rightOpens Camera settings (operator+ can edit)
PlaybackQuick-action rowOpens Playback on mobile for this camera
RecordQuick-action rowTriggers a fixed-duration clip (see below). Rate-limited to once per 5 seconds per camera. Server enforces the same limit
SnapshotQuick-action rowSaves a still frame to the cloud (audit-logged) and toasts “Snapshot saved”
ListenQuick-action row, when the camera has audio enabledToggles whether the camera’s mic plays through your phone
ShareQuick-action rowOpens the native share sheet with a novavms://camera/<id> deep link — recipients with the app installed land here directly; others see a benign URL
PTZ padBottom area, when the camera has PTZSix directional buttons — pan up / down / left / right + zoom in / out (hold to move; release to stop). No presets in v1 — see PTZ feature notes
Hold to TalkBottom area, when the camera has backchannelPush-to-talk to the camera’s speaker. See Use talkdown

What PTZ does and does not do

The PTZ pad is rendered only when the camera reports has_ptz = true. It sends ContinuousMove while you hold a direction button and Stop when you release. There are no presets, no GotoPreset, and no patrol tours in v1.

Pan / tilt / zoom magnitude is fixed at 0.5 (medium speed). The viewer role can watch live but cannot move the camera — the cloud rejects PTZ commands with HTTP 403.

PTZ is exposed on mobile only in v1; the web UI has no PTZ widget yet.

Listen (audio playback)

The Listen button appears when the camera’s hardware reports audio support AND your admin has not disabled audio for that camera. If has_audio is unknown, the button still renders — only an explicit has_audio = false or audio_enabled = false hides it.

Tap Listen to unmute. Tap again (or tap “Listening” when active) to mute. The camera’s audio stops when you close the live view or when the app is backgrounded.

Manual record trigger

Tap Record to trigger a single fixed-duration clip from this camera. The clip duration comes from the camera’s recording_duration_sec setting (default 15 seconds, configurable 5–120 seconds in Settings → Recording → Clip duration).

The Record button is rate-limited:

  • Mobile-side: one tap per 5 seconds per camera. Subsequent taps inside the window show “Rate limit: wait Xs”.
  • Server-side: the same 5-second window; a violator gets HTTP 429.

The button shows “Recording” for the 5-second rate-limit window, then returns to “Record”. This is a transient indicator, not a stop-toggle — the clip is a single fixed-duration recording, not an open-ended session.

The Share button emits a novavms://camera/<id> deep link via the system share sheet. Recipients with the NovaVMS mobile app installed land directly on this camera’s live view; recipients without the app see a benign URL.

To share an event’s clip (with the actual video file attached), use the Share button inside Event detail — that path uses the local-file share sheet so recipients get the playable MP4. See Share a clip from mobile.

Performance: what the app does before you tap a camera

The app does several pre-warm steps so the first camera you open starts as fast as possible:

  • At boot, the app creates a small prewarm pool of 6 RTCPeerConnections so H.264 cameras can offer/answer without paying ~50 ms of construction cost on the first tap.
  • At sign-in (and again every time the app returns to the foreground), the app fires off a “pre-warm LL-HLS writers” task in the background. The cloud starts producing CMAF segments for your H.265 cameras before you open any of them; this cuts up to 22–26 seconds off the first cold-start tap.
  • The app caches your TURN credentials from the sign-in response so it doesn’t have to fetch them mid-offer.

Decoder lease (why the grid sometimes shows thumbnails)

Mobile GPUs and SoCs have a hard limit on the number of simultaneous hardware video decoders they can hold open. NovaVMS enforces a FIFO decoder lease to avoid the “MediaCodec just kicked out the older decoder and the tile starts cycling” failure mode:

PlatformConcurrent decoders
Android emulator (AVD goldfish)4
Android real device8
iOS8

Tiles beyond the cap render a thumbnail (last frame) instead of mounting a live transport. Tap one of those thumbnail tiles to expand it into the full-screen single-camera view — the lease shifts to the foreground player.

When you switch apps

Live view stops when you background the NovaVMS app. The app is built without the FOREGROUND_SERVICE_MEDIA_PLAYBACK permission, so it cannot continue playing audio while another app is in front. (The same trade-off keeps talkdown push-to-talk only; you cannot leave the mic open in the background.)

When you return, the grid resumes, the H.265 pre-warm fires again, and tiles re-mount their transports from a warm state.

If the tile is black or stuck on “Reconnecting…”

  • H.264 camera, black or stuck reconnecting: WHEP could not negotiate, or the TURN relay is unreachable. Try toggling Wi-Fi off and back on, or kill and reopen the app to force a fresh WHEP offer.
  • H.265 camera, stuck on “Reconnecting…” 22+ seconds on cold start: the LL-HLS writer is cold. Background the app and bring it back to the foreground — the pre-warm step fires on every foreground transition.
  • Tile shows a static thumbnail and never goes live: the device decoder lease is full. Expand the tile (tap it) — the lease shifts to the foreground player.

See Troubleshoot mobile for more recovery steps.