@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.
@@ -680,10 +680,9 @@ export interface LiveStreamConsumer extends LiveStreamHandle {
680
680
  * consumer receives, so a consumer holding media it has not been told the configuration of is not a state
681
681
  * it can reach.
682
682
  *
683
- * Per consumer, against what THIS consumer was last given: a consumer that joins mid-session is primed
684
- * with a cached keyframe it did not witness arriving, and one that crosses its bound resynchronises onto
685
- * a later IDR having skipped the frame the source saw the change on. Announcing what the source saw
686
- * would leave both holding an encoder built for media they never received.
683
+ * Per consumer, against what this consumer was last given: a mid-session join begins at its next live
684
+ * keyframe, and a consumer crossing its queue bound resynchronises onto a later IDR. Each receives
685
+ * the configuration of the media it is delivered, even when another consumer has already seen it.
687
686
  *
688
687
  * Only a consumer announces this, never a bare {@link LiveStreamHandle}: it is read from the parameter
689
688
  * sets a shared source watches every frame for, which the raw pull underneath it does not do. That is
package/dist/index.js CHANGED
@@ -16227,6 +16227,10 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
16227
16227
  * change (the level-2 key arrived after an initial level-1 start), otherwise sends the 1139 nudge.
16228
16228
  */
16229
16229
  liveStartedChannels = /* @__PURE__ */ new Map();
16230
+ /** The `cipher_id` this connection's `CMD_GATEWAYINFO` reply named, once its negotiation has read it. */
16231
+ gatewayCipherId;
16232
+ /** The `n` of the last `[0x01, n]` header this connection sent in the clear — see {@link nextClearCommandBytes}. */
16233
+ clearCommandCount = 0;
16230
16234
  unackedLiveStarts = /* @__PURE__ */ new Map();
16231
16235
  liveStartRetransmitTimer;
16232
16236
  logger;
@@ -16424,6 +16428,7 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
16424
16428
  this.level2Negotiating = true;
16425
16429
  const generation = this.connectionGeneration;
16426
16430
  const cipherId = gatewayInfoCipherId(gwPayload);
16431
+ this.gatewayCipherId = cipherId;
16427
16432
  this.trace({ phase: "level2-negotiating", cipherId });
16428
16433
  void (async () => {
16429
16434
  try {
@@ -16530,6 +16535,8 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
16530
16535
  this.level2Pending = this.cfg.resolveCipherKey !== void 0;
16531
16536
  this.level2Negotiating = false;
16532
16537
  this.level2Reprompted = false;
16538
+ this.gatewayCipherId = void 0;
16539
+ this.clearCommandCount = 0;
16533
16540
  }
16534
16541
  const socket = dgram.createSocket("udp4");
16535
16542
  this.socket = socket;
@@ -16820,7 +16827,7 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
16820
16827
  this.sendMediaPayloadLevel2(CMD_START_REALTIME_MEDIA, channel, accountId, {});
16821
16828
  return;
16822
16829
  }
16823
- const want = this.level2Key ? "l2" : "l1";
16830
+ const want = this.level2Key ? "l2" : this.sendsInTheClear() ? "clear" : "l1";
16824
16831
  if (opts?.force) {
16825
16832
  for (const pending of this.unackedLiveStarts.values())
16826
16833
  if (pending.channel === channel)
@@ -16850,8 +16857,17 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
16850
16857
  * `video_type` is 12, and `transaction` is the millisecond timestamp as a string. The device answers a
16851
16858
  * start carrying these values with a keyframe; `encryptkey` is the modulus it RSA-wraps each keyframe's
16852
16859
  * AES media key with (unwrapped by {@link decodeVideoFrame}).
16860
+ *
16861
+ * With `clearMsgId`, the body of a start sent in the clear: `{accountId, encryptkey, msg_id, streamtype: 1}`
16862
+ * only, `msg_id` being `clearMsgId`, which changes from one start to the next.
16853
16863
  */
16854
- startLiveJson(channel, accountId) {
16864
+ startLiveJson(channel, accountId, clearMsgId) {
16865
+ if (clearMsgId !== void 0) {
16866
+ return Buffer.from(JSON.stringify({
16867
+ commandType: CMD_START_LIVE,
16868
+ data: { accountId, encryptkey: this.rsaModulus(), msg_id: clearMsgId, streamtype: 1 }
16869
+ }), "utf-8");
16870
+ }
16855
16871
  const now2 = Date.now();
16856
16872
  return Buffer.from(JSON.stringify({
16857
16873
  commandType: CMD_START_LIVE,
@@ -16882,25 +16898,31 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
16882
16898
  * (GCM, signCode 8, frame type 10) when the session has a negotiated key, else level-1 (AES-128-ECB,
16883
16899
  * signCode 1, frame type 11) — chosen by the session key, not the device family. The camera RSA-wraps
16884
16900
  * each keyframe's AES media key with the `encryptkey` modulus (unwrapped by {@link decodeVideoFrame}).
16901
+ * A session that {@link sendsInTheClear} sends it unencrypted instead: signCode 0, frame type 0, header
16902
+ * bytes `[0x01, n]` from {@link nextClearCommandBytes}, and `n` as the body's `msg_id`.
16885
16903
  */
16886
16904
  sendStartLiveOwnSession(channel, accountId) {
16887
16905
  if (!this.connectAddress)
16888
16906
  return;
16889
- const plain = this.startLiveJson(channel, accountId);
16890
16907
  let payload;
16891
16908
  let signCode;
16892
16909
  let magic;
16893
16910
  let frameType;
16894
16911
  if (this.level2Key) {
16895
- const enc2 = this.encryptLevel2(plain);
16912
+ const enc2 = this.encryptLevel2(this.startLiveJson(channel, accountId));
16896
16913
  if (!enc2)
16897
16914
  return;
16898
16915
  payload = enc2;
16899
16916
  signCode = 8;
16900
16917
  magic = [8, 0];
16901
16918
  frameType = START_LIVE_FRAME_TYPE_L2;
16919
+ } else if (this.sendsInTheClear()) {
16920
+ magic = this.nextClearCommandBytes();
16921
+ payload = this.startLiveJson(channel, accountId, magic[1]);
16922
+ signCode = 0;
16923
+ frameType = 0;
16902
16924
  } else {
16903
- payload = encryptP2PData(paddingP2PData(plain), this.level1Key);
16925
+ payload = encryptP2PData(paddingP2PData(this.startLiveJson(channel, accountId)), this.level1Key);
16904
16926
  signCode = 1;
16905
16927
  magic = [1, 0];
16906
16928
  frameType = START_LIVE_FRAME_TYPE_L1;
@@ -16920,6 +16942,21 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
16920
16942
  this.armLiveStartRetransmit();
16921
16943
  this.send(this.connectAddress, RequestMessageType.DATA, data);
16922
16944
  }
16945
+ /**
16946
+ * Whether this session's own-session start and stop go in the clear: its `CMD_GATEWAYINFO` reply named
16947
+ * `cipher_id` 0.
16948
+ */
16949
+ sendsInTheClear() {
16950
+ return this.gatewayCipherId === 0;
16951
+ }
16952
+ /**
16953
+ * Header bytes 10-11 of this connection's next command sent in the clear: `[0x01, n]`, with `n` counting
16954
+ * those commands from 1 and never 0. A retransmit repeats its `n`, and the device's reply echoes it.
16955
+ */
16956
+ nextClearCommandBytes() {
16957
+ this.clearCommandCount = this.clearCommandCount % 255 + 1;
16958
+ return [1, this.clearCommandCount];
16959
+ }
16923
16960
  /**
16924
16961
  * Repeat unacknowledged own-session live starts until the device takes one. Runs only while a start is
16925
16962
  * outstanding.
@@ -17082,6 +17119,8 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
17082
17119
  * `uint32`, the same 4-byte body as the attached talkback frames. The app sends this frame on closing
17083
17120
  * an own-session camera's live view; the camera stops streaming on it within a second, and keeps
17084
17121
  * streaming through the 1350-wrapped form.
17122
+ * - own-session on a session that {@link sendsInTheClear}: the same 4-byte body unencrypted, signCode 0,
17123
+ * header bytes from {@link nextClearCommandBytes}.
17085
17124
  * - without a level-2 key: the bare 1004 command.
17086
17125
  */
17087
17126
  stopLiveMedia(channel = STATION_CHANNEL3, accountId = "", homeBaseAttached = false) {
@@ -17094,12 +17133,19 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
17094
17133
  clearInterval(this.liveStartRetransmitTimer);
17095
17134
  this.liveStartRetransmitTimer = void 0;
17096
17135
  }
17136
+ const body = Buffer.allocUnsafe(4);
17137
+ body.writeUInt32LE(channel >>> 0, 0);
17097
17138
  if (this.level2Key && !homeBaseAttached) {
17098
- const body = Buffer.allocUnsafe(4);
17099
- body.writeUInt32LE(channel >>> 0, 0);
17100
17139
  this.sendRawLevel2Bytes(body, channel, CMD_STOP_REALTIME_MEDIA, 8);
17101
17140
  } else if (this.level2Key) {
17102
17141
  this.sendMediaPayloadLevel2(CMD_STOP_REALTIME_MEDIA, channel, accountId, {});
17142
+ } else if (!homeBaseAttached && this.sendsInTheClear() && this.connectAddress) {
17143
+ const data = Buffer.concat([
17144
+ buildCommandHeader(this.seqNumber, CMD_STOP_REALTIME_MEDIA),
17145
+ buildRawCommandPayload(body, channel, 0, this.nextClearCommandBytes(), 0)
17146
+ ]);
17147
+ this.seqNumber = this.seqNumber + 1 & 65535;
17148
+ this.send(this.connectAddress, RequestMessageType.DATA, data);
17103
17149
  } else {
17104
17150
  this.sendCommand(CMD_STOP_REALTIME_MEDIA, channel);
17105
17151
  }
@@ -19128,10 +19174,9 @@ async function openLiveStream(session, opts = {}) {
19128
19174
  async function captureSnapshotFromShared(source, opts = {}) {
19129
19175
  const timeoutMs = opts.timeoutMs ?? 2e4;
19130
19176
  const collectMs = opts.collectMs ?? 1500;
19131
- const skip = opts.skipKeyframes ?? 1;
19132
19177
  opts.signal?.throwIfAborted();
19133
19178
  const consumer = source.attach();
19134
- const primed = consumer.primed;
19179
+ const skip = opts.skipKeyframes ?? (source.state === "live" ? 0 : 1);
19135
19180
  let burst;
19136
19181
  try {
19137
19182
  burst = await new Promise((resolve, reject) => {
@@ -19148,8 +19193,7 @@ async function captureSnapshotFromShared(source, opts = {}) {
19148
19193
  if (fr.keyframe)
19149
19194
  keyCount++;
19150
19195
  if (!capturing) {
19151
- const threshold = primed ? 0 : skip;
19152
- if (!fr.keyframe || keyCount <= threshold)
19196
+ if (!fr.keyframe || keyCount <= skip)
19153
19197
  return;
19154
19198
  capturing = true;
19155
19199
  codec = fr.codec;
@@ -19159,7 +19203,7 @@ async function captureSnapshotFromShared(source, opts = {}) {
19159
19203
  settle = setTimeout(() => {
19160
19204
  cleanup();
19161
19205
  resolve({ h264: Buffer.concat(bufs), codec, sets });
19162
- }, primed ? 0 : collectMs);
19206
+ }, collectMs);
19163
19207
  };
19164
19208
  const onError = (err) => {
19165
19209
  cleanup();
@@ -19549,30 +19593,24 @@ var ConsumerImpl = class extends EventEmitter4 {
19549
19593
  queue = [];
19550
19594
  paused = false;
19551
19595
  detached = false;
19552
- primed = false;
19553
19596
  awaitingKeyframe = false;
19554
19597
  /** The last coded configuration announced to THIS consumer — see {@link flush}. */
19555
19598
  config;
19556
- /** Cached keyframe to replay, held until a "video" listener actually subscribes (see below). */
19557
- pendingPrime;
19558
- constructor(onDetach, maxQueue) {
19599
+ /** Re-arm an ordinary join when its first video-bearing subscription starts, including after an async gap. */
19600
+ constructor(onDetach, maxQueue, waitForKeyframe) {
19559
19601
  super();
19560
19602
  this.onDetach = onDetach;
19561
19603
  this.maxQueue = maxQueue;
19562
- this.on("newListener", (event) => {
19563
- if (event !== "video" || this.listenerCount("video") !== 0)
19564
- return;
19565
- const kf = this.pendingPrime;
19566
- if (!kf)
19567
- return;
19568
- this.pendingPrime = void 0;
19569
- queueMicrotask(() => this.deliverVideo(kf));
19570
- });
19571
- }
19572
- /** Stage a keyframe to replay when the first video listener subscribes. */
19573
- prime(item) {
19574
- this.primed = true;
19575
- this.pendingPrime = item;
19604
+ if (waitForKeyframe) {
19605
+ this.on("newListener", (event) => {
19606
+ if (event !== "video" && event !== "media")
19607
+ return;
19608
+ if (this.listenerCount("video") > 0 || this.listenerCount("media") > 0)
19609
+ return;
19610
+ this.awaitingKeyframe = true;
19611
+ this.config = void 0;
19612
+ });
19613
+ }
19576
19614
  }
19577
19615
  /** Attached-by-construction — `start()` is a no-op so a Consumer satisfies LiveStreamHandle. */
19578
19616
  start() {
@@ -19623,14 +19661,7 @@ var ConsumerImpl = class extends EventEmitter4 {
19623
19661
  this.flush(q[i]);
19624
19662
  }
19625
19663
  }
19626
- /**
19627
- * Source → consumer video, honouring resync-to-keyframe and the bounded queue.
19628
- *
19629
- * Takes the whole item rather than a frame, because a keyframe-prime replays a RETAINED one and has to
19630
- * arrive by the same route: a primed keyframe is exactly the IDR a resynchronising consumer is waiting
19631
- * for, so it must clear that wait as a live keyframe does, and it carries the configuration its own media
19632
- * was coded under. While that wait is unsatisfied a delta frame is dropped — nothing can begin at it.
19633
- */
19664
+ /** Source → consumer video, dropping dependent pictures until a subscribed consumer receives a keyframe. */
19634
19665
  deliverVideo(item) {
19635
19666
  if (this.detached)
19636
19667
  return;
@@ -19675,9 +19706,8 @@ var ConsumerImpl = class extends EventEmitter4 {
19675
19706
  * about ahead of the frame that carries it.
19676
19707
  *
19677
19708
  * The single delivery point is where the announcement belongs, because every way media reaches a
19678
- * consumer passes through here: a live frame, a replayed keyframe-prime, and a backlog drained by
19679
- * {@link resume}. Comparing against what this consumer was last given rather than what the source last
19680
- * saw is what makes a primed join and a post-overflow resynchronisation correct.
19709
+ * consumer passes through here, including a backlog drained by {@link resume}. The comparison is
19710
+ * against what this consumer was last given rather than what the source last saw.
19681
19711
  */
19682
19712
  flush(item) {
19683
19713
  if (item.kind === "video") {
@@ -19731,8 +19761,8 @@ var SharedLiveSource = class {
19731
19761
  * {@link LiveStreamStartError}.
19732
19762
  */
19733
19763
  delivered = { keyframe: false, video: false, audio: false };
19734
- /** Last keyframe access unit seen — replayed to a joining consumer (keyframe-prime). */
19735
- lastKeyframe;
19764
+ /** Whether the upstream has delivered a keyframe since its last teardown. */
19765
+ hasKeyframe = false;
19736
19766
  /** Last parameter sets the stream announced — see {@link parameterSets}. */
19737
19767
  lastParamSets;
19738
19768
  /**
@@ -19750,14 +19780,7 @@ var SharedLiveSource = class {
19750
19780
  warmRetryTimer;
19751
19781
  /** Whether this warm-up has already asked its owner to replace the session. */
19752
19782
  sessionReplacementAsked = false;
19753
- /**
19754
- * Whether the pending watch is a REUSE watch, which any frame settles.
19755
- *
19756
- * A cold warm-up needs a keyframe: nothing can be decoded without one. A join already holds the retained
19757
- * keyframe, so what its watch is missing is evidence the stream is still being served — and a delta frame is
19758
- * that evidence. Requiring a keyframe there let the deadline outlive an actively delivering stream whose
19759
- * group of pictures is longer than the window, and the timeout fails EVERY consumer.
19760
- */
19783
+ /** A reuse watch settles on upstream media independently of each consumer's keyframe gate. */
19761
19784
  reuseWatch = false;
19762
19785
  /** This source's opaque handle for tracing — see {@link SharedLiveSource.trace}. */
19763
19786
  traceId;
@@ -19765,8 +19788,8 @@ var SharedLiveSource = class {
19765
19788
  * How many re-issues this watch has spent with nothing arriving since it was armed.
19766
19789
  *
19767
19790
  * What a stream delivered BEFORE the current watch is no evidence about now — a reused stream's upstream may
19768
- * have served plenty and since been dropped by the station, and the retained keyframe replayed to a joining
19769
- * consumer says nothing either. Only a frame arriving after the watch was armed does, and {@link delivered}
19791
+ * have served plenty and since been dropped by the station. Only a frame arriving after the watch was
19792
+ * armed proves current delivery, and {@link delivered}
19770
19793
  * is reset to track exactly that.
19771
19794
  *
19772
19795
  * The first re-issue is therefore a keepalive: a reuse cannot yet know which case it is in, and a keepalive
@@ -19825,40 +19848,38 @@ var SharedLiveSource = class {
19825
19848
  }
19826
19849
  /**
19827
19850
  * Attach a new consumer. Warms the stream on the first attach (or cancels a pending linger teardown
19828
- * and reuses the warm stream), then replays the cached keyframe so the consumer can decode at once.
19851
+ * and reuses the warm stream). Delivery starts at the next live keyframe so every dependent
19852
+ * picture follows its reference pictures without a gap from a cached keyframe.
19829
19853
  */
19830
19854
  attach() {
19831
19855
  return this.attachConsumer(true);
19832
19856
  }
19833
19857
  /**
19834
19858
  * Attach at the same instant a keyframe-aligned prebuffer snapshot is taken. The returned consumer
19835
- * is not separately keyframe-primed, so replaying `buffered` followed by its live events neither
19836
- * duplicates the newest IDR nor leaves a gap at the handoff.
19859
+ * continues after `buffered` without waiting for another keyframe or duplicating the newest IDR.
19837
19860
  */
19838
19861
  attachWithPrebuffer(seconds) {
19839
19862
  const consumer = this.attachConsumer(false);
19840
19863
  return { consumer, buffered: this.bufferedMedia(seconds) };
19841
19864
  }
19842
- attachConsumer(prime) {
19865
+ attachConsumer(waitForKeyframe) {
19843
19866
  if (this.disposed)
19844
19867
  throw new Error("SharedLiveSource is disposed");
19845
19868
  const wasEmpty = this.consumers.size === 0;
19846
- const consumer = new ConsumerImpl((c) => this.onDetach(c), this.maxQueue);
19869
+ const consumer = new ConsumerImpl((c) => this.onDetach(c), this.maxQueue, waitForKeyframe);
19847
19870
  this.consumers.add(consumer);
19848
19871
  if (wasEmpty)
19849
19872
  this.opts.onActive?.();
19850
19873
  if (this.lingerTimer.pending) {
19851
19874
  this.lingerTimer.cancel();
19852
19875
  if (this._state === "lingering")
19853
- this._state = this.lastKeyframe ? "live" : "warming";
19876
+ this._state = this.hasKeyframe ? "live" : "warming";
19854
19877
  this.logger.debug(`${this.tag} re-attach in linger window \u2014 reusing warm stream (consumers=${this.consumers.size})`);
19855
19878
  }
19856
19879
  if (!this.stream)
19857
19880
  this.warm();
19858
19881
  else
19859
19882
  this.watchReusedStream();
19860
- if (prime && this.lastKeyframe)
19861
- consumer.prime(this.lastKeyframe);
19862
19883
  return consumer;
19863
19884
  }
19864
19885
  /**
@@ -19990,24 +20011,9 @@ var SharedLiveSource = class {
19990
20011
  current.nudge(!arrivedSinceWatch && this.fruitlessReissues > 1);
19991
20012
  }
19992
20013
  /**
19993
- * Watch a stream this consumer joined rather than warmed, so a dead one cannot pass for a live one.
19994
- *
19995
- * A reused stream hands a joining consumer the retained keyframe at once, which is evidence about the past:
19996
- * it says the stream WAS being served, not that it still is. A caller commits to media on that frame — a
19997
- * process, a negotiated session — so a stream the station has quietly stopped serving strands it with no
19998
- * deadline, because warming is what arms one and a reuse skips warming by definition.
19999
- *
20000
- * The watch is the warm-up's own, deadline and retry alike, and the first frame to arrive AFTER the join
20001
- * clears it, that being the only frame which says the stream is still being served. A stream still serving
20002
- * clears it long before the deadline fires; one that is not fails its consumers exactly as a cold start that
20003
- * never delivered would.
20004
- *
20005
- * The start is re-issued at once and then on the retry's cadence, because a station that stopped serving a
20006
- * channel when its last consumer left is the very case the retry recovers: waiting the whole window to
20007
- * report what one re-issued start can fix is a timeout where a stream was available.
20008
- *
20009
- * Nothing is armed while a watch is already pending, so several consumers joining one reused stream share the
20010
- * watch the first of them started.
20014
+ * Watch an existing upstream with the warm-up retry and deadline. Any video or audio received after
20015
+ * the watch begins confirms delivery and settles it. Each consumer's keyframe gate remains separate.
20016
+ * Concurrent joins share a pending watch; the first join reissues the start immediately.
20011
20017
  */
20012
20018
  watchReusedStream() {
20013
20019
  if (this.warmDeadlineTimer.pending || this.warmRetryTimer !== void 0)
@@ -20056,7 +20062,7 @@ var SharedLiveSource = class {
20056
20062
  this.clearBudget();
20057
20063
  this.budgetTimer.arm(timerMs(ms, this.batteryBudgetMs), () => this.onBudgetExpire());
20058
20064
  }
20059
- /** Settle a reuse watch on any frame — the join already holds a decodable picture. */
20065
+ /** Settle a reuse watch on any upstream frame without changing consumer keyframe gates. */
20060
20066
  settleReuseWatch() {
20061
20067
  if (!this.reuseWatch)
20062
20068
  return;
@@ -20118,7 +20124,7 @@ var SharedLiveSource = class {
20118
20124
  this.lastParamSets = updatedParamSets(frame.data, this.lastParamSets);
20119
20125
  const item = { kind: "video", frame, timestampMs: Date.now(), config: this.configOf(frame) };
20120
20126
  if (frame.keyframe) {
20121
- this.lastKeyframe = item;
20127
+ this.hasKeyframe = true;
20122
20128
  const wasWarming = this.warmRetryTimer !== void 0 || this.warmDeadlineTimer.pending;
20123
20129
  this.delivered.keyframe = true;
20124
20130
  if (this._state === "warming")
@@ -20251,7 +20257,7 @@ var SharedLiveSource = class {
20251
20257
  this.lingerTimer.arm(this.lingerMs, () => this.teardown("stopped"));
20252
20258
  }
20253
20259
  /**
20254
- * Stop + drop the underlying stream and clear the prime/ring caches. Rebuildable via attach().
20260
+ * Stop + drop the underlying stream and clear the keyframe/ring caches. Rebuildable via attach().
20255
20261
  *
20256
20262
  * The stream reference is dropped BEFORE stopping it, because `stop()` emits `"stop"` synchronously and
20257
20263
  * this source listens for that — so stopping re-enters `teardown` through {@link onUpstreamEnd}. Clearing
@@ -20271,7 +20277,7 @@ var SharedLiveSource = class {
20271
20277
  stream?.stop();
20272
20278
  } catch {
20273
20279
  }
20274
- this.lastKeyframe = void 0;
20280
+ this.hasKeyframe = false;
20275
20281
  this.lastParamSets = void 0;
20276
20282
  this.declaredGeometry = void 0;
20277
20283
  this.configuredFrom = void 0;
@@ -20674,8 +20680,6 @@ function openReadableFromConsumer(consumer, opts = {}) {
20674
20680
  if (!readable.push(value))
20675
20681
  consumer.pause();
20676
20682
  });
20677
- consumer.on("video", () => {
20678
- });
20679
20683
  consumer.on("stop", () => readable.push(null));
20680
20684
  consumer.on("error", (err) => readable.destroy(err));
20681
20685
  return readable;
@@ -22187,7 +22191,7 @@ var P2PCommandRouter = class _P2PCommandRouter {
22187
22191
  * A {@link MediaProvider} bound to one serial — resolves the session then calls `p2p/media`.
22188
22192
  *
22189
22193
  * Every live egress here is a consumer of the SAME shared pull, so N `live()` calls collapse to one
22190
- * PPCS session and a live snapshot against a warm, keyframe-primed source costs no extra pull at all; a
22194
+ * PPCS session and a live snapshot against a warm shared source costs no extra pull at all; a
22191
22195
  * cold source warms one and waits for a clean keyframe. Each shared egress passes its complete options
22192
22196
  * through because any of them may create the source, whose power and retention hints are fixed for
22193
22197
  * everyone who joins later. The bounded {@link MediaProvider.record} clip is the exception: it opens its