Skip to content

ONVIF (IP Camera)

ONVIF-compatible IP camera driver with live video streaming, snapshots, and PTZ (pan/tilt/zoom) control. Uses ONVIF SOAP for device control and OpenCV for RTSP stream capture.

Safety gate

This driver ships with requiresSafetyGates: false — PTZ moves, snapshots, and other operations bypass the safety gate entirely and no audit rows are written. See Per-driver opt-out.

Properties

PropertyTypeAccessUnitDescription
ipstringRCamera IP address
portintRONVIF service port
fpsintR/WTarget capture framerate
qualityintR/WJPEG encoding quality (1–100)
flipHboolR/WMirror frames horizontally (left ↔ right)
flipVboolR/WFlip frames vertically (top ↔ bottom)
digitalZoomdoubleR/WxSoftware zoom factor (1.0 = off, 2.0 = 2x, … up to 16)
zoomXdoubleR/WDigital-zoom horizontal pan (-1 = left, 0 = center, 1 = right)
zoomYdoubleR/WDigital-zoom vertical pan (-1 = top, 0 = center, 1 = bottom)
streamingboolRWhether RTSP capture is active
connectedboolRWhether camera is reachable
resolutionstringRStream resolution (e.g., "1920x1080")
profilesstring[]RAvailable media profile names

Actions

ActionArgsDescription
startConnect to RTSP stream and start video capture
stopStop RTSP capture and streaming
snapshotCapture a single frame, return as base64 JPEG
discoverFind ONVIF devices on the local network
getProfilesFetch available media profiles from the camera
ptzMove{ pan, tilt, zoom }PTZ relative move — nudge by a delta (values -1.0 to 1.0)
ptzMoveAbsolute{ pan, tilt, zoom }PTZ absolute move — go to a specific position (values -1.0 to 1.0)
ptzStopStop PTZ movement
ptzStatusRead current PTZ position and movement state, or null if unsupported
gotoPreset{ preset: string }Go to a PTZ preset position

PTZ control

ONVIF cameras support three styles of motion. All three coexist on the same driver:

  • ptzMove (relative) — a one-shot nudge from the current position. Cameras move once and stop on their own. Use for tweaking aim during live operation.
  • ptzMoveAbsolute (absolute) — drives to a specific position in the camera's coordinate space (typically each axis normalized to [-1, 1]). Use when you have known coordinates to revisit, or to script reproducible scans. Some lower-end ONVIF Profile S cameras don't implement absolute PTZ and will return a SOAP fault — fall back to gotoPreset or relative moves.
  • gotoPreset — navigate to a position previously saved on the camera as a numbered preset. Works on the widest set of cameras, but you can only target positions the camera (or its UI) has saved.

ptzStatus returns the current { pan, tilt, zoom, panTiltState, zoomState }. panTiltState / zoomState are "IDLE", "MOVING", or "UNKNOWN" — useful to wait for an absolute move to settle before issuing the next one. Each call is a SOAP round-trip; don't poll faster than ~500 ms.

javascript
// Drive to a known position, wait for it to settle, then take a snapshot
const cam = connector('camera');
cam.ptzMoveAbsolute({ pan: 0.4, tilt: -0.2, zoom: 0.5 });
while (true) {
  const s = cam.ptzStatus();
  if (!s || (s.panTiltState !== 'MOVING' && s.zoomState !== 'MOVING')) break;
  delay(200);
}
const jpeg = cam.snapshot();

Digital zoom

Three writable properties — digitalZoom, zoomX, zoomY — give you a software crop-and-rescale on top of any PTZ. Useful when:

  • The camera has no optical zoom (or only a coarse one), but you need to inspect a region for an AI / vision pipeline.
  • You want to look at several regions of the same frame in parallel without moving the camera (e.g. snapshot(), change crop, snapshot() again).
  • You want zoom that takes effect now — PTZ moves take seconds to settle, digital zoom is on the next frame.
PropertyRangeMeaning
digitalZoom1.016.0Crop a 1/zoom × 1/zoom window and scale back up to the original frame size. 1.0 = off
zoomX-11Horizontal pan: -1 = left edge, 0 = center, 1 = right edge
zoomY-11Vertical pan: -1 = top, 0 = center, 1 = bottom

The pan offsets are normalized against the available pan range at the current zoom — zoomX = 1 puts the crop's right edge flush with the image's right edge, so the zoomed region never extends past the source frame regardless of zoom level.

The output frame stays at the original resolution, so widgets, recordings, and downstream consumers don't see the size change. There's no actual detail gained — pure software crop. Combine with optical PTZ zoom (via ptzMove) when you need real reach.

javascript
const cam = connector('camera');

// Zoom 4x onto the top-right quadrant of the frame
cam.digitalZoom = 4;
cam.zoomX = 1;
cam.zoomY = -1;

