@mega-yfue/eufy-sdk 0.6.0-beta.35 → 0.6.0-beta.37

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.
@@ -173,10 +173,10 @@ export declare class RecordingDownloadError extends Error {
173
173
  *
174
174
  * `video` is Annex-B H.264 or H.265 in decode order, `audio` AAC-LC 16 kHz mono in ADTS framing (absent
175
175
  * when the recording carried none that could be decoded). `codec` is the codec of `video`, read off the parameter
176
- * sets of its first keyframe that carries them, and is absent when no keyframe does. `frames` counts the distinct
177
- * video sequences emitted. `missingFrames` counts gaps between received sequences and received sequences that yielded no
178
- * usable picture, including at either end. `durationMs` spans the first to the last emitted picture by the
179
- * camera's own timestamps; `fps` is derived from those stamps.
176
+ * sets of its first keyframe that carries them, and is absent when no keyframe does. `frames` counts the pictures
177
+ * emitted, numbered by the camera's frame numbers counted on across a change of frame size. `missingFrames` counts
178
+ * gaps in that numbering and numbered frames that yielded no usable picture, including at either end. `durationMs`
179
+ * spans the first to the last emitted picture by the camera's own timestamps; `fps` is derived from those stamps.
180
180
  */
181
181
  export interface RecordingDownload {
182
182
  video: Buffer;
package/dist/index.js CHANGED
@@ -20199,18 +20199,28 @@ function decodeRecording(frames, station, eccPrivateKeyHex) {
20199
20199
  const receivedNumbers = /* @__PURE__ */ new Set();
20200
20200
  let firstStamp;
20201
20201
  let lastStamp;
20202
+ let frameSize;
20203
+ let shift = 0;
20204
+ let sequence = 0;
20202
20205
  for (const { commandId, raw } of frames) {
20203
20206
  if (commandId === CommandType.CMD_VIDEO_FRAME) {
20204
20207
  const header = parseVideoFrameHeader(raw);
20205
20208
  if (!header)
20206
20209
  continue;
20207
- receivedNumbers.add(header.sequence);
20210
+ const size = header.width && header.height ? `${header.width}x${header.height}` : void 0;
20211
+ if (header.keyframe && size && frameSize !== void 0 && size !== frameSize) {
20212
+ shift = sequence + 1 - header.sequence & 65535;
20213
+ }
20214
+ if (size)
20215
+ frameSize = size;
20216
+ sequence = header.sequence + shift & 65535;
20217
+ receivedNumbers.add(sequence);
20208
20218
  const units = assembler.push(raw, (payload) => decoder && header.keyframe ? decoder.decodeFrame(payload)?.h264 : clearVideoPayload(payload, header));
20209
20219
  for (const unit of units) {
20210
20220
  video.push(unit.data);
20211
20221
  if (unit.keyframe)
20212
20222
  codec ??= sniffAnnexbCodec(unit.data);
20213
- numbers.add(header.sequence);
20223
+ numbers.add(sequence);
20214
20224
  const stamp = raw.readUIntLE(14, 6);
20215
20225
  firstStamp ??= stamp;
20216
20226
  lastStamp = stamp;
@@ -25545,13 +25555,14 @@ var RtcSession = class extends EventEmitter11 {
25545
25555
  closeAnnounced = false;
25546
25556
  sdpHandled = false;
25547
25557
  chain = Promise.resolve();
25558
+ stageReached = "signing";
25559
+ lastPeerState;
25548
25560
  constructor(opts) {
25549
25561
  super();
25550
25562
  this.opts = opts;
25551
25563
  this.logger = opts.logger ?? noopLogger;
25552
25564
  this.signaling = (opts.createSignaling ?? ((o) => new RtcSignalingClient(o)))(opts);
25553
25565
  this.peer = (opts.createPeer ?? ((o) => new RtcPeer(o)))({ logger: this.logger, ...opts.peer });
25554
- const mediaTrack = opts.peer?.mediaTrack === true;
25555
25566
  this.signaling.on("message", (inner) => {
25556
25567
  this.chain = this.chain.then(() => this.onSignaling(inner)).catch((e) => {
25557
25568
  this.emit("error", e instanceof Error ? e : new Error(String(e)));
@@ -25565,6 +25576,7 @@ var RtcSession = class extends EventEmitter11 {
25565
25576
  if (this.connected)
25566
25577
  return;
25567
25578
  this.connected = true;
25579
+ this.stageReached = "connected";
25568
25580
  this.logger.debug(`[rtc] ${this.opts.stationSn} command channel open`);
25569
25581
  this.emit("connected");
25570
25582
  });
@@ -25576,9 +25588,14 @@ var RtcSession = class extends EventEmitter11 {
25576
25588
  this.announceClose();
25577
25589
  });
25578
25590
  this.peer.on("connectionState", (state) => {
25579
- if ((state === "failed" || state === "closed") && (this.connected || mediaTrack)) {
25591
+ this.lastPeerState = state;
25592
+ if (state !== "failed" && state !== "closed")
25593
+ return;
25594
+ if (this.connected || this.mediaTrack) {
25580
25595
  this.connected = false;
25581
25596
  this.announceClose();
25597
+ } else {
25598
+ this.shutdown(new Error(`RTC ${this.opts.stationSn} peer ${state} before the command channel opened`));
25582
25599
  }
25583
25600
  });
25584
25601
  this.peer.on("error", (e) => this.emit("error", e));
@@ -25591,9 +25608,20 @@ var RtcSession = class extends EventEmitter11 {
25591
25608
  this.emit("commandData", frame, linkType);
25592
25609
  });
25593
25610
  }
25611
+ get mediaTrack() {
25612
+ return this.opts.peer?.mediaTrack === true;
25613
+ }
25594
25614
  get isConnected() {
25595
25615
  return this.connected;
25596
25616
  }
25617
+ /** The last bring-up stage this session reached. */
25618
+ get stage() {
25619
+ return this.stageReached;
25620
+ }
25621
+ /** The peer connection's last reported state; `undefined` before it reports one. */
25622
+ get peerState() {
25623
+ return this.lastPeerState;
25624
+ }
25597
25625
  /**
25598
25626
  * Start the sequence; resolves once the call is sent. `connected` fires when the channel opens. A session
25599
25627
  * closed by the time the sign fetch or the auth completes rejects instead of opening the socket or
@@ -25603,11 +25631,14 @@ var RtcSession = class extends EventEmitter11 {
25603
25631
  await this.signaling.fetchSign();
25604
25632
  if (this.closed)
25605
25633
  throw new Error("RTC session closed before the call was placed");
25634
+ this.stageReached = "connecting";
25606
25635
  await this.signaling.connect();
25636
+ this.stageReached = "authenticating";
25607
25637
  await this.waitForAuth();
25608
25638
  if (this.closed)
25609
25639
  throw new Error("RTC session closed before the call was placed");
25610
25640
  this.logger.debug(`[rtc] ${this.opts.stationSn} authenticated \u2014 ${this.opts.signalingMode ?? "scall"}`);
25641
+ this.stageReached = "calling";
25611
25642
  this.signaling.sendCall();
25612
25643
  }
25613
25644
  /** Send one portal packet; false when the command channel isn't open. */
@@ -25703,6 +25734,9 @@ var RtcSession = class extends EventEmitter11 {
25703
25734
  return;
25704
25735
  case "hangup":
25705
25736
  this.logger.debug(`[rtc] ${this.opts.stationSn} hub hung up`);
25737
+ if (!this.connected && this.opts.signalingMode !== "call") {
25738
+ this.shutdown(new Error(`RTC ${this.opts.stationSn} hub hung up before the command channel opened`));
25739
+ }
25706
25740
  return;
25707
25741
  default:
25708
25742
  return;
@@ -25715,6 +25749,7 @@ var RtcSession = class extends EventEmitter11 {
25715
25749
  const status = payload.status;
25716
25750
  if (status === 100 && payload.turn) {
25717
25751
  this.turn = payload.turn;
25752
+ this.stageReached = "relay-granted";
25718
25753
  try {
25719
25754
  await this.peer.init(payload.turn);
25720
25755
  } catch (e) {
@@ -25770,6 +25805,7 @@ var RtcSession = class extends EventEmitter11 {
25770
25805
  }
25771
25806
  const answer = await this.peer.handleRemoteOffer(offer);
25772
25807
  this.signaling.sendInfoSdp(this.opts.signalingMode === "call" ? answer : JSON.stringify(sdpToScallJson(answer)));
25808
+ this.stageReached = "answered";
25773
25809
  this.logger.debug(`[rtc] ${this.opts.stationSn} answered the hub's offer`);
25774
25810
  }
25775
25811
  };
@@ -26248,7 +26284,8 @@ var RtcCommandRouter = class {
26248
26284
  /**
26249
26285
  * One bounded bring-up: the session is up when its command channel opens, and it fails, with the
26250
26286
  * session closed and every listener gone, when `connect()` throws, the session closes first (rejecting
26251
- * with the failure that closed it, when it carries one), or {@link CONNECT_TIMEOUT_MS} passes. A single
26287
+ * with the failure that closed it, when it carries one), or {@link CONNECT_TIMEOUT_MS} passes, naming the
26288
+ * stage the session reached and its peer's last state. A single
26252
26289
  * promise, so an early failure cannot leave a second one rejecting unheard.
26253
26290
  */
26254
26291
  bringUp(sn, session) {
@@ -26274,7 +26311,7 @@ var RtcCommandRouter = class {
26274
26311
  };
26275
26312
  const onConnected = () => finish();
26276
26313
  const onClose = (err) => finish(err ?? new Error(`rtc: ${sn} session closed before the command channel opened`));
26277
- const timer = setTimeout(() => finish(new Error(`rtc: ${sn} did not come up within ${CONNECT_TIMEOUT_MS2}ms`)), CONNECT_TIMEOUT_MS2);
26314
+ const timer = setTimeout(() => finish(new Error(`rtc: ${sn} did not come up within ${CONNECT_TIMEOUT_MS2}ms (stage ${session.stage}, peer ${session.peerState ?? "not started"})`)), CONNECT_TIMEOUT_MS2);
26278
26315
  session.once("connected", onConnected);
26279
26316
  session.once("close", onClose);
26280
26317
  Promise.resolve().then(() => session.connect()).catch((e) => finish(e instanceof Error ? e : new Error(String(e))));