|
| 1 | +# Storebaelt On-Demand Live Webcam Enhancement Plan |
| 2 | + |
| 3 | +**Date:** 2026-06-03 |
| 4 | +**Scope:** Demand-driven live/HLS enhancement for the Storebaelt webcam publisher and Explorer UI. |
| 5 | +**Related background:** `Storebaelt_Webcam_Poster_Integration_Status_and_UX_Options_2026-06-03.md` |
| 6 | + |
| 7 | +## Goal |
| 8 | + |
| 9 | +Improve the Storebaelt webcam user experience without permanently running heavier live-video ingestion. |
| 10 | + |
| 11 | +The baseline publisher should remain cheap and reliable: it continues publishing poster-image freshness/status observations at the normal cadence. A live/HLS mode should activate only when a client explicitly requests it, remain active only while that client renews a short lease, and shut itself off automatically when no users are watching. |
| 12 | + |
| 13 | +## Design Principle |
| 14 | + |
| 15 | +Use a **lease with TTL**. |
| 16 | + |
| 17 | +A client does not permanently enable live processing. Instead, it requests a short-lived live session for one camera. While the client is actively displaying the webcam, it renews the lease. If renewals stop, the live worker expires the lease and stops HLS work for that camera. |
| 18 | + |
| 19 | +Example lease state: |
| 20 | + |
| 21 | +```text |
| 22 | +cameraId -> liveUntil |
| 23 | +``` |
| 24 | + |
| 25 | +Runtime behavior: |
| 26 | + |
| 27 | +- `now < liveUntil`: live capture is active for that camera. |
| 28 | +- `now >= liveUntil`: live capture is stopped for that camera. |
| 29 | +- new lease request: extend `liveUntil`. |
| 30 | + |
| 31 | +## Recommended Architecture |
| 32 | + |
| 33 | +### Existing service |
| 34 | + |
| 35 | +`storebaelt-webcams-publisher.service` |
| 36 | + |
| 37 | +Responsibilities: |
| 38 | + |
| 39 | +- poll poster JPEG endpoints every 5 minutes |
| 40 | +- compute image hashes |
| 41 | +- publish poster/status/freshness observations |
| 42 | +- remain the durable, low-cost baseline publisher |
| 43 | + |
| 44 | +### New companion service |
| 45 | + |
| 46 | +`storebaelt-webcams-live.service` |
| 47 | + |
| 48 | +Responsibilities: |
| 49 | + |
| 50 | +- expose a small live-session control surface |
| 51 | +- maintain in-memory leases per camera |
| 52 | +- activate HLS discovery/frame capture only for leased cameras |
| 53 | +- publish live status/frame-reference observations while active |
| 54 | +- stop per-camera work when leases expire |
| 55 | + |
| 56 | +The live service can run continuously with near-zero work while idle, or later be made socket-activated if operational simplicity permits. |
| 57 | + |
| 58 | +## Activation Interface Options |
| 59 | + |
| 60 | +### Option A: Small publisher-side HTTP API |
| 61 | + |
| 62 | +Initial pragmatic interface: |
| 63 | + |
| 64 | +```http |
| 65 | +POST /live/storebaelt/{cameraId}/lease |
| 66 | +Content-Type: application/json |
| 67 | +
|
| 68 | +{ "ttlSeconds": 60, "mode": "latestFrame" } |
| 69 | +``` |
| 70 | + |
| 71 | +Response: |
| 72 | + |
| 73 | +```json |
| 74 | +{ |
| 75 | + "cameraId": "storebaelt-tower", |
| 76 | + "liveUntil": "2026-06-03T21:10:00Z", |
| 77 | + "active": true, |
| 78 | + "mode": "latestFrame", |
| 79 | + "status": "starting" |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +Additional endpoints: |
| 84 | + |
| 85 | +- `GET /live/storebaelt/{cameraId}/status` |
| 86 | +- `DELETE /live/storebaelt/{cameraId}/lease` as an optional explicit stop signal |
| 87 | + |
| 88 | +This is the fastest and clearest first implementation. |
| 89 | + |
| 90 | +### Option B: CSAPI control stream |
| 91 | + |
| 92 | +Longer-term, model live activation as a CSAPI control stream such as `requestLiveWebcam`. |
| 93 | + |
| 94 | +Command fields: |
| 95 | + |
| 96 | +- `cameraId` |
| 97 | +- `ttlSeconds` |
| 98 | +- `mode`: `latestFrame`, `frameEveryNSeconds`, or `playlistMetadata` |
| 99 | +- optional `quality` |
| 100 | + |
| 101 | +This is more standards-aligned, but it depends on Explorer and server control-stream maturity. It should be treated as a follow-on migration path after the small HTTP control surface proves the workflow. |
| 102 | + |
| 103 | +### Option C: Direct client HLS playback after discovery |
| 104 | + |
| 105 | +The live service discovers/validates the HLS URL and returns it to Explorer, but the browser plays the stream directly. |
| 106 | + |
| 107 | +This can provide the most live-feeling experience, but it depends on CORS, token behavior, and player compatibility. It is useful to evaluate, but should not be the only plan. |
| 108 | + |
| 109 | +## Live Output Options |
| 110 | + |
| 111 | +### Preferred first output: live status plus frame-reference observations |
| 112 | + |
| 113 | +While a lease is active, the live service should publish a lightweight observation containing: |
| 114 | + |
| 115 | +- `cameraId` |
| 116 | +- `liveActive` |
| 117 | +- `liveMode` |
| 118 | +- `leaseUntil` |
| 119 | +- `hlsPlaylistUrl` when safely shareable |
| 120 | +- `frameUrl` or `latestFrameUrl` when available |
| 121 | +- `frameTime` |
| 122 | +- `frameSha256` if a frame is fetched/generated |
| 123 | +- `sourceLatencySeconds` when derivable |
| 124 | +- `status`: `starting`, `live`, `stale`, `error`, or `idle` |
| 125 | +- `errorMessage` only when needed and safe to expose |
| 126 | + |
| 127 | +This keeps Explorer aligned with the existing observation-driven UI model. |
| 128 | + |
| 129 | +### Alternative output: transient frame cache |
| 130 | + |
| 131 | +If generated frame images are needed, the live service can write latest frames to a short-lived local/cache path and publish a URL to that frame. |
| 132 | + |
| 133 | +This avoids storing image bytes in CSAPI observations, but requires an image-serving path and cache cleanup policy. |
| 134 | + |
| 135 | +## Explorer UX Plan |
| 136 | + |
| 137 | +Add webcam-specific controls to the deployed-system card: |
| 138 | + |
| 139 | +1. Always show poster status: |
| 140 | + - latest poster image |
| 141 | + - last checked time |
| 142 | + - image changed time |
| 143 | + - unchanged duration |
| 144 | + - stale badge when threshold is exceeded |
| 145 | + |
| 146 | +2. Add a `Start Live` control: |
| 147 | + - visible for Storebaelt webcam deployments |
| 148 | + - starts a 60 second lease for the selected camera |
| 149 | + - changes to `Live` or `Starting` status while active |
| 150 | + |
| 151 | +3. Renew the lease while the user is actively viewing: |
| 152 | + - renew every 30 seconds for a 60 second TTL |
| 153 | + - stop renewing when the card is closed, route changes, or tab visibility is lost for a configured grace period |
| 154 | + |
| 155 | +4. Show live fallback links: |
| 156 | + - embedded player URL |
| 157 | + - public Storebaelt webcam page |
| 158 | + |
| 159 | +5. Fail gracefully: |
| 160 | + - if live activation fails, keep poster image visible |
| 161 | + - show a compact `Live unavailable` state with the player link |
| 162 | + |
| 163 | +## Publisher Runtime Behavior |
| 164 | + |
| 165 | +The live service should be polite and bounded: |
| 166 | + |
| 167 | +- one worker task per active camera |
| 168 | +- max TTL accepted per lease, for example 120 seconds |
| 169 | +- default TTL 60 seconds |
| 170 | +- minimum renewal interval enforced server-side |
| 171 | +- stop HLS fetch/decode immediately after idle timeout |
| 172 | +- do not fetch HLS for cameras without active leases |
| 173 | +- avoid publishing unchanged live frames unless publishing heartbeat/status is intentional |
| 174 | + |
| 175 | +Recommended default live cadence while active: |
| 176 | + |
| 177 | +- status heartbeat every 5-10 seconds |
| 178 | +- frame extraction every 5-15 seconds, depending on observed source behavior and CPU cost |
| 179 | + |
| 180 | +## Data Model Additions |
| 181 | + |
| 182 | +Create a separate live/status datastream per Storebaelt camera, or extend the existing webcam image datastream only if the result schema remains clear. |
| 183 | + |
| 184 | +Preferred: separate output name such as `storebaeltWebcamLiveStatus`. |
| 185 | + |
| 186 | +Candidate result fields: |
| 187 | + |
| 188 | +- `cameraId` |
| 189 | +- `cameraTitle` |
| 190 | +- `liveActive` |
| 191 | +- `liveMode` |
| 192 | +- `leaseUntil` |
| 193 | +- `status` |
| 194 | +- `hlsPlaylistUrl` |
| 195 | +- `frameUrl` |
| 196 | +- `latestFrameUrl` |
| 197 | +- `frameTime` |
| 198 | +- `frameSha256` |
| 199 | +- `sourceLatencySeconds` |
| 200 | +- `playerUrl` |
| 201 | +- `pageUrl` |
| 202 | +- `errorMessage` |
| 203 | + |
| 204 | +Keep poster-source fields in the existing `storebaeltWebcamImage` output so long-term poster freshness remains easy to understand. |
| 205 | + |
| 206 | +## Implementation Phases |
| 207 | + |
| 208 | +### Phase 1: Poster freshness heartbeat |
| 209 | + |
| 210 | +- Publish heartbeat/status observations every 5 minutes, even when the poster image hash is unchanged. |
| 211 | +- Add fields such as `imageChanged`, `firstSeenTime`, `lastChangedTime`, `lastSeenTime`, `unchangedPollCount`, and `stalenessStatus`. |
| 212 | +- Update Explorer to separate `last checked` from `image changed`. |
| 213 | + |
| 214 | +### Phase 2: Live source reconnaissance |
| 215 | + |
| 216 | +- Inspect the Mediathand/Video.js player network behavior. |
| 217 | +- Identify whether HLS playlist URLs are stable, tokenized, or browser-bound. |
| 218 | +- Determine whether browser direct playback is viable. |
| 219 | +- Measure bandwidth, latency, and cache behavior. |
| 220 | + |
| 221 | +### Phase 3: Live lease service prototype |
| 222 | + |
| 223 | +- Add `storebaelt_webcams_live_service.py` or equivalent companion module. |
| 224 | +- Implement lease API and per-camera lease table. |
| 225 | +- Add bounded background worker activation per camera. |
| 226 | +- Publish live status observations with no frame extraction first. |
| 227 | + |
| 228 | +### Phase 4: Frame or playlist integration |
| 229 | + |
| 230 | +- Add HLS playlist discovery and validation. |
| 231 | +- Choose either direct playlist handoff, latest-frame extraction, or transient frame cache. |
| 232 | +- Publish live status/frame-reference observations only while leases are active. |
| 233 | + |
| 234 | +### Phase 5: Explorer live UX |
| 235 | + |
| 236 | +- Add `Start Live` and live status states to Storebaelt webcam cards. |
| 237 | +- Renew leases while the card is open. |
| 238 | +- Stop renewing when the user leaves the card. |
| 239 | +- Render live frame/playlist when available, otherwise fall back to poster image and external player links. |
| 240 | + |
| 241 | +### Phase 6: Operational hardening |
| 242 | + |
| 243 | +- Add systemd unit for the live service. |
| 244 | +- Add rate limits and max active leases. |
| 245 | +- Add logs/metrics for active lease count, source errors, and frame extraction failures. |
| 246 | +- Add cleanup for transient frames if used. |
| 247 | + |
| 248 | +## Risks and Mitigations |
| 249 | + |
| 250 | +| Risk | Mitigation | |
| 251 | +| --- | --- | |
| 252 | +| HLS URLs are tokenized or unstable | Discover per lease; cache only briefly; retain external player fallback. | |
| 253 | +| CORS blocks direct browser playback | Use server-side frame extraction or transient frame cache. | |
| 254 | +| Live extraction is CPU/bandwidth heavy | Activate only under lease; cap concurrent cameras and frame cadence. | |
| 255 | +| Source terms discourage heavy polling | Keep poster mode as default; use low live cadence; only fetch while user-requested. | |
| 256 | +| Latest observation time is confused with frame capture time | Store poll/check time and frame/image change time separately. | |
| 257 | +| Explorer leaves leases active after navigation | TTL expiration is authoritative; client stop signal is only an optimization. | |
| 258 | + |
| 259 | +## Acceptance Criteria |
| 260 | + |
| 261 | +- With no active Explorer webcam views, no HLS playlist or segment fetches occur. |
| 262 | +- Opening a Storebaelt webcam card and selecting live mode activates only that camera. |
| 263 | +- Closing the card or stopping renewals deactivates the camera after the TTL. |
| 264 | +- Poster image and freshness status remain available even when live mode fails. |
| 265 | +- The UI clearly distinguishes last checked time, image/frame changed time, and live activation state. |
| 266 | +- Oracle service logs show bounded, understandable live activation and idle behavior. |
| 267 | + |
| 268 | +## Recommendation |
| 269 | + |
| 270 | +Proceed with Phase 1 first, because it fixes the current stale-image confusion regardless of HLS viability. Then prototype the lease service with live status only before adding frame extraction. This keeps the design honest, observable, and reversible while preserving a path to a much better webcam experience. |
0 commit comments