@mega-yfue/eufy-sdk 0.5.0-beta.5 → 0.5.0-beta.7
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/index.js
CHANGED
|
@@ -15699,8 +15699,8 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
15699
15699
|
return this.connected;
|
|
15700
15700
|
}
|
|
15701
15701
|
/**
|
|
15702
|
-
* Ask the OS for {@link RECEIVE_BUFFER_BYTES} on a bound socket, and
|
|
15703
|
-
* less or refuses.
|
|
15702
|
+
* Ask the OS for {@link RECEIVE_BUFFER_BYTES} on a bound socket, and log once per session when it grants
|
|
15703
|
+
* less (debug) or refuses (warn).
|
|
15704
15704
|
*
|
|
15705
15705
|
* The request is made here rather than through `createSocket`'s `recvBufferSize`: Node applies that option
|
|
15706
15706
|
* inside the bind callback, where a refusal is thrown out of reach of this session and ends the process.
|
|
@@ -15716,7 +15716,8 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
15716
15716
|
if (granted >= RECEIVE_BUFFER_BYTES || this.receiveBufferReported)
|
|
15717
15717
|
return;
|
|
15718
15718
|
this.receiveBufferReported = true;
|
|
15719
|
-
|
|
15719
|
+
const [level, outcome] = granted > 0 ? ["debug", ` (granted ${granted})`] : ["warn", " (request refused)"];
|
|
15720
|
+
this.logger[level](`[p2p] ${this.cfg.stationSn} UDP receive buffer below the ${RECEIVE_BUFFER_BYTES} bytes requested` + outcome + `; live video can lose keyframes. Raise the OS limit (net.core.rmem_max on Linux, kern.ipc.maxsockbuf on BSD) to at least ${RECEIVE_BUFFER_BYTES}.`);
|
|
15720
15721
|
}
|
|
15721
15722
|
/** Open the socket and start the lookup → hole-punch handshake. */
|
|
15722
15723
|
async connect() {
|
|
@@ -16809,11 +16810,13 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16809
16810
|
if (advance > SEQUENCE_LOOKBACK) {
|
|
16810
16811
|
if (65536 - advance <= STALE_RETRANSMIT_DEPTH)
|
|
16811
16812
|
return;
|
|
16812
|
-
if (this.pendingByDataType.has(dataType) && this.tracedDatagramGaps++ < MAX_TRACED_DATAGRAM_GAPS)
|
|
16813
|
+
if ((dataType === P2PDataType.VIDEO || this.pendingByDataType.has(dataType)) && this.tracedDatagramGaps++ < MAX_TRACED_DATAGRAM_GAPS)
|
|
16813
16814
|
this.trace({ phase: "sequence-restart", dataType });
|
|
16814
16815
|
this.clearReorderTimer(dataType);
|
|
16815
16816
|
this.reorderByType.delete(dataType);
|
|
16816
16817
|
this.pendingByDataType.delete(dataType);
|
|
16818
|
+
if (dataType === P2PDataType.VIDEO)
|
|
16819
|
+
this.emit("videoGap");
|
|
16817
16820
|
this.lastSeqByType.set(dataType, seqNo);
|
|
16818
16821
|
this.reassemble(dataType, msg.subarray(8));
|
|
16819
16822
|
return;
|
|
@@ -16879,9 +16882,11 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16879
16882
|
*/
|
|
16880
16883
|
abandonHole(dataType) {
|
|
16881
16884
|
const held = this.reorderByType.get(dataType).held;
|
|
16882
|
-
if (this.pendingByDataType.has(dataType) && this.tracedDatagramGaps++ < MAX_TRACED_DATAGRAM_GAPS)
|
|
16885
|
+
if ((dataType === P2PDataType.VIDEO || this.pendingByDataType.has(dataType)) && this.tracedDatagramGaps++ < MAX_TRACED_DATAGRAM_GAPS)
|
|
16883
16886
|
this.trace({ phase: "datagram-gap", dataType });
|
|
16884
16887
|
this.pendingByDataType.delete(dataType);
|
|
16888
|
+
if (dataType === P2PDataType.VIDEO)
|
|
16889
|
+
this.emit("videoGap");
|
|
16885
16890
|
const last = this.lastSeqByType.get(dataType);
|
|
16886
16891
|
const earliest = [...held.keys()].sort((a, b) => (a - last & 65535) - (b - last & 65535))[0];
|
|
16887
16892
|
this.lastSeqByType.set(dataType, earliest - 1 & 65535);
|
|
@@ -17518,7 +17523,8 @@ var AccessUnitAssembler = class {
|
|
|
17518
17523
|
* `decode` extracts the payload of a frame, whatever the stream's encryption: it is called for EVERY
|
|
17519
17524
|
* frame, including a continuation, because a continuation carries its own wrapped key ahead of its share
|
|
17520
17525
|
* of the payload (measured: 129 bytes beyond what its header declares, exactly as a unit's first frame
|
|
17521
|
-
* carries).
|
|
17526
|
+
* carries). A continuation without its matching open unit has no Annex-B start code and cannot be emitted
|
|
17527
|
+
* as a complete unit, even if its repeated header says keyframe.
|
|
17522
17528
|
*/
|
|
17523
17529
|
push(payload, decode) {
|
|
17524
17530
|
const header = parseVideoFrameHeader(payload);
|
|
@@ -17538,6 +17544,8 @@ var AccessUnitAssembler = class {
|
|
|
17538
17544
|
return [unitOf(open2.header, Buffer.concat(open2.chunks))];
|
|
17539
17545
|
}
|
|
17540
17546
|
this.discard();
|
|
17547
|
+
if (!beginsAccessUnit(body))
|
|
17548
|
+
return [];
|
|
17541
17549
|
if (full) {
|
|
17542
17550
|
this.open = { header, chunks: [body], carried: body.length };
|
|
17543
17551
|
return [];
|
|
@@ -17891,6 +17899,12 @@ var LiveStream = class extends EventEmitter3 {
|
|
|
17891
17899
|
lastCodec = "h264";
|
|
17892
17900
|
/** Rebuilds an access unit the station split across several frames — see {@link AccessUnitAssembler}. */
|
|
17893
17901
|
units = new AccessUnitAssembler((drop) => this.reportDroppedUnit(drop));
|
|
17902
|
+
/** Whether a missing video datagram or incomplete unit invalidated the current reference chain. */
|
|
17903
|
+
awaitingKeyframeAfterGap = false;
|
|
17904
|
+
videoGapHandler = () => {
|
|
17905
|
+
this.units.discard();
|
|
17906
|
+
this.awaitingKeyframeAfterGap = true;
|
|
17907
|
+
};
|
|
17894
17908
|
/** The channel inbound media must be tagged with, once {@link acceptsMedia} trusts the station's tag. */
|
|
17895
17909
|
mediaChannel;
|
|
17896
17910
|
tracedFirstVideoCommand = false;
|
|
@@ -17933,6 +17947,7 @@ var LiveStream = class extends EventEmitter3 {
|
|
|
17933
17947
|
return this;
|
|
17934
17948
|
this.listening = true;
|
|
17935
17949
|
this.session.on("data", this.handler);
|
|
17950
|
+
this.session.on("videoGap", this.videoGapHandler);
|
|
17936
17951
|
this.session.on("liveStartUnacknowledged", this.unackedHandler);
|
|
17937
17952
|
this.sendStart();
|
|
17938
17953
|
const keepAliveMs = this.opts.keepAliveMs ?? DEFAULT_KEEPALIVE_MS;
|
|
@@ -18030,6 +18045,8 @@ var LiveStream = class extends EventEmitter3 {
|
|
|
18030
18045
|
return;
|
|
18031
18046
|
this.listening = false;
|
|
18032
18047
|
this.session.off("data", this.handler);
|
|
18048
|
+
this.session.off("videoGap", this.videoGapHandler);
|
|
18049
|
+
this.awaitingKeyframeAfterGap = false;
|
|
18033
18050
|
this.session.off("liveStartUnacknowledged", this.unackedHandler);
|
|
18034
18051
|
if (this.kaTimer)
|
|
18035
18052
|
clearInterval(this.kaTimer);
|
|
@@ -18065,6 +18082,11 @@ var LiveStream = class extends EventEmitter3 {
|
|
|
18065
18082
|
try {
|
|
18066
18083
|
if (f.commandId === CMD_VIDEO_FRAME2) {
|
|
18067
18084
|
for (const unit of this.units.push(f.data, (payload) => this.annexbOf(payload, f.signCode))) {
|
|
18085
|
+
if (this.awaitingKeyframeAfterGap) {
|
|
18086
|
+
if (!unit.keyframe)
|
|
18087
|
+
continue;
|
|
18088
|
+
this.awaitingKeyframeAfterGap = false;
|
|
18089
|
+
}
|
|
18068
18090
|
if (!this.tracedFirstVideoUnit) {
|
|
18069
18091
|
this.tracedFirstVideoUnit = true;
|
|
18070
18092
|
this.trace({ phase: "first-video-unit", keyframe: unit.keyframe });
|
|
@@ -18159,6 +18181,7 @@ var LiveStream = class extends EventEmitter3 {
|
|
|
18159
18181
|
* otherwise silent in both directions: no frame reaches a consumer, and nothing states why.
|
|
18160
18182
|
*/
|
|
18161
18183
|
reportDroppedUnit(drop) {
|
|
18184
|
+
this.awaitingKeyframeAfterGap = true;
|
|
18162
18185
|
const message = `[live] dropped an incomplete access unit (${drop.carried} bytes in ${drop.chunks} frame(s), tail never arrived, ${drop.count} so far)`;
|
|
18163
18186
|
if (drop.count === 1)
|
|
18164
18187
|
this.logger.warn(message);
|