Hikvision Cameras & NVRs
Hikvision is a production-tier adapter . It communicates with cameras and NVRs through Hikvision’s ISAPI HTTP+XML protocol with full Digest authentication. The adapter includes input validation on channel/track/snapshot IDs, an SSRF guard on NVR credential resolution, and tenant scoping on every NVR lookup.
What works
Section titled “What works”| Feature | Notes |
|---|---|
| Live MJPEG / snapshot streaming | _SnapshotCache shares one ISAPI fetch per channel across all concurrent viewers |
| PTZ control | Pan / tilt / zoom continuous move, presets (read, set, go-to), patrols |
| Recording playback | Timeline search, segment download |
| NVR import wizard | Auto-discover channels, inherit credentials per-channel with org scoping |
| Motion / line-crossing / intrusion events | Ingest via long-poll alertStream |
| License-plate recognition (LPR) | Event tagging on supported cameras |
| Camera groups, recording templates, view layouts | Full CRUD |
Hik-Connect cloud relay is not supported - local ISAPI only. Two-way audio (G.711u) is supported on compatible cameras via POST /api/v1/cameras/{id}/audio/start and POST /api/v1/cameras/{id}/audio/stop.
Adapter tier
Section titled “Adapter tier”| DG | CB | RR | TS | SSRF | RG |
|---|---|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
See Adapter Contract for what each column means.
Prerequisites
Section titled “Prerequisites”- Hikvision camera or NVR firmware V4.x or later (ISAPI support)
- HTTP (port 80) or HTTPS (port 443) access from the FreeSDN host
- An admin-level account or a dedicated operator account with remote login enabled
Adding a standalone camera
Section titled “Adding a standalone camera”- In FreeSDN, open Video Surveillance → Cameras and click Add Camera.
- Select vendor Hikvision, enter the camera IP/hostname, port, and credentials.
- FreeSDN tests the ISAPI connection. On success the camera appears in the camera list.
To add via the API directly:
# Add the camera - supply credentials directly in the same request bodycurl -X POST https://freesdn.example.com/api/v1/cameras/ \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{ "name": "Lobby", "ip_address": "10.0.1.50", "port": 80, "vendor": "hikvision", "username": "freesdn", "password": "s3cr3t", "site_id": "<site-uuid>" }'Adding an NVR (brownfield import)
Section titled “Adding an NVR (brownfield import)”The NVR import wizard is a four-step process: test connection → discover channels → configure → import.
Step 1 - Test connection
Section titled “Step 1 - Test connection”curl -X POST https://freesdn.example.com/api/v1/cameras/nvrs/test-connection \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{ "host": "10.0.1.64", "port": 80, "username": "freesdn", "password": "s3cr3t" }'Response includes device_name, model, firmware_version, serial_number.
Step 2 - Discover channels
Section titled “Step 2 - Discover channels”curl -X POST https://freesdn.example.com/api/v1/cameras/nvrs/discover \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{ "host": "10.0.1.64", "port": 80, "username": "freesdn", "password": "s3cr3t" }'Returns a channels list (channel ID, name, online status, PTZ flag, RTSP main/sub URLs) and a storage summary (total/used GB per disk).
The adapter queries three ISAPI endpoints in order and uses the first that responds:
GET /ISAPI/ContentMgmt/InputProxy/channels(most NVRs)GET /ISAPI/System/Video/inputs/channelsGET /ISAPI/Streaming/channels(fallback)
Step 3 - Import
Section titled “Step 3 - Import”curl -X POST https://freesdn.example.com/api/v1/cameras/nvrs/import \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{ "host": "10.0.1.64", "port": 80, "username": "freesdn", "password": "s3cr3t", "site_id": "<site-uuid>", "name": "Office NVR", "selected_channels": [1, 2, 3, 4] }'selected_channels is optional - omit it to import all enabled channels. The import is idempotent on external_device_id; re-importing the same NVR automatically re-syncs its channels and returns a normal 201 response with "synced": true - no error is raised and no manual sync call is required.
Step 4 - Sync (on-demand refresh)
Section titled “Step 4 - Sync (on-demand refresh)”After the initial import, sync re-queries the NVR for current channels, adds new cameras, marks missing cameras offline, and updates storage stats:
curl -X POST https://freesdn.example.com/api/v1/cameras/nvrs/<nvr-uuid>/sync \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>"Returns {"added": N, "removed": N, "updated": N}.
Stream URLs
Section titled “Stream URLs”The adapter constructs RTSP URLs using Hikvision’s NVR channel-numbering convention:
| Stream | URL pattern |
|---|---|
| Main stream, channel N | rtsp://user:pass@{host}:554/Streaming/Channels/{N*100+1} |
| Sub stream, channel N | rtsp://user:pass@{host}:554/Streaming/Channels/{N*100+2} |
| Standalone camera main | rtsp://user:pass@{host}:554/Streaming/Channels/101 |
Snapshot:
GET /ISAPI/Streaming/channels/{N*100+1}/picturePTZ control
Section titled “PTZ control”PTZ is available on supported cameras via the cameras module:
# Continuous move (pan right) - action and speed are query paramscurl -X POST 'https://freesdn.example.com/api/v1/cameras/<id>/ptz?action=right&speed=50' \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>"
# Go to a saved preset - action=preset with the preset number as a query paramcurl -X POST 'https://freesdn.example.com/api/v1/cameras/<id>/ptz?action=preset&preset=1' \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>"
# Save (set) a new preset at the current camera position - preset number and name are query paramscurl -X POST 'https://freesdn.example.com/api/v1/cameras/<id>/ptz/presets?preset=1&name=Lobby+Default' \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>"Recording playback
Section titled “Recording playback”Search recordings by time range:
curl -H "Cookie: freesdn_access=<token>" -H "X-CSRF-Token: <csrf>" \ "https://freesdn.example.com/api/v1/cameras/recordings/search?camera_id=<id>&start_time=2026-06-01T00:00:00Z&end_time=2026-06-01T06:00:00Z"Returns a list of recording segments with start/end times and a playback URL per segment.
Events
Section titled “Events”The adapter subscribes to the long-poll ISAPI alertStream and ingests motion, line-crossing, intrusion, and LPR events into the FreeSDN event bus. Events appear on the Events page and can trigger Fabric connections.
Security notes
Section titled “Security notes”- Credentials are encrypted at rest with Fernet (derived from
ENCRYPTION_SALT). - Adapter responses are passed through the central
redact_secretsfunction (~89 sensitive keys plus Proxmoxipconfig0-ipconfig31prefix patterns, camelCase-aware) before returning to the UI. Any credential-bearing fields in ISAPI responses are stripped at this layer. - Channel IDs, track IDs, and snapshot IDs are validated (
[0-9]+, bounded) before being interpolated into ISAPI URL paths. - SSRF guard: the NVR
hostfield is validated to block loopback, link-local, and cloud metadata endpoints. RFC 1918 private ranges are allowed by default (on-prem deployments). SetBLOCK_PRIVATE_CAMERA_SUBNETS=1to also block private ranges; useALLOWED_CAMERA_SUBNETS(comma-separated CIDRs) to add custom allowlist entries.
Frontend pages
Section titled “Frontend pages”- CamerasPage - camera grid with live thumbnail previews
- CameraDetailPage - live stream, PTZ, events, recordings tabs
- CameraWallPage - multi-camera wall view
- NVRListPage / NVRDetailPage - NVR list and per-NVR channel view
- MultiPlaybackPage - synchronized multi-channel playback
Backend route prefix
Section titled “Backend route prefix”All cameras-module endpoints mount under /api/v1/cameras/. Sub-routers include nvrs, events, groups, views, recording-templates, access, streams/hls, reports, and lpr.