Skip to content

onvif: send explicit Stop after ContinuousMove; honor command.speed - #2124

Open
forevaclevah2 wants to merge 1 commit into
koush:mainfrom
forevaclevah2:onvif-ptz-continuous-stop
Open

forevaclevah2 wants to merge 1 commit into
koush:mainfrom
forevaclevah2:onvif-ptz-continuous-stop

Conversation

@forevaclevah2

Copy link
Copy Markdown

ONVIF PTZ: Continuous movement never sends Stop, and command.speed is discarded

Two issues in plugins/onvif/src/onvif-ptz.ts, OnvifPtzMixin.ptzCommand.

1. command.speed is computed then dropped

In the Continuous branch, x, y and zoom are scaled by command.speed:

let x = command.pan;
let y = command.tilt;
let zoom = command.zoom;
if (command.speed?.pan)  x    *= command.speed.pan;
if (command.speed?.tilt) y    *= command.speed.tilt;
if (command.speed?.zoom) zoom *= command.speed.zoom;

…and then continuousMove() is called with the unscaled command.pan / command.tilt /
command.zoom. The scaled values are dead stores, so command.speed has no effect in
Continuous mode.

2. Continuous relies solely on the ONVIF <Timeout> element

The branch passes timeout: command.timeout || 1000 and never issues Stop, delegating
termination entirely to the camera. Timeout is optional in the ONVIF PTZ spec and is not
universally honored.

On an ONVIF_ICAMERA / JM800S5_AF (hw MC800S5, fw V3.4.0.3), the camera ignores both the
explicit <Timeout>PT1S</Timeout> and its own advertised
<DefaultPTZTimeout>PT00H01M00S</DefaultPTZTimeout>. Measured against the live camera: a
single ContinuousMove produced 84+ seconds of uninterrupted motion, sampled every 9s,
halted only by an explicit Stop. In practice one arrow tap in the UI pans the camera
indefinitely.

This camera has no working alternative mode, so Continuous is not merely a preference here:

  • RelativeMove and AbsoluteMove both crash the camera's HTTP/ONVIF server outright
    (empty reply, connections refused ~5s, self-restart), so ptzMovementType: 'Default'
    (which falls through to relativeMove) is unusable.
  • GetStatus is a stub returning x=-1 y=-1 zoom=-1, so there is no position feedback.
  • ContinuousMove + Stop and GotoPreset are the only operations that behave.

Changes

  • Pass the scaled x / y / zoom to continuousMove().
  • After continuousMove(), wait the movement duration and send an explicit Stop rather
    than depending on the camera to self-terminate. Stop is idempotent, so this is harmless
    on cameras that do honor Timeout.
  • Add a ptzContinuousMoveDuration setting (default 1000ms), since the useful nudge size is
    camera-dependent — the NVR UI's tilt steps (±0.03125) are imperceptible at some velocities.

Note on the await

ptzCommand now awaits the Stop, so the promise resolves only once motion has actually
ceased. That felt more correct than fire-and-forget, but it does mean the call takes the
movement duration to resolve. Happy to switch to scheduling the stop without awaiting if
you'd prefer the snappier behavior.

Verified against the affected camera: movement occurs, then the camera parks and stays
parked; rapid repeated commands do not strand motion.

The ContinuousMove Timeout element is optional in the ONVIF spec and is not
universally honored; some cameras move indefinitely. Send an explicit Stop
after the movement duration instead of relying on the camera to self
terminate. Stop is idempotent, so this is safe on cameras that do honor it.

Also pass the speed-scaled x/y/zoom to continuousMove(), which were computed
and then discarded in favor of the unscaled command values.

Adds a ptzContinuousMoveDuration setting to tune the movement duration.
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