@apocaliss92/scrypted-reolink-native 0.5.14 → 0.5.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agent-memory/baichuan-reolink-engineer/MEMORY.md +4 -0
- package/.claude/agent-memory/baichuan-reolink-engineer/baichuan_talk_sequence.md +62 -0
- package/.claude/agent-memory/baichuan-reolink-engineer/camstack_probe_gated_cap_registration.md +38 -0
- package/dist/main.nodejs.js +1 -1
- package/dist/plugin.zip +0 -0
- package/package.json +3 -3
- package/src/camera.ts +48 -6
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# Memory Index
|
|
2
|
+
|
|
3
|
+
- [camstack Reolink probe-gated cap registration](camstack_probe_gated_cap_registration.md) — probe-gated native caps (PTZ/autotrack/doorbell/intercom) MUST be re-registered retroactively in `probeAndPersistFeatures`; constructor pass runs before the feature-probe slice is populated.
|
|
4
|
+
- [Baichuan talk sequence + ADPCM framing](baichuan_talk_sequence.md) — canonical two-way-audio command order, block framing math, and fine-tuning knobs (shared by scrypted-reolink-native + camstack).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: baichuan-talk-sequence
|
|
3
|
+
description: Canonical Baichuan two-way-audio (talk) command order, IMA ADPCM block framing math, and the three intercom fine-tuning settings.
|
|
4
|
+
metadata:
|
|
5
|
+
type: reference
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Baichuan talk (two-way audio) — canonical sequence & framing
|
|
9
|
+
|
|
10
|
+
Source of truth: `reolink-baichuan-js` (`@apocaliss92/nodelink-js`).
|
|
11
|
+
- `src/reolink/baichuan/utils/talkConfig.ts` — build session info + TalkConfig.
|
|
12
|
+
- `src/reolink/baichuan/utils/talkSession.ts` — buffered send pump + stop.
|
|
13
|
+
- `src/reolink/baichuan/utils/talk.ts` — TalkConfig XML + `encodeBcMediaAdpcmBlock`.
|
|
14
|
+
- `ReolinkBaichuanApi.createDedicatedTalkSession` (recommended; own pooled socket).
|
|
15
|
+
|
|
16
|
+
## Command order
|
|
17
|
+
1. `getTalkAbility(channel)` — cmd `BC_CMD_ID_TALK_ABILITY`. Returns
|
|
18
|
+
`duplexList`, `audioStreamModeList`, `audioConfigList`.
|
|
19
|
+
2. Pick `audioConfig`: prefer entry with `audioType === 'adpcm'`, else first.
|
|
20
|
+
3. `sendTalkConfigWithReset` — cmd `BC_CMD_ID_TALK_CONFIG` (201), class
|
|
21
|
+
`BC_CLASS_MODERN_24`. On responseCode **422**, send `BC_CMD_ID_TALK_RESET`
|
|
22
|
+
(11) then retry config once; must end 200.
|
|
23
|
+
4. Stream audio: cmd `BC_CMD_ID_TALK` (binary payload, no reply) per payload
|
|
24
|
+
of 1..N ADPCM blocks. Paced by `expectedStreamEndMs` (playLengthMs =
|
|
25
|
+
samples/sampleRate).
|
|
26
|
+
5. Stop: flush remaining (pad last block with 0xff to `fullBlockSize`),
|
|
27
|
+
wait out the remaining play time, then `BC_CMD_ID_TALK_RESET` (must be 200).
|
|
28
|
+
|
|
29
|
+
UDP/battery: `channelIdOverride = channel` (0-based header channelId).
|
|
30
|
+
|
|
31
|
+
## Block framing (the load-bearing math)
|
|
32
|
+
- `blockSize = floor(audioConfig.lengthPerEncoder / 2)` (a.k.a. halfBlockSize)
|
|
33
|
+
- `fullBlockSize = blockSize + 4` (4-byte IMA block header: int16 predictor +
|
|
34
|
+
uint8 index + uint8 reserved)
|
|
35
|
+
- `samplesPerBlock = blockSize * 2 + 1` (the +1 is the header predictor sample)
|
|
36
|
+
- PCM bytes needed per block = `samplesPerBlock * 2` (s16le)
|
|
37
|
+
- Wire wrapper per block (`encodeBcMediaAdpcmBlock`): magic `0x62773130` ("bw10")
|
|
38
|
+
+ payload_size(u16)×2 + `0x0100`(u16) + halfBlockSize(u16) + block, padded to
|
|
39
|
+
8-byte multiple.
|
|
40
|
+
|
|
41
|
+
Validate at session open: `fullBlockSize === blockSize + 4` and
|
|
42
|
+
`sampleRate > 0` (Reolink typical 16000 Hz; block ≈ 64 ms).
|
|
43
|
+
|
|
44
|
+
## Input pipeline
|
|
45
|
+
Incoming WebRTC/mic audio is Opus → decode to **PCM s16le @ camera sampleRate,
|
|
46
|
+
mono** BEFORE IMA ADPCM encoding. `sampleRate` comes from
|
|
47
|
+
`session.info.audioConfig.sampleRate` (read AFTER start, not assumed).
|
|
48
|
+
Critical: if the decoder defaults to f32le, the talk session reads it as
|
|
49
|
+
Int16Array → pure noise. Pin s16le explicitly.
|
|
50
|
+
|
|
51
|
+
## Fine-tuning settings (Scrypted `intercom-mixin.ts` ⇄ camstack schema parity)
|
|
52
|
+
Same names, same ranges in both projects:
|
|
53
|
+
- `intercomBlocksPerPayload` — int [1,8], default 1. Lower = lower latency, more
|
|
54
|
+
packets/sec. Clamped by lib to [1,8].
|
|
55
|
+
- `intercomMaxBacklogMs` — int [20,5000], default 120. PCM backlog cap; drops
|
|
56
|
+
OLDEST samples (aligned to 16-bit) when exceeded → "backlog clamped" warning.
|
|
57
|
+
`maxBacklogBytes = floor(ms/1000 * sampleRate * 2)`, floored at one block.
|
|
58
|
+
- `intercomGain` — number [0.1,10], default 1.0. Output gain applied to s16le
|
|
59
|
+
PCM (hard-clipped) BEFORE encode (post-encode would corrupt ADPCM predictor).
|
|
60
|
+
In Scrypted it's an ffmpeg `-filter:a volume=N`; in camstack a JS Int16 multiply.
|
|
61
|
+
|
|
62
|
+
Related: [[camstack-probe-gated-cap-registration]].
|
package/.claude/agent-memory/baichuan-reolink-engineer/camstack_probe_gated_cap_registration.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: camstack-probe-gated-cap-registration
|
|
3
|
+
description: In camstack's addon-provider-reolink, probe-gated native caps must be re-registered retroactively after the feature probe; constructor registration runs too early.
|
|
4
|
+
metadata:
|
|
5
|
+
type: project
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Reolink probe-gated native-cap registration (camstack)
|
|
9
|
+
|
|
10
|
+
In `packages/addon-provider-reolink/src/reolink-camera.ts`, the constructor calls
|
|
11
|
+
`registerNativeCapabilities()` **synchronously**, but the `feature-probe`
|
|
12
|
+
runtime-state slice (`hasPtz` / `hasIntercom` / `hasDoorbell` / `hasAutotrack`)
|
|
13
|
+
is populated **later**, in the kernel-driven `onProbe()` lifecycle hook →
|
|
14
|
+
`probeAndPersistFeatures()`.
|
|
15
|
+
|
|
16
|
+
**Consequence:** any `registerXIfSupported()` helper that gates on
|
|
17
|
+
`getProbeFlags<ReolinkProbeFlags>().hasX === true` is a NO-OP during the
|
|
18
|
+
constructor pass on a fresh boot (empty slice). It MUST be re-invoked
|
|
19
|
+
retroactively at the end of `probeAndPersistFeatures()`, alongside its siblings:
|
|
20
|
+
`registerPtzIfSupported()`, `registerPtzAutotrackIfSupported()`,
|
|
21
|
+
`registerDoorbellIfSupported()`, `registerIntercomIfSupported()`.
|
|
22
|
+
|
|
23
|
+
Each helper is idempotent (guards on its own `private xRegistered` flag), so a
|
|
24
|
+
warm-rehydrate boot that registered in the constructor just no-ops on the
|
|
25
|
+
retroactive call.
|
|
26
|
+
|
|
27
|
+
**Why this matters:** if a probe-gated cap is never registered, the cap router
|
|
28
|
+
finds no provider for the device and the call fails at the **routing layer**
|
|
29
|
+
before any provider code runs — producing ZERO provider-side logs. This is how
|
|
30
|
+
the "Reolink intercom closes immediately" bug hid: the diagnostic was the
|
|
31
|
+
ABSENCE of the `intercom cap registered (...)` log line for Reolink devices,
|
|
32
|
+
while Hikvision logged it. Compare provider registration log lines across
|
|
33
|
+
vendors when a cap silently does nothing.
|
|
34
|
+
|
|
35
|
+
**Detection tip:** `grep -c "intercom cap registered"` per provider in the log.
|
|
36
|
+
Hikvision logged it 87×; Reolink logged it 0× → cap never registered.
|
|
37
|
+
|
|
38
|
+
Related: [[baichuan-talk-sequence]].
|