Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
9616d07
feat(cv610): sensor.mode selects, the way it does on SigmaStar
snokvist Aug 16, 2026
275bbda
docs: contract 0.18.3, VERSION 0.65.3 for the CV610 selector
snokvist Aug 16, 2026
02c7023
fix(cv610): crop to the encoded aspect instead of squashing it
snokvist Aug 16, 2026
9108d7b
fix(cv610): run the shared config validation, and write the crop unco…
snokvist Aug 16, 2026
c12a5b1
config(cv610): default bitrate 8000 -> 2600, a seed that survives MCS0
snokvist Aug 16, 2026
c48f569
Merge pull request #228 from snokvist/feature/cv610-sensor-mode-selec…
snokvist Aug 16, 2026
edcf995
feat(cv610): write the IMX662 power-on register sequence
snokvist Aug 16, 2026
3177e90
feat(cv610): enable the IMX662 ISP algorithm blocks
snokvist Aug 16, 2026
022cdef
build(cv610): generate header dependencies for the sensor plugin
snokvist Aug 16, 2026
46ec584
feat(cv610): sharpen, DRC, bayer NR and the AWB saturation table
snokvist Aug 16, 2026
32f4f28
docs(cv610): describe what the IMX662 plugin actually programs
snokvist Aug 16, 2026
0d7dd8a
chore: v0.65.4 — CV610 IMX662 sensor init sequence and ISP tuning
snokvist Aug 16, 2026
c02c9fe
docs(cv610): spec the runtime IQ control surface as follow-up work
snokvist Aug 16, 2026
08b6fb5
feat(cv610): expose the ISP as a runtime IQ control surface
snokvist Aug 16, 2026
990c855
feat(webui): drive the IQ tab from backend capability, not backend name
snokvist Aug 16, 2026
e2cfd39
docs(cv610): record that DRC and dehaze ship bypassed, and close the …
snokvist Aug 16, 2026
9c85612
chore: v0.65.5 — CV610 runtime IQ control surface
snokvist Aug 16, 2026
d0a71f2
fix(cv610-iq): reject over-long arrays, and stop offering an import t…
snokvist Aug 16, 2026
5ce5378
docs: correct the 0.65.5 verification note for the review fixes
snokvist Aug 16, 2026
8f4abba
fix(iq): repair star6e/maruko regressions and reject out-of-range values
snokvist Aug 16, 2026
9ff9ca8
fix(cv610): write 0x44C0, and stop clearing a fixed bit in 0x30DD
snokvist Aug 16, 2026
2ed230c
Merge branch 'feature/cv610-imx662-sensor-init-block' into feature/cv…
snokvist Aug 16, 2026
bb59831
feat(cv610): fix low-light bitrate runaway, add QP bounds and AE ceil…
snokvist Aug 16, 2026
a303ab1
feat(cv610): select IMX662 HCG conversion gain at high analog gain
snokvist Aug 16, 2026
0e185b2
feat(cv610): cap ISP digital gain, and stop AGAIN_MAX lying about its…
snokvist Aug 16, 2026
bca7194
feat(cv610): expose bayer-NR strength and motion-detect temporal NR
snokvist Aug 16, 2026
a5b3c74
fix: adversarial pre-merge review — HCG compensation, tab regression,…
snokvist Aug 16, 2026
63bcbf5
cv610: write the IMX662 power-on register sequence and enable the ISP…
snokvist Aug 16, 2026
781a1e3
test(api): cover the routes capability against the callback pointer
snokvist Aug 17, 2026
27def74
cv610: runtime IQ control surface (#230)
snokvist Aug 17, 2026
9cbe609
cv610: low-light bitrate, HCG conversion gain, QP bounds and AE ceili…
snokvist Aug 17, 2026
ac72182
cv610: refuse to unload the MPP stack under a live consumer
snokvist Aug 17, 2026
f0bdbfe
cv610: move the unload guard next to the rmmod it protects
snokvist Aug 17, 2026
ae2596b
cv610: complete the device list, fix the line-boundary miss, fail closed
snokvist Aug 17, 2026
120c0f3
cv610: stop reloading the MPP modules on a venc restart
snokvist Aug 17, 2026
2c9314c
cv610: clear ISP on pre-clean, fix reload env, harden the verify suite
snokvist Aug 17, 2026
141ae28
test(cv610): actually restore the config the closing message claims
snokvist Aug 17, 2026
edf55c8
cv610: point the audio-mismatch hint at the init script, not the loader
snokvist Aug 17, 2026
386cb47
Merge pull request #232 from snokvist/fix/cv610-refuse-mpp-unload-wit…
snokvist Aug 17, 2026
92a8344
test(cv610): assert the hub OSD survives the restart, from the compos…
snokvist Aug 17, 2026
fcafaf4
test(cv610): fix two ways the new OSD assertion could lie
snokvist Aug 17, 2026
48fc1f7
docs(cv610): 23/23 on the suite, and the mode-change OSD window measu…
snokvist Aug 17, 2026
77de20d
Merge pull request #233 from snokvist/fix/cv610-no-module-reload-on-r…
snokvist Aug 17, 2026
d292c7d
cv610: recover audio after an abnormal venc exit
snokvist Aug 17, 2026
566873e
cv610: keep audio_initialized honest across the module cycle
snokvist Aug 17, 2026
bdf2f0f
cv610: act on the review — fix the fatal predicate, drop the inert calls
snokvist Aug 17, 2026
ea47830
cv610: put the module hint where the module-absent failure lands
snokvist Aug 17, 2026
d28ec24
cv610: recover audio after an abnormal venc exit (#234)
snokvist Aug 17, 2026
7522b16
cv610: delete the reload action instead of guarding it
snokvist Aug 17, 2026
4f893f3
cv610: remove the second unload path, the one review found
snokvist Aug 17, 2026
34155e6
cv610: delete the reload action and the start() module rollback (#235)
snokvist Aug 17, 2026
921b131
star6e: H.265 multi-slice output — video0.sliceCount binds MI_VENC_Se…
snokvist Aug 20, 2026
31987bc
feat(encoder): port resilience and slices to CV610 and Maruko (#237)
snokvist Aug 22, 2026
f3f3af4
fix: pre-upstream review pass over 0.65.3..0.67.0 (0.67.1)
snokvist Aug 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ enc-ctrl/tests/test_runner

# Source-built CV610 sensor-plugin artifacts
sensors/cv610/imx662/*.o
sensors/cv610/imx662/*.d
sensors/cv610/imx662/*.so

# Performance benchmark artifacts — local-only, not shipped upstream
Expand All @@ -41,3 +42,6 @@ a-*.d

# Serena LSP tooling config — local developer machine only, not shipped
/.serena/

# CI-pinned SDK checkouts (.github/workflows/main.yml lint-build-cv610)
.deps/
640 changes: 640 additions & 0 deletions HISTORY.md

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ MARUKO_ONLY_SRC := src/maruko_mi.c src/maruko_config.c src/maruko_video.c src/ma
STAR6E_ONLY_SRC := src/star6e_output.c src/star6e_audio.c src/star6e_hevc_rtp.c src/star6e_video.c src/star6e_pipeline.c src/star6e_controls.c src/star6e_runtime.c src/star6e_cus3a.c src/star6e_iq.c src/star6e_jpeg.c src/star6e_ipu.c src/star6e_ipu_yolo.c src/star6e_vpe_ports.c src/star6e_luma_tap.c src/star6e_awb.c
CV610_SRC := src/main.c src/backend_cv610.c src/cv610_runtime.c \
src/cv610_audio.c \
src/cv610_encoder_config.c \
src/cv610_iq.c \
src/cv610_modes.c \
src/cv610_pipeline.c src/cv610_validation.c src/backend.c \
src/venc_config.c src/venc_httpd.c src/venc_api.c src/venc_webui.c \
Expand Down Expand Up @@ -358,7 +360,7 @@ stage: build qr-decode
mkdir -p $(OUT_DIR)/isp-bins; cp -f iq-profiles/maruko-bin/*.bin $(OUT_DIR)/isp-bins/; \
fi; \
fi
@if [ "$(SOC_BUILD)" = "cv610" ]; then \
@set -e; if [ "$(SOC_BUILD)" = "cv610" ]; then \
$(MAKE) sensor-cv610 SOC_BUILD=cv610 \
CV610_CC="$(CV610_CC)" CV610_SDK_INC="$(CV610_SDK_INC)"; \
mkdir -p $(OUT_DIR)/sensors; \
Expand Down Expand Up @@ -392,7 +394,7 @@ TEST_SRCS := tests/test_runner.c tests/test_venc_config.c \
tests/test_sensor_select.c tests/test_venc_ring.c \
tests/test_file_util.c tests/test_h26x_util.c \
tests/test_h26x_param_sets.c \
tests/test_maruko_config.c \
tests/test_maruko_config.c tests/test_maruko_video.c \
tests/test_pipeline_common.c \
tests/test_codec_config.c tests/test_sdk_quiet.c \
tests/test_rtp_packetizer.c \
Expand Down Expand Up @@ -425,13 +427,14 @@ TEST_SRCS := tests/test_runner.c tests/test_venc_config.c \
TEST_LIB_SRCS := src/qr_scan.c tools/qr/waybeam_qr_format.c \
tools/qr/quirc/quirc.c tools/qr/quirc/decode.c \
tools/qr/quirc/identify.c tools/qr/quirc/version_db.c \
src/backend.c src/venc_config.c src/venc_api.c src/venc_httpd.c src/venc_webui.c src/venc_recordings.c src/sensor_select.c src/venc_ring.c src/venc_frame_ring.c src/file_util.c src/h26x_util.c src/h26x_param_sets.c src/intra_refresh.c src/isp_runtime.c src/maruko_config.c src/codec_config.c src/pipeline_common.c src/rtp_session.c src/sdk_quiet.c src/rtp_packetizer.c src/hevc_rtp.c src/star6e_hevc_rtp.c src/star6e_output.c src/star6e_audio.c src/audio_codec.c src/star6e_video.c src/star6e_recorder.c src/star6e_ts_recorder.c src/ts_mux.c src/rtp_sidecar.c src/stream_metrics.c src/output_socket.c src/timing.c src/idr_rate_limit.c src/venc_shm_throttle.c src/debug_osd_draw.c src/venc_jpeg.c src/mdns_wire.c src/mdns_beacon.c src/device_id.c src/framing_kalman.c src/attitude_est.c src/detect_dequant.c src/detect_wire.c src/star6e_vpe_ports.c src/maruko_scl_ports.c lib/cJSON.c
src/backend.c src/venc_config.c src/venc_api.c src/venc_httpd.c src/venc_webui.c src/venc_recordings.c src/sensor_select.c src/venc_ring.c src/venc_frame_ring.c src/file_util.c src/h26x_util.c src/h26x_param_sets.c src/intra_refresh.c src/isp_runtime.c src/maruko_config.c src/maruko_video.c src/maruko_output.c src/codec_config.c src/pipeline_common.c src/rtp_session.c src/sdk_quiet.c src/rtp_packetizer.c src/hevc_rtp.c src/star6e_hevc_rtp.c src/star6e_output.c src/star6e_audio.c src/audio_codec.c src/star6e_video.c src/star6e_recorder.c src/star6e_ts_recorder.c src/ts_mux.c src/rtp_sidecar.c src/stream_metrics.c src/output_socket.c src/timing.c src/idr_rate_limit.c src/venc_shm_throttle.c src/debug_osd_draw.c src/venc_jpeg.c src/mdns_wire.c src/mdns_beacon.c src/device_id.c src/framing_kalman.c src/attitude_est.c src/detect_dequant.c src/detect_wire.c src/star6e_vpe_ports.c src/maruko_scl_ports.c lib/cJSON.c

$(TEST_RUNNER): $(TEST_SRCS) $(TEST_LIB_SRCS) tests/test_helpers.h include/backend.h include/h26x_param_sets.h include/hevc_rtp.h include/isp_runtime.h include/maruko_config.h include/pipeline_common.h include/rtp_packetizer.h include/rtp_session.h include/rtp_sidecar.h include/star6e_audio.h include/star6e_hevc_rtp.h include/star6e_output.h include/star6e_recorder.h include/star6e_ts_recorder.h include/ts_mux.h include/audio_ring.h include/star6e_video.h include/stream_metrics.h include/venc_frame_ring.h include/venc_shm_throttle.h
$(HOST_CC) $(HOST_CFLAGS) $(TEST_SRCS) $(TEST_LIB_SRCS) -lpthread -ldl -lm -o $@

$(CV610_VALIDATION_TEST): tests/test_cv610_validation.c src/cv610_validation.c \
src/cv610_modes.c \
src/cv610_encoder_config.c src/intra_refresh.c \
src/venc_config.c src/codec_config.c lib/cJSON.c
$(HOST_CC) $(HOST_CFLAGS) -Werror $^ -lm -o $@

Expand All @@ -440,8 +443,9 @@ test: $(TEST_RUNNER) $(CV610_VALIDATION_TEST)
./$(CV610_VALIDATION_TEST)

test-werror: HOST_CFLAGS += -Werror
test-werror: $(TEST_RUNNER)
test-werror: $(TEST_RUNNER) $(CV610_VALIDATION_TEST)
./$(TEST_RUNNER)
./$(CV610_VALIDATION_TEST)

test-asan:
$(HOST_CC) $(HOST_CFLAGS) -Werror -fsanitize=address,undefined $(TEST_SRCS) $(TEST_LIB_SRCS) -lpthread -ldl -lm -o $(TEST_RUNNER)
Expand Down
42 changes: 14 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ omitted fields keep their compiled-in defaults.
"bitrate": 8192, "gopSize": 1.0,
"qpDelta": -4,
"sceneThreshold": 0, "sceneHoldoff": 2,
"sliceCount": 1,
"resilience": "off",
"framing": "off", "zoomX": 0.5, "zoomY": 0.5
},
Expand Down Expand Up @@ -309,7 +310,8 @@ omitted fields keep their compiled-in defaults.
- **`video0`** — rate control, fps, resolution, bitrate, GOP,
per-section QP delta. Video codec is hardcoded H.265 (HEVC).
Scene-change-triggered IDR (`sceneThreshold`,
`sceneHoldoff`) is Star6E-only. Intra-refresh is on Star6E and Maruko. The
`sceneHoldoff`) is on Star6E and Maruko. Intra-refresh and whole-access-unit H.265
slicing are on Star6E, Maruko and CV610. The
`framing` knob expands to either digital zoom (Star6E and Maruko) or image
stabilization (`stab` HW-crop / `stab-fill` floating-image, Star6E only;
live `pauseStab`).
Expand Down Expand Up @@ -383,7 +385,7 @@ curl http://<device-ip>:<port>/api/v1/version
```

```json
{"ok":true,"data":{"app_version":"...","backend":"star6e","contract_version":"0.2.0","config_schema_version":"0.2.0"}}
{"ok":true,"data":{"app_version":"0.67.0","backend":"star6e","contract_version":"0.18.5","config_schema_version":"1.0.0"}}
```

#### GET /api/v1/config
Expand Down Expand Up @@ -852,10 +854,10 @@ behaviour. Requires root (silent fallback otherwise).
A *separate* jitter source — a large I-frame whose serialization exceeds one
frame interval on a constrained uplink (e.g. 50 Mbps on a 100 Mbps link) — is
not a scheduling issue and is unaffected by this priority; mitigate it with a
[resilience preset](#resilience-preset-star6e--maruko) (intra-refresh) or a
[resilience preset](#resilience-preset-star6e--maruko--cv610) (intra-refresh) or a
lower bitrate.

#### Resilience preset (Star6E + Maruko)
#### Resilience preset (Star6E + Maruko + CV610)

A single field picks an error-resilience profile. Intra-refresh
(rolling GDR stripe), the SVC-T reference pyramid (refPred), and the GOP
Expand Down Expand Up @@ -941,32 +943,16 @@ prediction is from the previous frame either way.

| Field | Type | Mutability | Description |
|-------|------|------------|-------------|
| `video0.resilience` | string | **reboot** | `off` \| `rescue` \| `quality` \| `sprint` \| `racing` \| `endurance` \| `patrol` \| `rally` \| `range` \| `fpv` \| `ltr` \| `ltr:<N>` (default `off`) |
| `video0.resilience` | string | restart | `off` \| `rescue` \| `quality` \| `sprint` \| `racing` \| `endurance` \| `patrol` \| `rally` \| `range` \| `fpv` \| `ltr` \| `ltr:<N>` (default `off`) |
| `video0.gopSize` | double | restart | Seconds between IDRs. Honoured **only** when `resilience` is `"off"` or an `ltr` form; the other named presets override it. Live-reinit applies (no reboot). |

> ⚠️ **Resilience changes require a reboot on both Star6E and Maruko.**
> Setting `video0.resilience` (or any of the derived fields
> `intra_refresh_*` or `ref_*`) persists the new value to
> `/etc/waybeam.json` and returns `{"reboot_required": true}`. The
> live encoder pipeline keeps running the previous preset until the
> next daemon start.
>
> The SigmaStar MI SDK kernel module does not survive a live
> re-configure of these fields. Empirically confirmed on both
> backends (2026-05-15 bench testing):
>
> - **Star6E (Infinity6E)** — fork+exec respawn for the new config
> triggers an SoC kernel panic within 1–2 transitions; ICMP dies,
> requires a power-cycle.
> - **Maruko (Infinity6C)** — in-process pipeline reinit completes
> cleanly for most transitions, but one in a sweep of 7 zombied
> the daemon via a page fault in `MI_SYS_IMPL_FlushInputPortTasks`
> inside the `mi` kernel module. System stays up but waybeam dies
> and does not respawn — reboot is required to restart it.
>
> Different failure modes, same root cause. Cold-boot into any
> preset is 100 % reliable on both SigmaStar backends, so the reboot model is
> what we ship.
Resilience and slice-count changes are restart-required on all three encoder
backends. The API persists the value and uses the backend's process-respawn
handoff to build a fresh encoder graph; a hardware reboot is not part of the
normal apply contract. SigmaStar deliberately avoids in-process MI teardown
and reinitialization because vendor-kernel teardown has proven unsafe. Verify
the running result through `/api/v1/resilience/status` and startup slice
readback logs.

Expansion table:

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.65.2
0.67.1
3 changes: 2 additions & 1 deletion config/waybeam.default.cv610.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
"rcMode": "cbr",
"fps": 100,
"size": "1280x720",
"bitrate": 8000,
"bitrate": 2600,
"gopSize": 1.0,
"qpDelta": -4,
"sliceCount": 1,
"resilience": "off",
"framing": "off"
},
Expand Down
1 change: 1 addition & 0 deletions config/waybeam.default.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"maxQp": 0,
"sceneThreshold": 0,
"sceneHoldoff": 2,
"sliceCount": 1,
"resilience": "off",
"zoomX": 0.5,
"zoomY": 0.5,
Expand Down
1 change: 1 addition & 0 deletions config/waybeam.default.maruko.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"qpDelta": -4,
"sceneThreshold": 0,
"sceneHoldoff": 2,
"sliceCount": 1,
"resilience": "off",
"zoomX": 0.5,
"zoomY": 0.5,
Expand Down
78 changes: 61 additions & 17 deletions docs/CV610_BACKEND.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ large control surface.
(see "Sensor modes" below — the table in `src/cv610_modes.c` is the only
place they are written down).
- H.265 CBR, GOP, and I/P QP delta come from the existing `VencConfig` fields.
- Shared resilience presets drive row GDR and reference cadence with strict
vendor readback; frame-SHM carries GDR/ENHANCE metadata and copied droppable
NALs are marked TRAIL_N.
- `video0.sliceCount` enables whole-access-unit H.265 slicing. CV610 uses
32-pixel LCU-row units and keeps early per-slice output disabled.
- UDP and abstract UNIX outputs use the shared HEVC RTP packetizer.
- `frame-shm://` publishes the existing VFRM v1 whole-frame contract.
- The shared HTTP/WebUI API advertises a CV610-specific capability mask and
Expand All @@ -35,6 +40,26 @@ The graph uses VI-online mode, matching the standalone streamer's production
path. The module loader must provide a clean SYS/VB lifecycle; the backend
verifies the requested mode by reading it back before creating the VI pipe.

## Encoder capability delta

CV610 has a substantially broader vendor encoder surface than the current
backend exposes: nine declared RC families, multiple P/CRR GOP structures,
intra refresh, reference prediction, SVC, hierarchical QP, native frame-loss
and super-frame policies, ROI/QP maps, VUI/user data, and slice/low-delay
controls. SSC338Q currently has the broader device-proven Waybeam integration.

The evidence levels, live-device readback, exact comparison and deliberately
deferred controls are documented in
`documentation/SSC338Q_CV610_ENCODER_CAPABILITIES.md`. The resilience and
whole-access-unit slice port is tracked in
`documentation/CV610_RESILIENCE_SLICES_PLAN.md`.

The port is confirmed on device at 1080p30/60/100. Requests for 1, 3, 4, 6, 9,
12 and 17 slices delivered the requested VCL NAL census, reference cadences and
TRAIL_N counts matched, and an IDR-started 1080p60 capture decoded cleanly
under FFmpeg `-xerror`. Native SVC, extra RC/GOP modes and encoder-side loss
policies remain deliberately deferred.

## Build

The public OpenHisilicon headers and OpenIPC CV610 runtime library directory
Expand Down Expand Up @@ -66,10 +91,25 @@ chmod +x /etc/init.d/S95waybeam /usr/bin/load-cv610-online
```

The init script invokes the staged `/usr/bin/load-cv610-online` module
loader before starting the daemon and unloads the MPP stack after graceful
termination. Only one service may own the graph; disable the standalone
loader before starting the daemon. It does **not** unload the MPP stack on
stop: the HiSilicon drivers take no module reference for an open fd, so an
`rmmod` under a live consumer (typically `waybeam_hub` holding `/dev/rgn`)
frees file operations still in use and wedges the SoC hard enough to need a
physical power cycle. Unloading is done only by an explicit
`load-cv610-online stop`, which refuses while any consumer holds an MPP
device. Only one service may own the graph; disable the standalone
`S95cv610-streamer` service when enabling `S95waybeam`.

Three operator rules follow from that:

- **Recovery after an abnormal exit is a plain `S95waybeam start`.** The
modules are still loaded and the daemon re-initialises against them; there
is no `reload` action and no module rollback on a failed start.
- **Changing `CV610_AUDIO` or `CV610_SENSOR_PROFILE` needs a reboot.** The
loader refuses to re-load a differing module set over a live one.
- **A boot that has seen a hard kill cannot be unloaded cleanly.** Reboot
rather than fighting it.

`CV610_SENSOR_PROFILE` picks the sensor clock the modules load with. It is
now only a fallback: the daemon sets the clock for the selected mode during
bring-up (see "Sensor clock" below), so the profile matters only against a
Expand All @@ -80,8 +120,8 @@ Audio is opt-in in both layers: set `CV610_AUDIO=1` in
`open_aenc`, and `open_acodec`, then set `audio.enabled=true`, 48000 Hz, mono,
Opus, and a non-negative `outgoing.audioPort` in `/etc/waybeam.json`. The
daemon refuses other CV610 audio formats. The init script tracks the daemon
across API-driven process respawns and will not unload modules while any
Waybeam process still owns the graph. `audio.enabled` and `audio.mute` are
across API-driven process respawns; module unloading is guarded separately in
`load-cv610-online`, which refuses while any process holds an MPP device. `audio.enabled` and `audio.mute` are
restart-required HTTP controls; the rest of the audio group is hardcoded and
advertised unsupported. Enabling audio from a video-only boot still needs
kernel modules that a daemon respawn cannot load, so the daemon warns and
Expand Down Expand Up @@ -153,21 +193,22 @@ and continues on the boot-time clock.

## Deferred phases

1. Device smoke-test frame-SHM, repeated restart cycles, and a bounded soak.
1. Run a longer-duration soak beyond the bounded device matrices already
completed.
2. Add live output redirection and encoder output-FPS control.
3. Add frame-SHM pressure metrics/throttling and sidecar parity.
4. Port advanced features selectively: IQ controls and recording, and only
then consider dual VENC or stabilization. Add live audio gain/mute only
after the analog control path can be operator-verified.
4. Port recording selectively, and only then consider dual VENC or
stabilization. Add live audio gain/mute only after the analog control path
can be operator-verified.

Unsupported SigmaStar-only fields remain in the common configuration schema
but are marked unsupported in `/api/v1/capabilities` and rejected before
mutation. This keeps the shared dashboard honest while the CV610 control
surface grows feature by feature.

The pull-request CI cross-builds this experimental backend, but the production
release workflow remains limited to Star6E and Maruko until the integrated
binary passes the phase-1 device gate above.
Pull-request verification cross-builds this backend. Release promotion remains
gated on the CV610 build plus the focused device matrix documented in
`documentation/CV610_RESILIENCE_SLICES_PLAN.md`.

## Hardware verification

Expand All @@ -185,9 +226,12 @@ The integrated audio path initialized on the same target, resolved the known
ACODEC power-up race after its expected 100 ms retry, and produced about 100
Opus access units/s with zero reported send drops. A host receiver checked 12
consecutive RTP v2/PT98 packets: sequence deltas were 1 and 48 kHz timestamp
deltas were 480, exactly 10 ms. No microphone was connected, so acoustic
content is explicitly not operator-verified. A later service-stop hang was
localized to stale pidfile handling after an API respawn, not audio teardown;
the init-script fix requires a power-cycle and device retest. See
`documentation/CRASH_LOG.md`. Frame-SHM and long-duration soak verification
remain pending.
deltas were 960, exactly 20 ms (`CV610_AUDIO_POINT_NUM` 960; the 480/10 ms
figure predates the Opus frame-size parity fix). No microphone was connected, so acoustic
content is explicitly not operator-verified.

On 2026-08-22 the resilience/slice branch was also confirmed through
frame-SHM at 1080p30/60/100. Slice requests 1, 3, 4, 6, 9, 12 and 17 produced
the requested VCL census; a 380-frame IDR-started 1080p60 capture decoded
cleanly with FFmpeg `-xerror`; and GDR/ENHANCE metadata, TRAIL_N rewriting and
one-second loss recovery passed. Only a longer-duration soak remains pending.
Loading
Loading