Skip to main content

Talking to Cameras

The engine's activity functions say "move this camera" or "get me an image." Behind those calls is a physical Axis camera on a pole somewhere, reachable only over its own quirky HTTP API (VAPIX), sitting behind a self-signed certificate, demanding digest authentication, and occasionally not answering. Alert.CA.VAPIXService is the one place all of that mess is handled, so the rest of the platform can just call a method.

The one idea to hold on to​

Every camera call goes through a single typed HttpClient pipeline, and each stage solves one of the camera's inconveniences. You inject IVAPIXClient, call PanTiltZoomAsync/GetImageAsync/ToggleGuardTour, and the pipeline does the rest:

That whole chain is wired once in AddVAPIXHttpClient. Three stages worth understanding:

  • The certificate stage accepts anything. Cameras ship self-signed certs, so the primary handler's ServerCertificateCustomValidationCallback returns true unconditionally. It's insecure by design — the alternative is not talking to the cameras at all — and it's isolated to this client so nothing else inherits it.
  • The auth stage speaks both dialects. For basic auth it sets the header and sends. For digest it does the real handshake: send unauthenticated, read the 401 challenge (realm, nonce, qop), compute the digest header, and resend. The device's AuthType picks the path.
  • The resilience stage bounds failure. A vapix-pipeline handler adds a 30-second timeout and retries twice (exponential backoff, 2s) — but only on transient faults (5xx or network exceptions), never on a 401 or 404 where retrying is pointless.
Gotcha: retries only cover transient faults

The retry policy's ShouldHandle fires solely on HttpRequestException or a 5xx status. A misconfigured credential (401) or a wrong path (404) fails immediately with no retry — correct, because hammering a camera that authoritatively said "no" wastes the 30s budget and the camera's limited request capacity. If a call isn't retrying, check whether the fault is actually transient.

Credentials are decrypted at the last moment​

A camera call needs a username, password, and address — all stored encrypted on the AcquisitionDeviceEntity in Cosmos. VapixRequestBuilder.BuildRequestBase is where they're turned back into usable values, and only if the device is flagged encrypted:

Username = acquisitionDeviceEntity.IsEncrypted
? await encryptionService.DecryptAsync(credential.Username) : credential.Username,

That DecryptAsync is the AES-CBC decrypt from the Infrastructure encryption service — the same key machinery documented there. Plaintext credentials exist only for the moment a request is built, never at rest.

Wrapping up​

VAPIXService is the single door to physical Axis cameras: one typed HttpClient whose stages accept the self-signed cert, perform basic or digest auth, and retry transient faults within a 30-second budget — fed by a request builder that decrypts credentials at the last moment. Everything else calls IVAPIXClient and stays ignorant of the camera's quirks.

Not every "camera" is real, though. Next: Simulated Cameras.