1Objective
Between 18:00 and 06:00, when a Dahua camera detects a person or a vehicle, the CenterV2 monitoring agent should see a bounding box drawn on the live video.
The constraint that shapes every decision below: CenterV2 will not render third-party metadata. Any design that ships box coordinates alongside the video and hopes the client draws them is dead on arrival.
The box has to be in the pixels. Once it is in the pixels it survives GeoVision, the forward to CenterV2, and the recording — nothing downstream has to understand it.
2Where the project stands
Settled with a full config dump. The overlay node simply does not exist on this hardware.
One core's worth covers roughly 18 cameras. The video path is affordable.
18 MB instead of 4 GB across twelve cameras. This decides the trailer hardware.
| Item | State | Note |
|---|---|---|
| Camera can burn boxes itself | Ruled out | Evidence in §4.1 |
| Box coordinate format | Confirmed | Verified arithmetically against a live event |
| Pilot recorder | Running | Built and tested 2026-08-19 |
| Live RTSP proxy | Designed | Not built yet |
| GeoVision integration | Not started | Generic RTSP confirmed accepted |
Open questions
| Question | Why it matters | Blocking |
|---|---|---|
| GeoVision channel licensing | The recommended design needs two channels per camera. At 12 cameras that is 24. | Needs answer |
| Substream keyframe interval | Currently one every 4 seconds. GeoVision would show black on connect, which reads as a dead camera. | Needs approval |
| MediaMTX download | Required to serve the RTSP stream the proxy produces. | Needs approval |
| Email alerting | Deferred by the operator. No credentials stored anywhere. | Parked |
3Architecture decisions
3.1 A re-encoding proxy is required
Two approaches were rejected. Having the camera burn the boxes is not supported on this hardware (§4.1). Shipping metadata to CenterV2 fails because it will not render it.
Accepted: decode the camera substream, draw, re-encode, republish as RTSP. GeoVision accepts a generic RTSP source, which makes this clean to wire up.
3.2 Two GeoVision channels per camera
Channel A connects straight to the camera mainstream and is what records. Evidence stays clean, with no synthetic annotation burned into it, and if the proxy dies the recording is untouched. Channel B carries the proxy output and is the only channel mapped to CenterV2 for live view.
The cost is double the channel count. The alternative — a single channel through the proxy — puts synthetic marks into evidence video and makes the proxy a single point of failure for evidence. That trade is a management call, not an engineering default.
3.3 The live proxy has to encode continuously
The clip recorder can sleep when the scene is empty. The live proxy cannot: a VMS treats a stream that stops as a failed camera and raises an alarm. Decode plus encode is a permanent floor whenever the proxy is up, not an event-triggered cost.
The obvious optimization — pass the stream through untouched while idle and only re-encode during detections — does not work. Switching codecs mid-session changes the stream headers and breaks timestamp continuity, so the client disconnects. That is exactly the failure we were trying to avoid.
3.4 The box the operator sees comes from our detector, not the camera
The camera's metadata is event-shaped, not track-shaped (§4.3). It cannot produce a box that follows a person across frames. It is valuable as a trigger — "look here now" — but the box drawn on screen should come from the verified Hailo detector.
Drawing unverified camera boxes would give false positives visual legitimacy on the operator's screen. That could make alert quality worse, not better, which is the opposite of the point.
3.5 Trailer hardware: video plumber, not brain
The Intel N150 has four efficiency cores and Quick Sync. It handles the video path if we use hardware encoding rather than software (§5.2). It cannot run continuous neural detection for several cameras — no usable NPU, weak integrated graphics. Boxes have to arrive from the camera or from the Hailo box over the network.
4Research findings
Everything below came from device 172.16.0.36 — an IPC-HFW5442E-Z4E on
firmware 2.840.0000000.13.R, build 2022-01-10.
4.1 The camera cannot burn boxes into the encoded stream Confirmed
A full read of the camera's overlay configuration returns only channel title, timestamp, custom titles, privacy masks, the PTZ on-screen displays, temperature and voltage. There is no rule-overlay or target-overlay node, so there is no flag to switch on for detection boxes.
This explains something worth internalizing: the green boxes visible in the Dahua web interface are not in the video. The browser draws them from a parallel metadata feed. The pixels never carried them, which is why GeoVision has never shown them and why no camera setting will produce them.
Whether a newer WizSense S3/S5 model behaves differently. Worth one check before treating this as a fleet-wide rule.
4.2 Box coordinate format Confirmed
Boxes arrive as corners — not position-plus-size — in Dahua's normalized 0–8192 space rather than pixels. This was verified, not inferred: the payload's own center field is exactly the midpoint of the box it ships with.
| Source | Field | Format | Space |
|---|---|---|---|
| Camera tripwire | Object.BoundingBox | [x1, y1, x2, y2] | 0–8192 |
| Camera smart motion | object[].Rect | [x1, y1, x2, y2] | 0–8192 |
| Frigate HTTP API | data.box | [x, y, w, h] | 0–1 |
| Frigate MQTT | after.box | [x1, y1, x2, y2] | pixels |
The two camera sources disagree with each other. For the same tracked object they reported different rectangles — different detectors, different instants. They must never be averaged or mixed.
These are the third and fourth box formats in the wider Jatagan work. Confusing formats has already produced one false conclusion on the AI Box project, so the rule now is to name the source before reasoning about any box.
4.3 What the metadata does not contain Confirmed
Across four real tripwire events:
- No confidence score. The field is present but always zero. There is nothing to threshold on.
- No trajectory. The track array is empty every time.
- No tracking. The start and stop events carry the identical frozen rectangle. It is a snapshot at the moment of crossing, not a box that follows anyone.
This is what forces decision 3.4.
4.4 Timestamps align to the millisecond Confirmed
Each event carries a presentation timestamp that correlates with the payload clock to within a few milliseconds across sampled pairs. This is the mechanism for placing a box on its exact frame without guessing at network latency.
4.5 Traps worth writing down
| Trap | Consequence | Status |
|---|---|---|
| The smart-motion object ID field is misspelled in the firmware | The parser has to misspell it too, deliberately | Confirmed |
| The event clock field appears to carry local time dressed as UTC | Reading it as true UTC would shift alignment by whole hours | Inferred — calibrate |
| Command-line tooling treats array indices in a URL as wildcards | A configuration write silently failed and returned an empty body | Confirmed & solved |
4.6 Side findings on the tripwire rules
Outside this project's scope, but observed in the same captures and directly affecting alert quality, so recorded here rather than lost.
- The active rule carries eight vertices for what should be a two-point tripwire, with the last two jumping back across the scene. The camera evaluates all eight.
- Observed consequence: two different people each triggered two crossings in opposite directions about two seconds apart. Two out of two. Every person crossing that rule may be generating two alerts. The fix is redrawing the tripwire in the camera, not in code.
- Both active rules have no size filtering at all, so a two-pixel object at the back of the scene qualifies the same as a person at ten meters.
5Measurements
All taken on the engineering workstation (28 cores) against the pilot camera, D1 substream. The trailer hardware is weaker, so these are ratios to scale from, not absolute budgets.
5.1 Idle path — listener plus rolling frame buffer
| Substream rate | One camera | Twelve cameras |
|---|---|---|
| 30 fps | 2.3% of a core | 0.27 cores |
| 15 fps | 1.4% of a core | 0.16 cores |
The drop is not exactly half because the event connection costs the same either way.
5.2 Encoder benchmark — one camera, D1 at 15 fps
| Path | CPU | Twelve cameras, always on |
|---|---|---|
| Pass through, no decode | 0.26% of a core | 0.03 cores |
| Software encode | 7.6% | 0.91 cores |
| Hardware encode (Quick Sync) | 5.3% | 0.64 cores |
NVIDIA encoding is compiled into our ffmpeg but unusable on this workstation — there is no NVIDIA GPU in it. On the N150 expect software encoding to scale far worse and Quick Sync to hold roughly steady, since it is a dedicated block rather than general-purpose cores.
5.3 Memory — the twelve-second pre-roll buffer
A clip has to start before the trigger, otherwise the video begins after the subject has already walked into frame. That means keeping recent video in memory at all times. How we keep it is the single most consequential decision for the trailer hardware.
| Buffer contents | One camera | Twelve cameras |
|---|---|---|
| Decoded frames at 30 fps | ~350 MB | 4.08 GB |
| Decoded frames at 15 fps | ~174 MB | 2.04 GB |
| Compressed packets | ~1.5 MB | 18 MB |
A 200× difference. An N150 machine typically ships with 8–16 GB, so four gigabytes of buffers alone is not viable and two is uncomfortable. Eighteen megabytes is free.
6Change log
6.1 Changes to live equipment
| Date | Device | Change | Reversible | Verified |
|---|---|---|---|---|
| 2026-08-19 | 172.16.0.36 | Substream frame rate 30 → 15. Mainstream untouched. | Yes | Config re-read & stream confirmed |
Nothing else on any camera, recorder or remote host has been modified by this project. Every other device interaction was read-only. The frame rate change was explicitly authorized in session; the pending keyframe change is not covered by that authorization and will be raised separately.
6.2 Local artifacts created
| Path | Purpose |
|---|---|
| C:\Users\lhuer\jatagan-bbox\ | Project root |
| bbox_clip_pilot.py | The pilot: listener, buffer, recorder, notifier |
| synthetic_test.py | Injects a fake detection so the record path can be validated without waiting for a person |
| measure_idle.py | Idle CPU measurement |
| measure_ram.py | Buffer memory measurement |
| LOGBOOK.md | Full local version of this document |
| C:\Users\lhuer\.jatagan\ | Credentials, deliberately outside any project folder so they never reach a repository |
| E:\jatagan-bbox-pilot\ | Clip output, sidecar data, run log |
6.3 Defect found and fixed during the pilot
Clip frame rate was hardcoded while the live substream ran at a different rate, so clips would have played at roughly half speed. The buffer now measures the real arrival rate and writes the clip at that rate. Caught by test T5, confirmed fixed by T6.
7Corrections to earlier conclusions
Kept deliberately. Each of these was believed and acted on before being disproven, and the record is more useful with them than without.
7.1 The camera does not give us a free trajectory
An earlier note recorded that tripwire events carried the camera's own computed trajectory, described as "a free second opinion." Four real events show it empty every time. Any design leaning on camera-supplied tracking had no foundation.
7.2 Buffering compressed packets — right call, wrong reason
First stated as the top processor optimization. Then measured: continuous decoding costs 1.4% of a core, so as a processor optimization it is nearly pointless, and it was deprioritized. Then measured from the memory side, where it is a 200× difference.
It is back to priority one — as a memory decision, not a processor one. On a 28-core workstation with abundant RAM the problem is invisible. On the trailer hardware it decides whether the system runs at all.
7.3 "An empty scene costs nothing"
True for the clip recorder, false for the live proxy. See 3.3.
8Resource inventory
Workstation
| Resource | Detail |
|---|---|
| Cores | 28 |
| Python | 3.13.2 — imaging and HTTP libraries present; PyAV not installed (needed for the packet-buffer rewrite) |
| ffmpeg | 8.1.1, bundled with Jatagan Footage Editor |
| Encoders available | Software and Intel Quick Sync both working; NVIDIA present but unusable |
| External disk | Samsung T7 Shield, 931 GB, 786 GB free |
Devices
| Device | Address | Notes |
|---|---|---|
| Pilot camera | 172.16.0.36 | IPC-HFW5442E-Z4E, on-screen label AIC-0-36 / D2P1-184 |
| Hailo filter PC | 172.16.10.61 | Remote shell is PowerShell 5.1. No email configuration exists there, contrary to an early assumption |
| GeoVision recorder | 172.16.11.245 | Accepts generic RTSP sources |
Credentials are never recorded in this document.
Existing code worth reusing
Two files in the earlier snapshot project turned out to cover most of the pilot's ground.
tripwire_email_notifier.py already implements the event listener, a video ring
buffer, box drawing in the camera's coordinate space, and email with anti-spam throttling.
ivs_watch.py contains a tuned algorithm for choosing which frame a detection
belongs to — prior art on exactly the alignment problem this project has to solve.
9Test log
| # | Date | Test | Result |
|---|---|---|---|
| T1 | 08-16 | 15 s event capture | Heartbeats only. Endpoint alive, format not confirmed |
| T2 | 08-16 | 100 s event capture | Heartbeats only. Reported as a null result rather than rounded up |
| T3 | 08-16 | 9 min event capture | Two people captured. Six usable events. Sections 4.2–4.6 all come from this |
| T4 | 08-19 | Preflight check | Camera, ffmpeg, disk, RTSP all pass. Verdict READY |
| T5 | 08-19 | Synthetic clip, 30 fps | Defect found — frame rate mismatch, clip would play at half speed |
| T6 | 08-19 | Synthetic clip after fix | 363 frames, 11.37 s, rate measured correctly |
| T7 | 08-19 | Visual check of a drawn frame | Box renders correctly; label and timestamp present; camera on-screen display intact |
| T8 | 08-19 | Idle CPU at 30 fps | 2.3% of a core |
| T9 | 08-19 | Camera set to 15 fps, re-measure | Confirmed arriving at 15.0 fps; idle CPU 1.4% |
| T10 | 08-19 | Synthetic clip on 15 fps stream | 194 frames, 11.28 s. Correct |
| T11 | 08-19 | Encoder benchmark | See section 5.2 |
| T12 | 08-19 | Live pilot window, real foot traffic | Running — results pending |
10Next steps
- Review the pilot output. The question it answers is whether the box lands on the right person at the right frame — not whether we can draw one, which is already settled.
- If alignment holds: stand up the streaming server, build the live proxy, and prove the chain end to end into GeoVision.
- Shorten the substream keyframe interval before the proxy work. A VMS showing four seconds of black on connect reads as a dead camera.
- Confirm GeoVision channel headroom, which decides the recording architecture.
- Move the pre-roll buffer from decoded frames to compressed packets before anything ships to the trailer. This is the 200× memory decision.