// AI inspects the zoomed region
const desc = ai("What's the reading on this gauge?", cam.snapshot());

// Reset
cam.digitalZoom = 1;
cam.zoomX = 0;
cam.zoomY = 0;

Coordinates apply to the displayed image — i.e. after any flipH / flipV — so (zoomX, zoomY) always points to the same spot on the dashboard widget regardless of how the camera is mounted.

Streams

StreamDescription
videoBase64 JPEG frames from RTSP stream at configured FPS
snapshotBase64 JPEG emitted once per snapshot() call — exactly the frame that was returned to the caller

Debugging AI-vision with the snapshot stream

The snapshot stream pushes one frame per snapshot() call. Bind a Canvas widget to <connector>:snapshot (config field stream: "camera:snapshot", mode image) to see exactly the frame your AI/vision pipeline received — useful when an AI's description doesn't match what's currently on screen. The lag is usually in the camera's H.264 GOP / B-frame settings, not the driver: the snapshot you see in the widget is the snapshot the AI got, so any stale-looking frames are real and the AI isn't hallucinating.

Config Options

OptionTypeDefaultDescription
ipstring""Camera IP address (required)
portint80ONVIF service port
usernamestring""Authentication username
passwordstring""Authentication password
profileint0Media profile index (0 = main stream)
fpsint10Target capture framerate (1–30)
qualityint75JPEG encoding quality (1–100)
flipHboolfalseMirror frames horizontally
flipVboolfalseFlip frames vertically
digitalZoomdouble1.0Initial software zoom factor (1.0–16.0)
zoomXdouble0Initial horizontal pan of the zoom crop (-1..1)
zoomYdouble0Initial vertical pan of the zoom crop (-1..1)

Latency

The driver is configured for low-latency RTSP capture: FFmpeg runs with nobuffer | low_delay | framedrop=1 | analyzeduration=0 | probesize=32768 | max_delay=0 | reorder_queue_size=0 and OpenCV's internal buffer is requested at size 1. The capture loop drains decoder output as fast as frames arrive and only encodes one out of every (rate ÷ fps) frames — so increasing fps does not increase latency, it just emits more of the frames the decoder is already producing.

Most remaining glass-to-screen delay (typically 0.5–1.5 s) comes from the camera's H.264 encoder GOP / B-frame settings and the browser's MJPEG decode pipeline, not from this driver. To reduce it further:

  • Use the substream profile (profile: 1 if available) — usually 480p with a shorter GOP, often half the latency of the main stream.
  • In the camera's own UI, set GOP to ≤ 1× framerate, disable B-frames, and prefer CBR over VBR.
  • Keep the camera on a wired LAN segment; Wi-Fi adds 50–200 ms of jitter buffer at the camera or router.

Video Recording

The ONVIF driver supports video recording to workspace/data/recordings/. See also Webcam which shares the same recording interface.

Recording Actions:

ActionArgsReturnsDescription
recordseconds, filename?filenameRecord for N seconds (blocks until done)
recordStartfilename?filenameStart recording (manual stop)
recordStopfilenameStop recording, finalize file

Recording Property:

PropertyTypeAccessDescription
recordingboolRWhether recording is active
  • Output format: MP4 (MP4V codec), falls back to AVI (MJPEG) if codec unavailable
  • Auto-starts the camera stream if not already streaming
  • Filename is auto-generated (Onvif_{timestamp}.mp4) if not specified
javascript
// From a script
const cam = connector('camera');
const file = cam.record({ seconds: 20 });           // duration-based
cam.recordStart({ filename: "experiment-1" });       // manual start
const result = cam.recordStop();                     // manual stop

Example Connector

javascript
// workspace/connectors/camera.js
export default {
  driver: "Onvif",
  config: {
    ip: "192.168.1.100",
    port: 80,
    username: "admin",
    password: "password",
    profile: 0,
    fps: 10,
  },
  poll: ["connected", "streaming", "resolution"],

  methods: {
    lookLeft:  { fn: () => driver.ptzMove({ pan: -0.5, tilt: 0, zoom: 0 }), description: "Pan left" },
    lookRight: { fn: () => driver.ptzMove({ pan: 0.5, tilt: 0, zoom: 0 }),  description: "Pan right" },
    zoomIn:    { fn: () => driver.ptzMove({ pan: 0, tilt: 0, zoom: 0.5 }),  description: "Zoom in" },
    zoomOut:   { fn: () => driver.ptzMove({ pan: 0, tilt: 0, zoom: -0.5 }), description: "Zoom out" },
    stopMove:  { fn: () => driver.ptzStop(), description: "Stop PTZ movement" },
  },
};

Note: Requires OpenCV native libraries, which ship bundled with the Windows build. If no IP is configured, use the discover action to find cameras on your network.

Muxit — Hardware Orchestration Platform