Skip to content

fix(star6e): gate dual-VENC creation on record.mode, not record.enabled#86

Closed
wkumik wants to merge 1 commit into
OpenIPC:masterfrom
wkumik:fix/dual-record-bitrate-gate
Closed

fix(star6e): gate dual-VENC creation on record.mode, not record.enabled#86
wkumik wants to merge 1 commit into
OpenIPC:masterfrom
wkumik:fix/dual-record-bitrate-gate

Conversation

@wkumik

@wkumik wkumik commented Jun 13, 2026

Copy link
Copy Markdown

Problem

Dual-record mode (record.mode=dual) is supposed to encode an independent ch1 at record.bitrate for the SD recording while ch0 streams at video0 config. But when a client controls recording at runtime — sets record.enabled=false and starts via /api/v1/record/start — the recording silently captured ch0 (the stream bitrate) instead, so record.bitrate/record.gopSize had no effect. The documented "stream low / record high" mode was unreachable outside the auto-start-at-boot path.

Root cause

src/star6e_runtime.c: record routing keys on whether ps->dual exists (if (!ps->dual) → mirror path records ch0; else routes to the ch1 thread). But ps->dual was created only under:

if (vcfg->record.enabled &&
    (mode == "dual" || mode == "dual-stream")) { ... }

With record.enabled=false, ps->dual stayed NULL, so the API-triggered recording fell back to mirror mode and captured ch0. The channel topology (mode) was wrongly gated on the auto-start flag (enabled).

Fix

Gate dual-VENC creation on record.mode alone. record.enabled still controls only whether recording auto-starts at boot. The ch1 TS writer already no-ops while the recorder is closed (star6e_ts_recorder_write_stream returns early on fd < 0), so an idle ch1 (mode=dual, not yet recording) writes nothing until /api/v1/record/start opens the file.

Star6E only (Maruko has no TS recording).

Device verification (ssc338q + imx415)

mode=dual, enabled=false, bitrate=40000, recording started via /api/v1/record/start:

Bitrate GOP (keyframes / 8 s) Channel
Before ~5.2 Mbps 0.25 s (matches video0.gopSize) ch0 (stream)
After 41.3 Mbps 1.0 s (matches record.gopSize) ch1 (record)

ffprobe on the post-fix clip: bit_rate=41327232, 8 keyframes over 8.03 s.

Tradeoff

With mode=dual, ch1 now encodes continuously even when not recording (~118 fps total at 1080p59+1080p59, within the ~150 fps HW budget). This matches the documented dual-mode behavior. A follow-up could gate ch1 StartRecvPic on record-active to avoid idle encode.

Notes

  • No HTTP API change (record/start behaves the same; it just now correctly captures ch1).
  • Both backends build clean (make build SOC_BUILD=star6e / maruko). make verify's webui-check reports pre-existing embedded-blob drift unrelated to this change (this PR does not touch the WebUI).

🤖 Generated with Claude Code

The secondary (ch1) record channel was created at pipeline init only when
record.enabled was true. A runtime-control client (e.g. RubyFPV) that sets
record.enabled=false and starts recording later via /api/v1/record/start
never got ch1: the record path fell back to mirror mode and captured ch0
(the video0 stream bitrate, typically ~5-12 Mbps adaptive) instead of ch1
(record.bitrate, e.g. 40 Mbps). The documented "stream low / record high"
dual mode was unreachable outside the auto-start-at-boot path.

Channel topology now follows record.mode alone; record.enabled still
controls only whether recording auto-starts at boot. The ch1 TS writer
already no-ops while the recorder is closed, so an idle ch1 (mode=dual, not
yet recording) writes nothing until /api/v1/record/start opens the file.
Star6E only (Maruko has no TS recording).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@snokvist

Copy link
Copy Markdown
Collaborator

Thanks @wkumik — verified and much appreciated. 🙏

This fix has been folded into the v0.17.1 sync in #87 (cherry-picked with -x, your authorship preserved on the commit). It's integrated as 0.17.1 rather than 0.16.1 because the fork had already shipped 0.17.0 (HEVC RTP AP removal) by the time of integration, so the patch number follows chronologically.

Closing this in favor of #87. Your branch is left intact.

@snokvist snokvist closed this Jun 13, 2026
snokvist added a commit that referenced this pull request Jun 13, 2026
…e gate (#87)

Brings upstream from v0.16.0 to v0.17.1. Two functional changes plus docs.

v0.17.0 — Drop HEVC RTP Aggregation Packets (fork #142)
  AP (NAL type 48) removed from both backends; the wire is now single-NAL +
  FU-A only, the universally-supported subset of RFC 7798. In single-slice
  H.265 only VPS/SPS/PPS aggregated, and bundling the parameter set into one
  datagram made a single loss take out the whole GOP on a lossy link; it also
  broke majestic-tuned (single-NAL+FU-A-only) depacketizers. HevcApBuilder
  deleted; VPS/SPS/PPS now prepend as separate packets on IDR. HevcRtpStats
  drops ap_packets/ap_nals; [pktzr] line is nals N | rtp N | fill N B |
  single N | fu N. Dead h26x_util_hevc_get_layer_id/_get_tid_plus1 removed.
  Device-verified on Star6E IMX335 (192.168.1.13).

v0.17.1 — gate dual-VENC creation on record.mode, not record.enabled
  Cherry-picked from PR #86 by @wkumik (commit 9410bd7, authorship preserved).
  Dual-record mode never engaged under runtime control: with
  record.enabled=false + /api/v1/record/start, ps->dual stayed NULL and the
  record path fell back to mirror mode, capturing ch0 (stream bitrate) instead
  of ch1 (record.bitrate). Channel topology now follows record.mode alone;
  record.enabled still controls only boot auto-start. Star6E only.
  Device-verified by @wkumik on ssc338q + imx415.

Docs: single-PID reinit findings (fork #141), REFACTORING_PLAN/README/CRASH_LOG.

Both backends build clean (star6e + maruko); 1653 host tests pass.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants