@mega-yfue/eufy-sdk 0.5.0-beta.82 → 0.5.0-beta.83
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/dist/core/contracts.d.ts +3 -4
- package/dist/index.js +90 -86
- package/dist/index.js.map +2 -2
- package/dist/transport/p2p/command-router.d.ts +1 -1
- package/dist/transport/p2p/media.d.ts +3 -5
- package/dist/transport/p2p/p2p-session.d.ts +21 -0
- package/dist/transport/p2p/readable-egress.d.ts +1 -2
- package/dist/transport/p2p/shared-live-source.d.ts +15 -42
- package/package.json +1 -1
|
@@ -262,7 +262,7 @@ export declare class P2PCommandRouter {
|
|
|
262
262
|
* A {@link MediaProvider} bound to one serial — resolves the session then calls `p2p/media`.
|
|
263
263
|
*
|
|
264
264
|
* Every live egress here is a consumer of the SAME shared pull, so N `live()` calls collapse to one
|
|
265
|
-
* PPCS session and a live snapshot against a warm
|
|
265
|
+
* PPCS session and a live snapshot against a warm shared source costs no extra pull at all; a
|
|
266
266
|
* cold source warms one and waits for a clean keyframe. Each shared egress passes its complete options
|
|
267
267
|
* through because any of them may create the source, whose power and retention hints are fixed for
|
|
268
268
|
* everyone who joins later. The bounded {@link MediaProvider.record} clip is the exception: it opens its
|
|
@@ -24,11 +24,9 @@ import type { SharedLiveSource } from "./shared-live-source.js";
|
|
|
24
24
|
export declare function openLiveStream(session: P2PSession, opts?: LiveStreamOptions): Promise<LiveStream>;
|
|
25
25
|
/**
|
|
26
26
|
* **Live snapshot off a SHARED source** (V6) — snapshot as just another consumer of the shared live
|
|
27
|
-
* pull.
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* keyframe (the first IDR after a cold start is frequently partial, so skip it by default). Requires
|
|
31
|
-
* `ffmpeg` for the Annex-B → JPEG decode.
|
|
27
|
+
* pull. A warm source reuses its upstream session; collection starts from live keyframes rather than
|
|
28
|
+
* a retained picture. A live source captures its next keyframe by default; a cold source skips the
|
|
29
|
+
* first keyframe because its first IDR is frequently partial. Requires `ffmpeg` for the Annex-B → JPEG decode.
|
|
32
30
|
*
|
|
33
31
|
* The returned dimensions are the ENCODED IMAGE's own, read back out of it — never the frame header's.
|
|
34
32
|
* The header states the stream's geometry when the capture started, and a stream that reconfigures
|
|
@@ -185,6 +185,10 @@ export declare class P2PSession extends EventEmitter {
|
|
|
185
185
|
* change (the level-2 key arrived after an initial level-1 start), otherwise sends the 1139 nudge.
|
|
186
186
|
*/
|
|
187
187
|
private readonly liveStartedChannels;
|
|
188
|
+
/** The `cipher_id` this connection's `CMD_GATEWAYINFO` reply named, once its negotiation has read it. */
|
|
189
|
+
private gatewayCipherId?;
|
|
190
|
+
/** The `n` of the last `[0x01, n]` header this connection sent in the clear — see {@link nextClearCommandBytes}. */
|
|
191
|
+
private clearCommandCount;
|
|
188
192
|
private readonly unackedLiveStarts;
|
|
189
193
|
private liveStartRetransmitTimer?;
|
|
190
194
|
private readonly logger;
|
|
@@ -396,6 +400,9 @@ export declare class P2PSession extends EventEmitter {
|
|
|
396
400
|
* `video_type` is 12, and `transaction` is the millisecond timestamp as a string. The device answers a
|
|
397
401
|
* start carrying these values with a keyframe; `encryptkey` is the modulus it RSA-wraps each keyframe's
|
|
398
402
|
* AES media key with (unwrapped by {@link decodeVideoFrame}).
|
|
403
|
+
*
|
|
404
|
+
* With `clearMsgId`, the body of a start sent in the clear: `{accountId, encryptkey, msg_id, streamtype: 1}`
|
|
405
|
+
* only, `msg_id` being `clearMsgId`, which changes from one start to the next.
|
|
399
406
|
*/
|
|
400
407
|
private startLiveJson;
|
|
401
408
|
/**
|
|
@@ -403,8 +410,20 @@ export declare class P2PSession extends EventEmitter {
|
|
|
403
410
|
* (GCM, signCode 8, frame type 10) when the session has a negotiated key, else level-1 (AES-128-ECB,
|
|
404
411
|
* signCode 1, frame type 11) — chosen by the session key, not the device family. The camera RSA-wraps
|
|
405
412
|
* each keyframe's AES media key with the `encryptkey` modulus (unwrapped by {@link decodeVideoFrame}).
|
|
413
|
+
* A session that {@link sendsInTheClear} sends it unencrypted instead: signCode 0, frame type 0, header
|
|
414
|
+
* bytes `[0x01, n]` from {@link nextClearCommandBytes}, and `n` as the body's `msg_id`.
|
|
406
415
|
*/
|
|
407
416
|
private sendStartLiveOwnSession;
|
|
417
|
+
/**
|
|
418
|
+
* Whether this session's own-session start and stop go in the clear: its `CMD_GATEWAYINFO` reply named
|
|
419
|
+
* `cipher_id` 0.
|
|
420
|
+
*/
|
|
421
|
+
private sendsInTheClear;
|
|
422
|
+
/**
|
|
423
|
+
* Header bytes 10-11 of this connection's next command sent in the clear: `[0x01, n]`, with `n` counting
|
|
424
|
+
* those commands from 1 and never 0. A retransmit repeats its `n`, and the device's reply echoes it.
|
|
425
|
+
*/
|
|
426
|
+
private nextClearCommandBytes;
|
|
408
427
|
/**
|
|
409
428
|
* Repeat unacknowledged own-session live starts until the device takes one. Runs only while a start is
|
|
410
429
|
* outstanding.
|
|
@@ -475,6 +494,8 @@ export declare class P2PSession extends EventEmitter {
|
|
|
475
494
|
* `uint32`, the same 4-byte body as the attached talkback frames. The app sends this frame on closing
|
|
476
495
|
* an own-session camera's live view; the camera stops streaming on it within a second, and keeps
|
|
477
496
|
* streaming through the 1350-wrapped form.
|
|
497
|
+
* - own-session on a session that {@link sendsInTheClear}: the same 4-byte body unencrypted, signCode 0,
|
|
498
|
+
* header bytes from {@link nextClearCommandBytes}.
|
|
478
499
|
* - without a level-2 key: the bare 1004 command.
|
|
479
500
|
*/
|
|
480
501
|
stopLiveMedia(channel?: number, accountId?: string, homeBaseAttached?: boolean): void;
|
|
@@ -25,7 +25,6 @@ export interface ReadableEgressOptions {
|
|
|
25
25
|
/**
|
|
26
26
|
* Wrap a shared-source {@link Consumer} in a fresh Readable. The consumer is detached when the
|
|
27
27
|
* Readable is destroyed/ended, so callers own the lifetime by owning the stream.
|
|
28
|
-
* Frames arrive through onMedia
|
|
29
|
-
* because ConsumerImpl replays it when the first video listener is added.
|
|
28
|
+
* Frames arrive through onMedia, preserving their source-arrival metadata.
|
|
30
29
|
*/
|
|
31
30
|
export declare function openReadableFromConsumer(consumer: Consumer, opts?: ReadableEgressOptions): Readable;
|
|
@@ -96,7 +96,6 @@ export interface SharedLiveSourceOptions {
|
|
|
96
96
|
* back directly), plus the listener removal and arrival-timed feed the recording and readable egresses use.
|
|
97
97
|
*/
|
|
98
98
|
export interface Consumer extends LiveStreamConsumer {
|
|
99
|
-
/** What this consumer holds the pull for. */
|
|
100
99
|
/** Detach a previously registered listener (mirrors {@link LiveStreamHandle.on}). */
|
|
101
100
|
off(event: "video", listener: (frame: LiveVideoFrame) => void): this;
|
|
102
101
|
off(event: "audio", listener: (frame: LiveAudioFrame) => void): this;
|
|
@@ -104,18 +103,14 @@ export interface Consumer extends LiveStreamConsumer {
|
|
|
104
103
|
off(event: "error", listener: (err: Error) => void): this;
|
|
105
104
|
/** Subscribe to frames carrying the source-captured arrival time used by the prebuffer. */
|
|
106
105
|
onMedia(listener: (item: TimedMediaFrame) => void): this;
|
|
107
|
-
/** True once the source has replayed a cached keyframe to this consumer (no GOP wait on join). */
|
|
108
|
-
readonly primed: boolean;
|
|
109
106
|
/** Leave the source (refcount--). Idempotent. `stop()` is an alias (LiveStreamHandle). */
|
|
110
107
|
detach(): void;
|
|
111
108
|
}
|
|
112
109
|
/**
|
|
113
110
|
* One media frame retained with its transport-arrival time for prebuffer continuity.
|
|
114
111
|
*
|
|
115
|
-
* A video frame carries the coded configuration in force when it
|
|
116
|
-
*
|
|
117
|
-
* drain hands over, and both have to announce the configuration their media was coded under rather than
|
|
118
|
-
* whichever one is current by the time they are delivered.
|
|
112
|
+
* A video frame carries the coded configuration in force when it arrived, so a prebuffer drain
|
|
113
|
+
* announces the configuration its retained media was coded under.
|
|
119
114
|
*/
|
|
120
115
|
export type TimedMediaFrame = {
|
|
121
116
|
kind: "video";
|
|
@@ -145,8 +140,8 @@ export declare class SharedLiveSource {
|
|
|
145
140
|
* {@link LiveStreamStartError}.
|
|
146
141
|
*/
|
|
147
142
|
private delivered;
|
|
148
|
-
/**
|
|
149
|
-
private
|
|
143
|
+
/** Whether the upstream has delivered a keyframe since its last teardown. */
|
|
144
|
+
private hasKeyframe;
|
|
150
145
|
/** Last parameter sets the stream announced — see {@link parameterSets}. */
|
|
151
146
|
private lastParamSets?;
|
|
152
147
|
/**
|
|
@@ -164,14 +159,7 @@ export declare class SharedLiveSource {
|
|
|
164
159
|
private warmRetryTimer?;
|
|
165
160
|
/** Whether this warm-up has already asked its owner to replace the session. */
|
|
166
161
|
private sessionReplacementAsked;
|
|
167
|
-
/**
|
|
168
|
-
* Whether the pending watch is a REUSE watch, which any frame settles.
|
|
169
|
-
*
|
|
170
|
-
* A cold warm-up needs a keyframe: nothing can be decoded without one. A join already holds the retained
|
|
171
|
-
* keyframe, so what its watch is missing is evidence the stream is still being served — and a delta frame is
|
|
172
|
-
* that evidence. Requiring a keyframe there let the deadline outlive an actively delivering stream whose
|
|
173
|
-
* group of pictures is longer than the window, and the timeout fails EVERY consumer.
|
|
174
|
-
*/
|
|
162
|
+
/** A reuse watch settles on upstream media independently of each consumer's keyframe gate. */
|
|
175
163
|
private reuseWatch;
|
|
176
164
|
/** This source's opaque handle for tracing — see {@link SharedLiveSource.trace}. */
|
|
177
165
|
private readonly traceId;
|
|
@@ -179,8 +167,8 @@ export declare class SharedLiveSource {
|
|
|
179
167
|
* How many re-issues this watch has spent with nothing arriving since it was armed.
|
|
180
168
|
*
|
|
181
169
|
* What a stream delivered BEFORE the current watch is no evidence about now — a reused stream's upstream may
|
|
182
|
-
* have served plenty and since been dropped by the station
|
|
183
|
-
*
|
|
170
|
+
* have served plenty and since been dropped by the station. Only a frame arriving after the watch was
|
|
171
|
+
* armed proves current delivery, and {@link delivered}
|
|
184
172
|
* is reset to track exactly that.
|
|
185
173
|
*
|
|
186
174
|
* The first re-issue is therefore a keepalive: a reuse cannot yet know which case it is in, and a keepalive
|
|
@@ -220,13 +208,13 @@ export declare class SharedLiveSource {
|
|
|
220
208
|
get parameterSets(): ParamSets | undefined;
|
|
221
209
|
/**
|
|
222
210
|
* Attach a new consumer. Warms the stream on the first attach (or cancels a pending linger teardown
|
|
223
|
-
* and reuses the warm stream)
|
|
211
|
+
* and reuses the warm stream). Delivery starts at the next live keyframe so every dependent
|
|
212
|
+
* picture follows its reference pictures without a gap from a cached keyframe.
|
|
224
213
|
*/
|
|
225
214
|
attach(): Consumer;
|
|
226
215
|
/**
|
|
227
216
|
* Attach at the same instant a keyframe-aligned prebuffer snapshot is taken. The returned consumer
|
|
228
|
-
*
|
|
229
|
-
* duplicates the newest IDR nor leaves a gap at the handoff.
|
|
217
|
+
* continues after `buffered` without waiting for another keyframe or duplicating the newest IDR.
|
|
230
218
|
*/
|
|
231
219
|
attachWithPrebuffer(seconds: number): {
|
|
232
220
|
consumer: Consumer;
|
|
@@ -298,24 +286,9 @@ export declare class SharedLiveSource {
|
|
|
298
286
|
*/
|
|
299
287
|
private reissueStart;
|
|
300
288
|
/**
|
|
301
|
-
* Watch
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
* it says the stream WAS being served, not that it still is. A caller commits to media on that frame — a
|
|
305
|
-
* process, a negotiated session — so a stream the station has quietly stopped serving strands it with no
|
|
306
|
-
* deadline, because warming is what arms one and a reuse skips warming by definition.
|
|
307
|
-
*
|
|
308
|
-
* The watch is the warm-up's own, deadline and retry alike, and the first frame to arrive AFTER the join
|
|
309
|
-
* clears it, that being the only frame which says the stream is still being served. A stream still serving
|
|
310
|
-
* clears it long before the deadline fires; one that is not fails its consumers exactly as a cold start that
|
|
311
|
-
* never delivered would.
|
|
312
|
-
*
|
|
313
|
-
* The start is re-issued at once and then on the retry's cadence, because a station that stopped serving a
|
|
314
|
-
* channel when its last consumer left is the very case the retry recovers: waiting the whole window to
|
|
315
|
-
* report what one re-issued start can fix is a timeout where a stream was available.
|
|
316
|
-
*
|
|
317
|
-
* Nothing is armed while a watch is already pending, so several consumers joining one reused stream share the
|
|
318
|
-
* watch the first of them started.
|
|
289
|
+
* Watch an existing upstream with the warm-up retry and deadline. Any video or audio received after
|
|
290
|
+
* the watch begins confirms delivery and settles it. Each consumer's keyframe gate remains separate.
|
|
291
|
+
* Concurrent joins share a pending watch; the first join reissues the start immediately.
|
|
319
292
|
*/
|
|
320
293
|
private watchReusedStream;
|
|
321
294
|
/** Arm the battery budget timer (battery/solar sources) — replaces any pending budget/grace. */
|
|
@@ -328,7 +301,7 @@ export declare class SharedLiveSource {
|
|
|
328
301
|
private onBudgetExpire;
|
|
329
302
|
/** Re-push the battery budget (host called `extend()` from the notice), cancelling the auto-stop. */
|
|
330
303
|
private extendBudget;
|
|
331
|
-
/** Settle a reuse watch on any frame
|
|
304
|
+
/** Settle a reuse watch on any upstream frame without changing consumer keyframe gates. */
|
|
332
305
|
private settleReuseWatch;
|
|
333
306
|
/** Stop the warm-up retry + deadline (the stream is confirmed live). */
|
|
334
307
|
private clearWarmWatch;
|
|
@@ -410,7 +383,7 @@ export declare class SharedLiveSource {
|
|
|
410
383
|
/** Refcount hit zero — arm the linger teardown. A new attach in the window cancels it. */
|
|
411
384
|
private arm;
|
|
412
385
|
/**
|
|
413
|
-
* Stop + drop the underlying stream and clear the
|
|
386
|
+
* Stop + drop the underlying stream and clear the keyframe/ring caches. Rebuildable via attach().
|
|
414
387
|
*
|
|
415
388
|
* The stream reference is dropped BEFORE stopping it, because `stop()` emits `"stop"` synchronously and
|
|
416
389
|
* this source listens for that — so stopping re-enters `teardown` through {@link onUpstreamEnd}. Clearing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mega-yfue/eufy-sdk",
|
|
3
|
-
"version": "0.5.0-beta.
|
|
3
|
+
"version": "0.5.0-beta.83",
|
|
4
4
|
"description": "One typed TypeScript client for the Anker eufy v6 cloud — capability-driven devices, realtime events over P2P/MQTT/push, and live media",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "mega-yfue",
|