- This document is the source of truth for the runtime HTTP API.
- Any added, changed, or removed HTTP endpoint behavior must be reflected here.
- Keep endpoints lean and focused on direct operational value.
- Accepted
/api/v1/setand/api/v1/defaultschanges are persisted to the registered config path before the response returns. Manual/api/v1/restartstill reloads exactly what is already on disk. - Keep JSON payloads simple and descriptive.
- Keep mutability semantics explicit:
live— applied immediately without pipeline restart.restart_required— triggers automatic pipeline reinit (teardown + rebuild).read_only— cannot be changed via API.
contract_version:0.10.1status:active
- Non-breaking changes: add optional fields, add new endpoints, extend enum values.
- Breaking changes: remove endpoints, rename fields, change required field semantics.
- For every breaking change: increment contract major version, add migration note, update
HISTORY.md. - For every non-breaking change: increment contract minor/patch version, update this file.
- HTTP/1.0, all methods use
GET(compatible with BusyBox wget) - Default port: 80 (configurable via
system.web_portin config) - Response content type:
application/json; charset=UTF-8 - Query parameters: field name is the key, value (if any) follows
=
{
"ok": true,
"data": {}
}{
"ok": false,
"error": {
"code": "string_code",
"message": "human readable message"
}
}| Code | HTTP Status | Meaning |
|---|---|---|
invalid_request |
400 | Missing or malformed parameters |
validation_failed |
400/409 | Value rejected by field or config validation |
not_found |
404 | Unknown field or route |
record_active |
409 | Action blocked while recording is in progress |
not_implemented |
501 | Apply callback not available for this field |
internal_error |
500 | Server-side failure |
Return app, backend, schema, and contract version information.
curl http://<device-ip>/api/v1/versionResponse 200:
{
"ok": true,
"data": {
"app_version": "0.1.7",
"contract_version": "0.10.1",
"config_schema_version": "1.0.0",
"backend": "star6e"
}
}Return the full active runtime config.
curl http://<device-ip>/api/v1/configResponse 200:
{
"ok": true,
"data": {
"config": {
"system": { "webPort": 80, "overclockLevel": 2, "verbose": false },
"sensor": { "index": -1, "mode": -1, "unlockEnabled": true, "..." : "..." },
"isp": { "sensorBin": "/etc/sensors/imx415_greg_fpvXVIII-gpt200.bin", "legacyAe": false, "aeFps": 15, "gainMax": 0, "awbMode": "auto", "awbCt": 5500, "keepAspect": true },
"image": { "mirror": false, "flip": false, "rotate": 0 },
"video0": { "codec": "h265", "rcMode": "cbr", "fps": 90, "size": "auto", "bitrate": 8192, "gopSize": 1.0, "qpDelta": 0, "frameLost": true, "sceneThreshold": 0, "sceneHoldoff": 2, "intraRefreshMode": "off", "intraRefreshLines": 0, "intraRefreshQp": 0, "zoomPct": 0.0, "zoomX": 0.5, "zoomY": 0.5 },
"outgoing": { "enabled": true, "server": "udp://192.168.2.20:5600", "streamMode": "rtp", "maxPayloadSize": 1400, "connectedUdp": false },
"fpv": { "roiEnabled": true, "roiQp": 0, "roiSteps": 2, "roiCenter": 0.25, "noiseLevel": 0 },
"record": { "enabled": false, "mode": "off", "dir": "/tmp/sdcard", "format": "ts", "maxSeconds": 300, "maxMB": 500 },
"debug": { "showOsd": false }
},
"runtime": {
"active_precrop": { "x": 0, "y": 240, "w": 2560, "h": 1440 }
}
}
}The runtime block is read-only and reports pipeline state that is not
part of the editable config:
active_precrop— VIF crop rectangle currently programmed (includes any sensor overscan offsets or SCL crop origin). Present whenever a Star6E or Maruko pipeline has been started; absent before pipeline start or after pipeline stop.
Return per-field mutability and backend support.
curl http://<device-ip>/api/v1/capabilitiesResponse 200:
{
"ok": true,
"data": {
"fields": {
"video0.bitrate": { "mutability": "live", "supported": true },
"video0.fps": { "mutability": "live", "supported": true },
"video0.gop_size": { "mutability": "live", "supported": true },
"video0.qp_delta": { "mutability": "live", "supported": true },
"video0.codec": { "mutability": "restart_required", "supported": true },
"video0.size": { "mutability": "restart_required", "supported": true },
"video0.scene_threshold": { "mutability": "restart_required", "supported": true },
"video0.scene_holdoff": { "mutability": "restart_required", "supported": true },
"video0.intra_refresh_mode": { "mutability": "restart_required", "supported": true },
"video0.intra_refresh_lines": { "mutability": "restart_required", "supported": true },
"video0.intra_refresh_qp": { "mutability": "restart_required", "supported": true },
"video0.zoom_pct": { "mutability": "restart_required", "supported": true },
"video0.zoom_x": { "mutability": "live", "supported": true },
"video0.zoom_y": { "mutability": "live", "supported": true },
"system.verbose": { "mutability": "live", "supported": true },
"outgoing.enabled": { "mutability": "live", "supported": true },
"outgoing.server": { "mutability": "live", "supported": true },
"outgoing.stream_mode": { "mutability": "restart_required", "supported": true },
"outgoing.connected_udp": { "mutability": "restart_required", "supported": true },
"fpv.roi_qp": { "mutability": "live", "supported": true }
}
}
}(truncated — all fields listed in actual response)
supported is backend-specific. Current Star6E and Maruko builds both expose
scene detection, intra refresh, and digital zoom fields.
Majestic-compatible alias of /api/v1/config.
curl http://<device-ip>/api/v1/config.jsonRead a single config field. The field name is the query parameter key (no value needed).
# Read current bitrate
curl "http://<device-ip>/api/v1/get?video0.bitrate"
# Read current codec
curl "http://<device-ip>/api/v1/get?video0.codec"
# Read current qpDelta
curl "http://<device-ip>/api/v1/get?video0.qp_delta"
# Read a string field
curl "http://<device-ip>/api/v1/get?isp.sensor_bin"Response 200:
{"ok":true,"data":{"field":"video0.bitrate","value":8192}}{"ok":true,"data":{"field":"video0.codec","value":"h265"}}{"ok":true,"data":{"field":"video0.qp_delta","value":0}}{"ok":true,"data":{"field":"isp.sensor_bin","value":"/etc/sensors/imx415_greg_fpvXVIII-gpt200.bin"}}Error 400 — missing field name:
{"ok":false,"error":{"code":"invalid_request","message":"missing query parameter (field name)"}}Error 404 — unknown field:
{"ok":false,"error":{"code":"not_found","message":"unknown config field"}}Majestic-style camelCase aliases are also accepted for selected fields,
including fpv.roiQp, fpv.roiEnabled, fpv.roiSteps, fpv.roiCenter,
fpv.noiseLevel, isp.sensorBin, isp.awbMode, isp.awbCt,
isp.keepAspect, video0.rcMode, video0.gopSize, video0.qpDelta,
video0.sceneThreshold, video0.sceneHoldoff,
video0.intraRefreshMode, video0.intraRefreshLines,
video0.intraRefreshQp, video0.zoomPct, video0.zoomX, video0.zoomY,
outgoing.maxPayloadSize,
outgoing.audioPort, system.webPort, and system.overclockLevel.
Write a config field. The field name is the query key, the new value follows =.
Live fields (mutability: "live") are applied immediately without pipeline restart:
# Change bitrate to 4096 kbps
curl "http://<device-ip>/api/v1/set?video0.bitrate=4096"
# Change FPS
curl "http://<device-ip>/api/v1/set?video0.fps=60"
# Swap ISP tuning bin (empty = auto-detect /etc/sensors/<sensor>.bin)
curl "http://<device-ip>/api/v1/set?isp.sensorBin=/etc/sensors/imx415_fpv.bin"
# Change GOP interval (seconds between keyframes; 0 = all-intra)
curl "http://<device-ip>/api/v1/set?video0.gop_size=0.5"
# Bias relative I-frame QP (Majestic-compatible range: -12..12)
curl "http://<device-ip>/api/v1/set?video0.qp_delta=-4"
# Pan within the active digital zoom crop
curl "http://<device-ip>/api/v1/set?video0.zoomX=0.25&video0.zoomY=0.75"
# Apply multiple live fields atomically in one request
curl "http://<device-ip>/api/v1/set?video0.bitrate=4096&system.verbose=true"
# Coupled live timing changes can be sent together
curl "http://<device-ip>/api/v1/set?video0.fps=30&video0.gopSize=1.0"When video0.scene_threshold is non-zero, the inline scene detector tracks
frame size EMA and requests IDR after scene change spikes settle.
If a GET /api/v1/set request contains multiple key=value pairs joined by
&, every field must be live. Mixed live + restart requests are rejected.
Duplicate fields are also rejected after alias canonicalization, so
video0.qp_delta and video0.qpDelta cannot appear in the same batch.
Response 200:
{"ok":true,"data":{"field":"video0.bitrate","value":4096}}Response 200 for multi-set:
{"ok":true,"data":{"applied":[{"field":"video0.bitrate","value":4096},{"field":"system.verbose","value":true}]}}Restart-required fields (mutability: "restart_required") trigger an automatic
pipeline reinit (sensor→VIF→VPE→VENC teardown and rebuild):
# Change resolution (single call, triggers one pipeline reinit)
curl "http://<device-ip>/api/v1/set?video0.size=1280x720"
# Use sensor native resolution (default — no downscaling)
curl "http://<device-ip>/api/v1/set?video0.size=auto"
# Preset shortcuts also work
curl "http://<device-ip>/api/v1/set?video0.size=720p"
curl "http://<device-ip>/api/v1/set?video0.size=1080p"
# Enable scene-change IDR control
curl "http://<device-ip>/api/v1/set?video0.scene_threshold=150"
# Enable 2x digital zoom (encoded resolution becomes half width/height)
curl "http://<device-ip>/api/v1/set?video0.zoomPct=0.5"Response 200 (includes "reinit_pending": true):
{"ok":true,"data":{"field":"video0.size","value":"1280x720","reinit_pending":true}}Restart/reinit writes stay single-field by design. Even though the main loop debounces reinit requests, clients should send restart-required changes one at a time and let each accepted write schedule the pipeline rebuild.
Adaptive control usage notes:
- Keep
video0.scene_threshold=0for fixed-GOP workflows and drive keyframe interval throughvideo0.gop_size. - On the current Star6E IMX335 bench, a practical starting point is:
video0.sceneThreshold=150,video0.sceneHoldoff=2. - Tune threshold first, holdoff second. In practice, threshold changes are a safer first response than raising holdoff.
Example Star6E tuning sequence:
curl "http://<device-ip>/api/v1/set?video0.sceneThreshold=150"
curl "http://<device-ip>/api/v1/set?video0.sceneHoldoff=2"Validation errors — some values are rejected before being applied:
# Attempt to switch Star6E RTP mode to h264
curl "http://<device-ip>/api/v1/set?video0.codec=h264"Error 409:
{"ok":false,"error":{"code":"validation_failed","message":"star6e RTP mode currently supports h265 only"}}Error 501 — apply callback not available:
{"ok":false,"error":{"code":"not_implemented","message":"apply callback not available"}}Error 400 — multi-set included a restart-required field:
{"ok":false,"error":{"code":"invalid_request","message":"multi-set only supports live fields; restart-required fields must be set one at a time"}}The same camelCase aliases listed above are accepted here for Majestic-oriented clients.
- Type: signed integer
- Range:
-12..12 - Mutability:
live - Alias:
video0.qpDelta - Semantics: adjusts I-frame QP relative to P-frame; negative values lower I-frame QP (higher quality keyframes), positive values raise it.
- Types: double
- Ranges:
video0.zoom_pct:0.0to disable zoom, or0.25..1.0crop fractionvideo0.zoom_x:0.0..1.0video0.zoom_y:0.0..1.0
- Mutability:
video0.zoom_pct:restart_requiredbecause it changes encoded resolutionvideo0.zoom_x,video0.zoom_y:live
- Aliases:
video0.zoomPct,video0.zoomX,video0.zoomY - Semantics: digital zoom uses a 1:1 crop. The crop window and encoded output
resolution shrink together; there is no SCL upscale and no additional output
bandwidth pressure.
zoom_xandzoom_ymove the crop center inside the active aspect-ratio-corrected source surface.
Return the configured target FPS from the active runtime config.
curl http://<device-ip>/api/v1/fps/configResponse 200:
{"ok":true,"data":{"fps":60}}Return the live/applied FPS reported by the active backend. If a backend does not expose a distinct live value, this falls back to the configured FPS.
curl http://<device-ip>/api/v1/fps/liveResponse 200:
{"ok":true,"data":{"fps":60}}The outgoing.enabled field controls whether encoded frames are sent over UDP.
# Enable output (starts sending, restores FPS, issues IDR)
curl "http://<device-ip>/api/v1/set?outgoing.enabled=true"
# Disable output (stops sending, reduces FPS to 5fps idle)
curl "http://<device-ip>/api/v1/set?outgoing.enabled=false"Behavior when disabled:
- FPS is reduced to 5fps (idle rate) to minimize sensor/ISP power draw.
- Encoder keeps running at the reduced rate; frames are encoded and discarded.
- The previous FPS is stored and restored when output is re-enabled.
- An IDR keyframe is issued on re-enable for immediate stream sync.
Default: false — output must be explicitly enabled. Configure outgoing.server
before enabling.
The outgoing.server field can be changed at runtime to redirect the stream.
# Redirect stream to a different GCS
curl "http://<device-ip>/api/v1/set?outgoing.server=udp://<receiver-ip>:5600"- Accepted URI schemes:
udp://HOST:PORT— standard UDP datagram outputunix://NAME— Linux abstract Unix datagram socket@NAMEshm://NAME— shared-memory RTP ring buffer
- No pipeline restart required.
- An IDR keyframe is issued after the change for stream continuity.
- If
connectedUdpis enabled, the UDP socket is re-connected to the new destination. - Live redirects support
udp://andunix://. Live switch toshm://is not supported. connectedUdpapplies only toudp://.shm://remains RTP-only. It cannot share audio; use a nonzeroaudioPortfor separate UDP audio.- On Star6E,
audioPort=0piggybacks on the active video destination for bothudp://andunix://. - On Star6E, a nonzero
audioPortkeeps audio on a dedicated UDP port. Withunix://orshm://video output, that dedicated audio port is sent to127.0.0.1:<audioPort>.
# Live: change max payload size on the fly (576..4000)
curl "http://<device-ip>/api/v1/set?outgoing.maxPayloadSize=4000"
# Restart-only
curl "http://<device-ip>/api/v1/set?outgoing.stream_mode=compact"
curl "http://<device-ip>/api/v1/set?outgoing.connected_udp=true"outgoing.stream_mode:"rtp"(default) or"compact". Determines packetization format.outgoing.max_payload_size: Maximum RTP/compact packet payload in bytes. Default1400.MUT_LIVE— applies on the next encoded frame; in-flight packetization for the current frame keeps the old size. Range[576, 4000]. Values above ~1472 require end-to-end MTU support (e.g. Realtek's 3993-byte jumbo-frame links); on a standard 1500-MTU path the kernel will IP-fragment, defeating the point. Composes with other live fields in a single multi-set request — for example?video0.bitrate=8000&outgoing.maxPayloadSize=4000applies both atomically. Live updates are accepted across all transports (udp://,unix://,shm://): the SHM ring slot is sized at startup to fit the validated ceiling so any value in range applies live without restart, just like UDP/Unix.outgoing.connected_udp: Whentrue, callsconnect()on the UDP socket so the kernel returns ICMP port-unreachable errors viasendmsg(). Useful for detecting that a receiver is down. Defaultfalse(fire-and-forget).
Setting video0.fps via the API applies hardware-level frame decimation within the
active sensor mode. The sensor continues running at its native maxFps; the MI_SYS bind
layer between VPE and VENC drops frames to match the requested rate.
# On a 90fps sensor mode: set output to 30fps (sensor stays at 90, VENC receives 30)
curl "http://<device-ip>/api/v1/set?video0.fps=30"
# Set output to 60fps
curl "http://<device-ip>/api/v1/set?video0.fps=60"
# Restore full sensor rate
curl "http://<device-ip>/api/v1/set?video0.fps=90"Clamping: If the requested FPS exceeds the current sensor mode's maxFps, the value
is silently clamped to the mode maximum. For example, requesting 120fps on a 90fps mode
sets the output to 90fps. To access a higher sensor mode, edit /etc/venc.json and
restart the process.
What happens under the hood:
- VPE→VENC bind is torn down and re-established with
src_fps:dst_fpsratio - VENC rate control
fpsNumis updated for correct bitrate allocation - No pipeline restart — latency is sub-second
Mode switching limitation: Changing sensor modes (e.g. 90fps→120fps) requires a full
process restart. The SigmaStar kernel driver does not reliably reinitialize the MIPI PHY
when switching modes in-process. Use /api/v1/restart (reloads /etc/venc.json) or
restart the venc process to change sensor modes.
Reload /etc/venc.json from disk and rebuild the pipeline. Equivalent to sending
SIGHUP. This endpoint does NOT write the in-memory config back to disk, so a manual
file swap (editor, scp, json_cli) followed by /api/v1/restart reloads exactly what
was placed on disk.
In v0.7.8 persistence moved into the /api/v1/set layer — every set (LIVE or RESTART)
now saves to disk before returning, so the WebUI "Save & Restart" flow (applyChanges
→ /api/v1/restart) ends with the on-disk copy already matching memory before the
reload runs.
curl http://<device-ip>/api/v1/restartResponse 200:
{"ok":true,"data":{"reinit":true}}Overwrite the in-memory config with compiled-in defaults, persist to /etc/venc.json,
then trigger a full pipeline reinit. Drives the "Restore Defaults" button in the WebUI.
Added in v0.7.8. The saved field in the response reflects whether persistence
actually succeeded — false means the runtime is at defaults but the on-disk copy is
stale (e.g. disk full, readonly FS, permission error); check the venc log for the
[venc_config] ERROR: line.
curl http://<device-ip>/api/v1/defaultsResponse 200:
{"ok":true,"data":{"defaults":true,"reinit":true,"saved":true}}Return live AE diagnostics from the active backend.
curl http://<device-ip>/api/v1/aeResponse 200:
{
"ok": true,
"data": {
"sensor_plane": { "ret": 0, "pad": 0, "shutter_us": 1112, "sensor_gain_x1024": 10240, "comp_gain_x1024": 1024 },
"exposure_limit": { "ret": 0, "min_shutter_us": 150, "max_shutter_us": 10000, "min_sensor_gain": 1024, "max_sensor_gain": 30000, "min_isp_gain": 1024, "max_isp_gain": 1024 },
"exposure_info": { "ret": 0, "stable": true, "reach_boundary": false, "long_us": 9999, "long_sensor_gain_x1024": 1673, "long_isp_gain_x1024": 1024, "luma_y": 236, "avg_y": 247 },
"state": { "ret": 0, "raw": 0, "name": "normal" },
"expo_mode": { "ret": 0, "raw": 0, "name": "auto" },
"metrics": { "exposure_us": 9999, "sensor_gain_x1024": 1673, "isp_gain_x1024": 1024, "fps": 90 },
"runtime": { "sensor_fps": 90, "active_precrop": { "x": 0, "y": 240, "w": 2560, "h": 1440 } }
}
}runtime.active_precrop is included on both backends whenever the
pipeline has been started; it is omitted before the first start and
after a stop.
Error 501:
{"ok":false,"error":{"code":"not_implemented","message":"AE query not available"}}Return live AWB diagnostics from the active backend.
curl http://<device-ip>/api/v1/awbError 501:
{"ok":false,"error":{"code":"not_implemented","message":"AWB query not available"}}Query all ISP IQ parameter values. Always available on Star6E backend.
curl http://<device-ip>/api/v1/iqResponse 200:
{
"ok": true,
"data": {
"lightness": {"ret": 0, "enabled": true, "op_type": "auto", "value": 50},
"contrast": {"ret": 0, "enabled": true, "op_type": "manual", "value": 70},
"color_to_gray": {"ret": 0, "value": false},
"demosaic": {"ret": 0, "enabled": true, "value": 45}
}
}Each parameter reports:
ret: MI_ISP return code (0 = success)enabled: bEnable flagop_type:"auto"or"manual"(omitted for bool-only and manual-only params)value: current primary value (backward-compat scalar)fields: (multi-field params only) object with all named sub-fields and arraysavailable:falseif the dlsym symbol was not found
Multi-field example (colortrans):
"colortrans": {
"ret": 0, "enabled": true, "value": 200,
"fields": {
"y_ofst": 200, "u_ofst": 0, "v_ofst": 0,
"matrix": [23, 45, 9, 1005, 987, 56, 56, 977, 1015]
}
}Error 501 if backend doesn't support IQ (Maruko):
{"ok":false,"error":{"code":"not_implemented","message":"IQ query not available"}}Set a single IQ parameter. The parameter is switched to manual mode (for auto/manual params) and the value is written to the primary manual field.
Supports dot-notation for multi-field params, comma-separated arrays, and
enable/disable toggling via the .enabled virtual field:
# Simple scalar
curl "http://<device-ip>/api/v1/iq/set?contrast=70"
# Dot-notation for sub-field
curl "http://<device-ip>/api/v1/iq/set?colortrans.y_ofst=200"
# Array value (comma-separated)
curl "http://<device-ip>/api/v1/iq/set?colortrans.matrix=23,45,9,1005,987,56,56,977,1015"
# Enable/disable toggle (non-bool params only)
curl "http://<device-ip>/api/v1/iq/set?colortrans.enabled=0"
curl "http://<device-ip>/api/v1/iq/set?crosstalk.enabled=1"
# Bool toggle
curl "http://<device-ip>/api/v1/iq/set?color_to_gray=1"Response 200:
{"ok":true,"data":{"param":"colortrans.y_ofst","value":200}}
{"ok":true,"data":{"param":"colortrans.matrix","value":[23,45,9,1005,987,56,56,977,1015]}}Import IQ parameters from a JSON body (output of GET /api/v1/iq).
Partial imports are supported — only parameters present in the JSON are applied.
The enabled field is respected during import — parameters with "enabled":false
will be disabled on the ISP.
# Full import from exported file
curl -X POST -H "Content-Type: application/json" \
-d @my_tuning.json http://<device-ip>/api/v1/iq/import
# Partial import — only specific params
echo '{"lightness":{"value":75},"demosaic":{"fields":{"dir_thrd":30}}}' | \
curl -X POST -H "Content-Type: application/json" -d @- http://<device-ip>/api/v1/iq/importResponse 200:
{"ok":true,"data":{"imported":true}}Serves a self-contained HTML dashboard (gzip-compressed, ~14KB). The dashboard provides Settings, API Reference, and Image Quality tabs. All modern browsers decompress the gzip response automatically.
Available parameters (62 total, Star6E):
| Parameter | Type | Range | Description |
|---|---|---|---|
lightness |
u32 | 0-100 | Lightness level |
contrast |
u32 | 0-100 | Contrast level |
brightness |
u32 | 0-100 | Brightness level |
saturation |
u8 | 0-127 | Color saturation (32=1X) |
sharpness |
u8 | 0-255 | Overshoot gain |
hsv |
u8 | 0-64 | Hue LUT first entry |
nr3d |
u8 | 0-255 | 3D NR motion threshold |
nr3d_ex |
u32 | 0-1 | 3D NR extended AR enable |
nr_despike |
u8 | 0-15 | De-spike blend ratio |
nr_luma |
u8 | 0-255 | Luma NR strength |
nr_luma_adv |
u32 | 0-1 | Advanced luma NR debug enable |
nr_chroma |
u8 | 0-127 | Chroma NR match ratio |
nr_chroma_adv |
u8 | 0-255 | Advanced chroma NR strength |
false_color |
u8 | 0-255 | False color frequency threshold |
crosstalk |
u8 | 0-31 | Cross-talk correction strength |
demosaic |
u8 | 0-63 | Demosaic direction threshold |
obc |
u16 | 0-255 | Optical black correction R value |
dynamic_dp |
u8 | 0-1 | Hot pixel detection enable |
dp_cluster |
u32 | 0-1 | Cluster dead pixel edge mode |
r2y |
u16 | 0-1023 | R2Y matrix first coefficient |
colortrans |
u16 | 0-2047 | Color transform Y offset |
rgb_matrix |
u16 | 0-8191 | CCM first coefficient |
wdr |
u8 | 0-4 | WDR box number |
wdr_curve_adv |
u16 | 0-16384 | WDR curve slope |
pfc |
u8 | 0-255 | Phase focus correction strength |
pfc_ex |
u32 | 0-1 | Extended PFC debug enable |
hdr |
u8 | 0-1 | HDR NR enable |
hdr_ex |
u16 | 0-65535 | HDR sensor exposure ratio |
shp_ex |
u32 | 0-1 | Extended sharpness debug enable |
rgbir |
u8 | 0-7 | RGBIR position type |
iq_mode |
u32 | 0-1 | IQ mode (0=day, 1=night) |
lsc |
u16 | 0-65535 | Lens shading center X |
lsc_ctrl |
u8 | 0-255 | LSC R ratio by CCT |
alsc |
u8 | 0-255 | Adaptive LSC grid X |
alsc_ctrl |
u8 | 0-255 | ALSC R ratio by CCT |
obc_p1 |
u16 | 0-255 | OBC phase 1 R value |
stitch_lpf |
u16 | 0-256 | Stitch LPF first coefficient |
rgb_gamma |
bool | 0/1 | RGB gamma enable |
yuv_gamma |
bool | 0/1 | YUV gamma enable |
wdr_curve_full |
bool | 0/1 | WDR full curve enable |
dummy |
bool | 0/1 | Dummy tuning enable |
dummy_ex |
bool | 0/1 | Extended dummy enable |
defog |
bool | 0/1 | Defogging enable |
color_to_gray |
bool | 0/1 | Grayscale mode |
nr3d_p1 |
bool | 0/1 | 3D NR phase 1 enable |
fpn |
bool | 0/1 | Fixed pattern noise enable |
Hardware test results (SSC30KQ, imx335):
- 45/46 symbols resolved (
stitch_lpfnot present) - 40/45 params roundtrip correctly (set → query reads same value)
- 3 offset mismatches:
nr_despike,pfc,hdr(set succeeds but readback differs — struct padding) - 2 ISP-rejected:
nr3d_p1,fpn(set succeeds but ISP ignores on this sensor)
Return live observability for the audio capture/encode pipeline. Useful for
diagnosing silent audio failures (missing libmi_ai.so on Maruko, missing
libopus.so, capture thread not running, codec mismatch).
curl http://<device-ip>/api/v1/audio/statusResponse 200 (Star6E with audio enabled):
{
"ok": true,
"data": {
"enabled": true,
"backend": "star6e",
"lib_loaded": true,
"device_enabled": true,
"channel_enabled": true,
"running": true,
"codec": "opus",
"sample_rate": 48000,
"channels": 1,
"opus_loaded": true
}
}Response 200 (Maruko with audio enabled):
{
"ok": true,
"data": {
"enabled": true,
"backend": "maruko",
"lib_loaded": true,
"device_opened": true,
"group_enabled": true,
"running": true,
"codec": "opus",
"sample_rate": 48000,
"channels": 1,
"opus_loaded": true
}
}Response 200 when audio.enabled=false:
{"ok":true,"data":{"enabled":false,"backend":"maruko"}}Field reference:
| Field | Meaning |
|---|---|
enabled |
audio.enabled=true and *_audio_init reached the run state |
lib_loaded |
The MI audio shared library (libmi_audio.so Star6E / libmi_ai.so Maruko) was found and dlopened |
device_enabled / device_opened |
The capture device handle is open |
channel_enabled / group_enabled |
The capture channel / group is enabled |
running |
Capture and encode threads are alive |
codec |
"g711a", "g711u", "opus", "pcm", or "unknown" |
sample_rate |
Configured audio sample rate (Hz) |
channels |
1 (mono) or 2 (stereo) |
opus_loaded |
When codec="opus", the Opus encoder was successfully initialized. false here while codec="opus" means audio falls back to raw PCM with a startup warning. |
Error 501 — backend has no audio observability hook (query_audio_status
not registered):
{"ok":false,"error":{"code":"not_implemented","message":"audio status not available on this backend"}}Return a compact Prometheus-style ISP metrics snapshot.
curl http://<device-ip>/metrics/ispResponse 200:
# HELP isp_again Analog Gain
# TYPE isp_again gauge
isp_again 1673
# HELP isp_dgain Digital Gain
# TYPE isp_dgain gauge
isp_dgain 1024
# HELP isp_fps Sensor fps
# TYPE isp_fps gauge
isp_fps 90
Start SD card recording. Optional ?dir=/path query parameter overrides the
default recording directory (from config record.dir, default /media).
# Start recording with default dir
wget -q -O- "http://<device-ip>/api/v1/record/start"
# Start with custom directory
wget -q -O- "http://<device-ip>/api/v1/record/start?dir=/media/clips"Response 200:
{"ok":true,"data":{"action":"start","dir":"/media"}}Recording format is determined by record.format config: "ts" (default, MPEG-TS
with audio) or "hevc" (raw HEVC NAL stream). File rotation is controlled by
record.maxSeconds and record.maxMB config fields.
Backend gating: only the Star6E backend currently runs the runtime poll that
honors HTTP-driven start/stop. On Maruko, recording is config-driven only
(set record.enabled=true + record.mode="mirror"|"dual" in /etc/venc.json)
and /api/v1/record/start returns:
{"ok":false,"error":{"code":"not_implemented","message":"HTTP record control not available on this backend"}}with HTTP 501. This avoids the prior behaviour where the request returned
{"ok":true} but no recording started. Tracked as Phase 6.5 in
MARUKO_PARITY_PLAN.md.
Stop SD card recording.
wget -q -O- "http://<device-ip>/api/v1/record/stop"Response 200:
{"ok":true,"data":{"action":"stop"}}Same backend gating applies — Maruko returns 501 not_implemented.
Query recording status.
wget -q -O- "http://<device-ip>/api/v1/record/status"Response 200:
{
"ok": true,
"data": {
"active": true,
"format": "ts",
"path": "/media/rec_01h23m45s_abcd.ts",
"frames": 1500,
"bytes": 12345678,
"segments": 1,
"stop_reason": "none"
}
}stop_reason values: "none" (currently recording), "manual", "disk_full",
"write_error".
List .ts and .hevc files in the configured record.dir along with
disk-usage totals.
wget -q -O- "http://<device-ip>/api/v1/recordings"Response 200:
{
"ok": true,
"data": {
"dir": "/mnt/mmcblk0p1",
"free_bytes": 1234567890,
"total_bytes": 15000000000,
"files": [
{ "name": "rec_01h00m00s_abcd.ts", "size": 12345678, "mtime": 1713600000 }
],
"truncated": false
}
}Error 503 not_available — directory not mounted.
Error 500 internal_error — cannot read directory or out of memory.
The listing is capped at 512 entries; truncated is true when the
cap was hit and older recordings were not included. free_bytes /
total_bytes are -1 when statvfs is unavailable.
Stream a single recording as an attachment download. file must be
a plain name from the listing — leading ., path separators and
control bytes are rejected.
wget "http://<device-ip>/api/v1/recordings/download?file=rec_01h00m00s_abcd.ts"Content-Type is video/mp2t for .ts files and
application/octet-stream for .hevc.
Error 400 invalid_request — missing or unsafe file parameter.
Error 404 not_found — file does not exist in record.dir.
Remove a recording from record.dir.
wget -q -O- "http://<device-ip>/api/v1/recordings/delete?file=rec_01h00m00s_abcd.ts"Response 200:
{"ok":true}Error 400 invalid_request — missing or unsafe file parameter.
Error 404 not_found — file already gone.
Error 409 record_active — file is currently being written; stop
recording first.
Error 500 delete_failed — filesystem error.
Request an IDR (keyframe) from the encoder.
curl http://<device-ip>/request/idrResponse 200:
{"ok":true,"data":{"idr":true}}If outgoing.sidecar_port is enabled at the same time, Star6E also appends
the scene-detector telemetry trailer to sidecar FRAME packets. That
is the intended external interface for per-frame size/type/complexity observations.
Query the secondary VENC channel status. Always returns 200; the active
field tells you whether dual or dual-stream mode is currently running.
wget -q -O- "http://<device-ip>/api/v1/dual/status"Response 200 — dual VENC active:
{"ok":true,"data":{"active":true,"channel":1,"bitrate":20000,"fps":120,"gop":240}}Response 200 — dual VENC not active (off, mirror, or any non-dual mode):
{"ok":true,"data":{"active":false}}Live-change secondary VENC channel parameters. Supported parameters:
| Parameter | Type | Description |
|---|---|---|
bitrate |
uint | Bitrate in kbps (applied immediately via MI_VENC, IDR issued) |
gop |
double | GOP interval in seconds (converted to frames using ch1 fps) |
# Change ch1 bitrate to 10 Mbps
wget -q -O- "http://<device-ip>/api/v1/dual/set?bitrate=10000"
# Change ch1 GOP to 1 second (120 frames at 120fps)
wget -q -O- "http://<device-ip>/api/v1/dual/set?gop=1.0"Response 200:
{"ok":true,"data":{"field":"bitrate","value":10000}}
{"ok":true,"data":{"field":"gop","value":1.00,"frames":120}}Error 400 — missing or invalid parameter:
{"ok":false,"error":{"code":"missing_param","message":"Usage: /api/v1/dual/set?bitrate=N or ?gop=N"}}Error 404 — dual VENC not active.
Error 501 — backend does not support live dual/set (Maruko). The Star6E
binding owns the low-level MI_VENC_*ChnAttr write path; a Maruko port has
not landed yet.
Request an IDR keyframe on the secondary VENC channel.
wget -q -O- "http://<device-ip>/api/v1/dual/idr"Response 200:
{"ok":true,"data":{"idr":true}}Error 404 — dual VENC not active.
Return per-channel IDR-rate-limit counters. The encoder enforces a minimum
spacing between honored IDRs to keep bitrate predictable when many sources
(scene detector, HTTP /request/idr and /api/v1/dual/idr, recorder
segment rotation) ask for keyframes simultaneously. This endpoint reports
how many requests were honored vs. coalesced (dropped) per channel.
curl http://<device-ip>/api/v1/idr/statsResponse 200:
{
"ok": true,
"data": {
"min_spacing_us": 250000,
"channels": [
{"idx": 0, "honored": 47, "dropped": 3},
{"idx": 1, "honored": 12, "dropped": 0}
]
}
}min_spacing_us is the compile-time minimum spacing in microseconds.
Channels with both counters at zero are omitted. Available on both
backends; always returns a valid response (even when no IDR has been
requested yet — channels is then an empty array).
Return live observability for the active video transport (UDP / Unix / SHM). Used by the WebUI status bar and by external link controllers that need to detect output backpressure.
curl http://<device-ip>/api/v1/transport/statusResponse 200 (SHM ring transport, common for outgoing.server=shm://...):
{
"ok": true,
"data": {
"active": true,
"transport": "shm",
"fillPct": 12,
"inPressure": false,
"transportDrops": 0,
"pressureDrops": 0,
"packetsSent": 184523,
"oversizeDrops": 0,
"slotCount": 1024,
"usedSlots": 122
}
}Response 200 (UDP/Unix kernel-buffer fill_pct):
{
"ok": true,
"data": {
"active": true,
"transport": "udp",
"fillPct": 4,
"inPressure": false,
"pressureDrops": 0
}
}Response 200 (output disabled or no socket open):
{"ok":true,"data":{"active":false,"transport":"none"}}Field reference:
| Field | Meaning |
|---|---|
transport |
"shm", "udp", "unix", or "none" |
fillPct |
Current fill ratio 0..100. For SHM, ring fill; for UDP/Unix, kernel send-buffer fill |
inPressure |
True when fillPct >= 70 (high-water threshold) |
transportDrops |
(SHM only) Lifetime ring-full drops |
pressureDrops |
Frames dropped by the in-process backpressure path while a sidecar probe was subscribed |
packetsSent |
(SHM only) Lifetime writes accepted by the ring |
oversizeDrops |
(SHM only) Frames rejected for exceeding slot capacity |
slotCount / usedSlots |
(SHM only) Ring sizing; usedSlots is a snapshot |
Error 501 — backend has no transport observability hook.
Return the table of sensor pads and resolution modes the underlying SDK
reports for the currently-loaded sensor driver. Used to populate the
WebUI sensor-mode dropdown and to validate sensor.mode writes.
curl http://<device-ip>/api/v1/modesResponse 200:
{
"ok": true,
"data": {
"selected_pad": 0,
"selected_mode": 1,
"pads": [
{
"pad": 0,
"modes": [
{"index": 0, "width": 1920, "height": 1080, "min_fps": 1, "max_fps": 60, "desc": "1080p60", "selected": false},
{"index": 1, "width": 1920, "height": 1080, "min_fps": 1, "max_fps": 90, "desc": "1080p90", "selected": true},
{"index": 2, "width": 1472, "height": 816, "min_fps": 1, "max_fps": 120, "desc": "1472x816@120", "selected": false}
]
}
]
}
}selected_pad / selected_mode reflect the currently-active pipeline
selection. The full pads[].modes[] list always shows every mode the
driver enumerates so callers can show an "available modes" UI.
Error 500 modes_failed — MI_SNR_QueryResCount failed (e.g. sensor
driver not loaded yet during a brief startup window).
In addition to the /api/v1/restart endpoint, the pipeline can be reinited by sending
SIGHUP to the venc process:
# From the device shell
killall -HUP venc
# Remotely via SSH
ssh root@<device-ip> "killall -HUP venc"Behavior:
- Tears down the full pipeline (VENC→VPE→VIF→sensor, unbinds, closes socket)
- Reloads
/etc/venc.jsonfrom disk - Rebuilds the pipeline with the new config
- The HTTP server survives reinit cycles (no port re-bind)
- Stress-tested: 10+ consecutive SIGHUPs without failure
-
Accepted config writes are persistent.
/api/v1/setpersists accepted live and restart-required field changes to the registered config path before returning./api/v1/defaultspersists compiled defaults./api/v1/restartreloads the on-disk config and does not synthesize new changes by itself. -
Codec restriction is backend-specific. Star6E RTP mode requires
video0.codec=h265, so attempting to setvideo0.codec=h264there returns a409error. Maruko accepts bothh264andh265. -
BusyBox compatibility. All endpoints use
GETmethod so they work with BusyBoxwget(which only supports GET):# On-device with BusyBox wget wget -q -O- "http://127.0.0.1/api/v1/get?video0.fps" wget -q -O- "http://127.0.0.1/api/v1/set?video0.bitrate=4096"
- Star6E is the reference behavior for API-touching features.
- Maruko may return
not_implementedfor specific apply paths until parity work is complete. GETendpoints must remain consistent across backends.
Endpoints that behave the same on both backends are omitted. Only feature
divergence is listed. As of contract_version: 0.10.1:
| Feature / Endpoint | Star6E | Maruko | Notes |
|---|---|---|---|
/api/v1/record/{start,stop} |
yes | 501 | Maruko has no runtime poll loop yet (Phase 6.5). Config-driven recording (record.enabled=true + record.mode="mirror"|"dual") works. |
/api/v1/record/status |
live counters | live counters | Both backends register a status callback against the live Star6eTsRecorderState; Maruko reflects daemon-config-driven recording (mirror/dual). |
/api/v1/recordings* |
yes | yes | File listing/download/delete works against record.dir regardless of which backend wrote the file. |
/api/v1/audio/status |
yes | yes | Both backends register query_audio_status. |
/api/v1/dual/status, /dual/idr |
yes | yes | /dual/status always 200 (active:false when off, active:true,channel,bitrate,fps,gop when on). /dual/idr returns 200 when active, 404 when not. Maruko HTTP registration landed in 0.10.4 — earlier Maruko builds returned 404 from these even when record.mode=dual was running. |
/api/v1/dual/set |
yes | 501 | Star6E-only: the underlying MI_VENC_*ChnAttr write path binds to i6_venc_chn, but Maruko's venc library expects i6c_venc_chn (different layout). Maruko returns 501 until the call path is ported. |
/api/v1/iq and /api/v1/iq/set |
full (≈45 params) | full (parity in maruko_iq.c) |
Both backends use the same IQ table schema. |
/api/v1/awb |
live | live | Both backends register query_awb_info. |
/api/v1/ae |
live + runtime.active_precrop |
live + runtime.active_precrop |
Both backends now include runtime.active_precrop in the AE response (Maruko parity landed in 0.8.4). |
/api/v1/transport/status |
yes | yes | SHM-ring fields are shown when outgoing.server=shm://; otherwise the UDP/Unix subset. |
/api/v1/idr/stats |
yes | yes | Identical schema; values reflect each backend's IDR rate-limit. |
video0.codec=h264 |
rejected with 409 | accepted | Star6E RTP mode is HEVC-only on this build. |
video0.scene_threshold / scene_holdoff |
yes | yes | Restart-required fields; both backends run the shared scene detector. |
video0.zoom_pct / zoom_x / zoom_y |
yes | yes | zoom_pct requires reinit; zoom_x/y are live pan controls. |
isp.aeMode ("native" / "throttle") |
accepted but no-op | applied | Maruko-only opt-in; switching modes mid-run requires a process restart. Default "native" on both backends. |
0.10.1:GET /api/v1/dual/statusalways returns200now. When dual VENC is not active the body is{"ok":true,"data":{"active":false}}instead of the previous404+not_activeerror envelope./dual/setand/dual/idrkeep the404+not_activesemantics — those are write endpoints that need a live ch1 to operate on.- Maruko:
/api/v1/dual/{status,idr}now actually reflect the live dual VENC state. Before this version Maruko started chn 1 whenrecord.mode = "dual"or"dual-stream"but never registered the handle with the HTTP API, so all three endpoints returned404even when dual was running. Star6E behaviour unchanged. /api/v1/dual/setreturns501on Maruko (was: silent 404). Star6E behaviour unchanged. See "Backend Support Matrix".
0.10.0:- Added digital zoom fields:
video0.zoom_pct(zoomPctalias, restart-required) plus live pan fieldsvideo0.zoom_x/video0.zoom_y(zoomX/zoomYaliases). - Added validation for zoom API writes:
zoom_pctmust be0.0or[0.25, 1.0];zoom_x/ymust be finite values in[0.0, 1.0]. - Updated WebUI-facing field metadata examples for intra refresh and zoom.
- Corrected the persistence note: accepted
/api/v1/setwrites have been persisted since v0.7.8.
- Added digital zoom fields:
0.8.4:GET /api/v1/record/statusnow reflects daemon-config-driven recording on Maruko (mirror/dual): previously the response was zero-fill (active:false, all counters 0) even when a TS file was being written. The Maruko runtime now registers a status callback against the sameStar6eTsRecorderStatethe recorder uses. No schema change.GET /api/v1/aeon Maruko now includesruntime.active_precrop, matching Star6E. The precrop was already being reported via/api/v1/config; only the AE response was missing it.- Internal (no API surface change): the
/api/v1/record/start|stop501 gate now keys off an explicitvenc_api_set_record_http_control_supported(true)opt-in instead of the status-callback presence. This decoupling is what allowed Maruko to add status visibility without accidentally re-enabling the HTTP-driven control endpoints (which it still doesn't consume).
0.8.3:- Added
GET /api/v1/audio/status— live observability for the audio capture/encode pipeline (lib loaded, capture running, codec, rate, channels, Opus encoder available). Available on both backends; returns501when the backend has no audio observability hook. GET /api/v1/record/startandGET /api/v1/record/stopnow return501 not_implementedon backends without a runtime record poll (currently only Maruko). Previously the requests appeared to succeed with{"ok":true}but did nothing. Star6E behaviour is unchanged.- Documented three pre-existing routes that had landed in code without
contract entries:
GET /api/v1/modes(sensor pad/mode introspection),GET /api/v1/transport/status(output transport observability), andGET /api/v1/idr/stats(per-channel IDR rate-limit counters). No behavioural change. - Added a Backend Support Matrix table covering Star6E vs Maruko
divergence post-Phase-5 (audio), Phase-6 (recording), Phase-7 (dual
VENC), and Phase-9 (
isp.aeMode). - In-binary
/api/v1/versionnow reportscontract_version=0.8.3(previously the constant was stuck at0.3.0while the doc moved forward to0.8.2).
- Added
0.8.2:outgoing.max_payload_sizeis nowMUT_LIVE(wasMUT_RESTART) and can be batched with other live fields in a single/api/v1/setcall, e.g.?video0.bitrate=8000&outgoing.maxPayloadSize=4000.- Validation range tightened to
[576, 4000](boot will refuse a config outside that range). - SHM ring slot is sized at startup to fit the validated ceiling
(4000 + 12 RTP header = 4012 bytes per slot, 8-byte aligned), so
shm://accepts the full live range with no restart-to-grow caveat, matchingudp://andunix://behavior. Costs ~1.3 MiB extra SHM per ring.
0.6.3:- Added
GET /api/v1/recordings— list files with size/mtime plusfree_bytes/total_bytesfor the configuredrecord.dir. - Added
GET /api/v1/recordings/download?file=<name>— stream a recording as an attachment download. - Added
GET /api/v1/recordings/delete?file=<name>— delete a file; refuses the currently-active recording with409 record_active. - New error code
record_active(409) for actions blocked while recording. - Browser UI for the above endpoints lives in the
Recordingstab on the dashboard at/; there is no separate HTML route.
- Added
0.6.2:- Added
isp.keepAspect(boolean, defaulttrue) to config schema. Whenfalse, VIF captures the full sensor area and VPE scales without aspect-ratio cropping (image is stretched if sensor and encode AR differ).MUT_RESTART— applied on SIGHUP / Save & Restart. Star6E only; Maruko reads but ignores the field until SCL crop port lands as a follow-up. - Added
isp.keepAspectcamelCase alias (isp.keep_aspect). GET /api/v1/configresponse gains aruntimeblock withactive_precrop({x,y,w,h}) — the VIF crop currently programmed (includes any sensor overscan offsets). Omitted when the pipeline has not started or after stop. Available on both backends.GET /api/v1/aeStar6E response includesruntime.active_precropwith the same rectangle.
- Added
0.5.0:- Added
GET /api/v1/iq— query all ISP IQ parameter values (46 params). - Added
GET /api/v1/iq/set?param=value— set individual IQ parameters live. - Always enabled on Star6E (no config toggle needed — zero runtime overhead).
- Params cover image quality, noise reduction, corrections, dynamic range, lens calibration, LUT enables, and ISP mode controls.
- Star6E: 45/46 symbols resolved, Maruko returns 501.
- Added
0.4.0:- Added
GET /api/v1/dual/status— query secondary VENC channel state. - Added
GET /api/v1/dual/set?bitrate=N— live ch1 bitrate change. - Added
GET /api/v1/dual/set?gop=N— live ch1 GOP change (in seconds). - Added
GET /api/v1/dual/idr— request IDR on secondary channel. - All dual endpoints return 404 when dual VENC is not active.
- Config
recordsection expanded:mode("off"/"mirror"/"dual"/"dual-stream"),bitrate,fps,gopSizefor ch1 config,serverfor dual-stream.
- Added
0.3.0:- Added
GET /api/v1/record/start— start SD card recording (optional?dir=). - Added
GET /api/v1/record/stop— stop SD card recording. - Added
GET /api/v1/record/status— query recording status (active, format, bytes, segments, stop_reason). - Config
recordsection expanded:format("hevc" or "ts"),maxSeconds,maxMB. - MPEG-TS muxer: HEVC video + PCM audio in power-loss safe container.
- File rotation at IDR boundaries by time (default 300s) or size (default 500MB).
- RTP streaming and recording operate concurrently.
- Added
0.2.3:- Added
GET /api/v1/aefor live AE diagnostics. - Added
GET /api/v1/awbfor live AWB diagnostics. - Added
GET /metrics/ispfor compact ISP metrics export. - Added Majestic-compatible
GET /api/v1/config.jsonalias. - Added
GET /api/v1/fps/configfor configured FPS queries. - Added
GET /api/v1/fps/livefor live/applied FPS queries. - Added support for selected Majestic-style camelCase field aliases on
GET /api/v1/getandGET /api/v1/set.
- Added
0.2.1:outgoing.max_payload_sizenow applies to RTP mode (was only used by compact mode). Default 850. Set to 0 to disable adaptive sizing.
0.2.0:- Added
outgoing.enabled(MUT_LIVE): enable/disable UDP output with FPS idle. - Added
outgoing.serverchanged from MUT_RESTART to MUT_LIVE: live destination redirect. - Added
outgoing.streamMode(MUT_RESTART): explicit stream mode selection. - Added
outgoing.connectedUdp(MUT_RESTART): connected UDP error reporting. - IDR keyframe issued on output enable, destination change, and bitrate change.
- Server URIs now accept
udp://,unix://, andshm://.
- Added
0.1.3:- Documented live FPS control behavior (hardware bind decimation, clamping, mode switching limitation).
video0.fpsset via API now uses MI_SYS_BindChnPort2 rebind instead of /proc write.- Removed
isp.exposureconfig field, capability, and Prometheus metric. Auto-cap to frame period (1/fps) is now the only exposure mode. - Changed
video0.sizedefault from"1920x1080"to"auto"(use sensor native resolution). Added"auto"preset to size parser. - Removed
"4MP"size preset (sensor-specific, not a standard resolution).
0.1.2:- Updated to reflect actual implemented API (was draft, now active).
- All endpoints use GET method (BusyBox wget compatibility).
- Documented query parameter format:
?field_namefor get,?field_name=valuefor set. - Added
/api/v1/restartendpoint (replaces plannedPOST /api/v1/actions/restart). - Added
/request/idrendpoint. - Removed unimplemented
PUT /api/v1/configandPATCH /api/v1/config(future work). - Added curl examples for all endpoints.
- Added SIGHUP reinit documentation.
- Added safety notes (in-memory only, codec restriction).
0.1.1:- Updated examples to use
video.capture_resolutionrestart semantics.
- Updated examples to use
0.1.0:- Initial draft contract and endpoint definitions.