@mebius-io/web 0.4.2 → 0.4.4

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.d.ts CHANGED
@@ -273,13 +273,30 @@ declare class MebiusPlayer extends TypedEmitter<PlayerEventMap> {
273
273
  private statsTimer;
274
274
  private playing;
275
275
  private reporter;
276
+ /** True between a `buffering` event and the element actually resuming. */
277
+ private stalled;
278
+ /** Cancels element listeners bound for the lifetime of one play(). */
279
+ private elementListeners;
276
280
  /** @internal */
277
281
  constructor(signaling: SignalingClient, options?: PlayerOptions, deliveries?: readonly MebiusDelivery[], telemetry?: TelemetryTarget | null, userId?: string | undefined);
278
282
  /** Start playing `streamId` into the given video element or selector. */
279
283
  play(streamId: string, viewTarget: ViewTarget): Promise<void>;
280
284
  /** Stop playback and detach from the video element. */
281
285
  stop(): Promise<void>;
282
- /** Set output volume in the range 0..1. */
286
+ /**
287
+ * Set output volume in the range 0..1.
288
+ *
289
+ * Any volume above zero also unmutes. Playback often starts muted — the
290
+ * element may carry `muted` in the app's own markup, and the SDK itself mutes
291
+ * and retries when the browser refuses to autoplay with sound — and
292
+ * `video.volume` has no audible effect while `muted` is set. Setting volume
293
+ * without clearing it meant an app whose only audio control was a slider
294
+ * could never produce sound: the value moved, the stream stayed silent, and
295
+ * nothing reported a problem.
296
+ *
297
+ * Volume 0 mutes rather than merely turning the level down, so a UI that
298
+ * drags to zero also survives a later unmute at the element level.
299
+ */
283
300
  setVolume(volume: number): void;
284
301
  private attach;
285
302
  private startStats;
@@ -43113,7 +43113,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
43113
43113
  }
43114
43114
 
43115
43115
  // src/internal/telemetry.ts
43116
- var SDK_VERSION = "web/0.4.2";
43116
+ var SDK_VERSION = "web/0.4.4";
43117
43117
  var FLUSH_INTERVAL_MS = 15e3;
43118
43118
  var MAX_BATCH = 64;
43119
43119
  function describeDevice() {
@@ -43307,6 +43307,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
43307
43307
  // src/player.ts
43308
43308
  var STATS_INTERVAL_MS2 = 2e3;
43309
43309
  var FIRST_FRAME_TIMEOUT_MS = 8e3;
43310
+ var ELEMENT_OWNER = /* @__PURE__ */ new WeakMap();
43310
43311
  var MebiusPlayer = class extends TypedEmitter {
43311
43312
  /** @internal */
43312
43313
  constructor(signaling, options = {}, deliveries = [], telemetry = null, userId) {
@@ -43318,13 +43319,30 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
43318
43319
  this.statsTimer = null;
43319
43320
  this.playing = false;
43320
43321
  this.reporter = null;
43322
+ /** True between a `buffering` event and the element actually resuming. */
43323
+ this.stalled = false;
43324
+ /** Cancels element listeners bound for the lifetime of one play(). */
43325
+ this.elementListeners = null;
43321
43326
  this.candidates = createViewCandidates(options.mode ?? "auto", signaling, deliveries);
43322
43327
  }
43323
43328
  /** Start playing `streamId` into the given video element or selector. */
43324
43329
  async play(streamId, viewTarget) {
43325
43330
  if (this.playing) return;
43326
43331
  const video = resolveVideoElement(viewTarget);
43332
+ const previous = ELEMENT_OWNER.get(video);
43333
+ if (previous && previous !== this) await previous.stop();
43334
+ ELEMENT_OWNER.set(video, this);
43327
43335
  this.video = video;
43336
+ this.elementListeners = new AbortController();
43337
+ video.addEventListener(
43338
+ "playing",
43339
+ () => {
43340
+ if (!this.stalled || !this.playing) return;
43341
+ this.stalled = false;
43342
+ this.emit("playing", { streamId });
43343
+ },
43344
+ { signal: this.elementListeners.signal }
43345
+ );
43328
43346
  const startedAtMs = Date.now();
43329
43347
  let lastError = null;
43330
43348
  for (const candidate of this.candidates) {
@@ -43355,6 +43373,12 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
43355
43373
  }
43356
43374
  /** Stop playback and detach from the video element. */
43357
43375
  async stop() {
43376
+ this.elementListeners?.abort();
43377
+ this.elementListeners = null;
43378
+ if (this.video && ELEMENT_OWNER.get(this.video) === this) {
43379
+ ELEMENT_OWNER.delete(this.video);
43380
+ }
43381
+ this.stalled = false;
43358
43382
  this.stopStats();
43359
43383
  await this.reporter?.stop();
43360
43384
  this.reporter = null;
@@ -43363,10 +43387,25 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
43363
43387
  this.video = null;
43364
43388
  this.playing = false;
43365
43389
  }
43366
- /** Set output volume in the range 0..1. */
43390
+ /**
43391
+ * Set output volume in the range 0..1.
43392
+ *
43393
+ * Any volume above zero also unmutes. Playback often starts muted — the
43394
+ * element may carry `muted` in the app's own markup, and the SDK itself mutes
43395
+ * and retries when the browser refuses to autoplay with sound — and
43396
+ * `video.volume` has no audible effect while `muted` is set. Setting volume
43397
+ * without clearing it meant an app whose only audio control was a slider
43398
+ * could never produce sound: the value moved, the stream stayed silent, and
43399
+ * nothing reported a problem.
43400
+ *
43401
+ * Volume 0 mutes rather than merely turning the level down, so a UI that
43402
+ * drags to zero also survives a later unmute at the element level.
43403
+ */
43367
43404
  setVolume(volume) {
43368
43405
  const v = Math.min(1, Math.max(0, volume));
43369
- if (this.video) this.video.volume = v;
43406
+ if (!this.video) return;
43407
+ this.video.volume = v;
43408
+ this.video.muted = v === 0;
43370
43409
  }
43371
43410
  attach(transport) {
43372
43411
  transport.onEnded(() => {
@@ -43379,6 +43418,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
43379
43418
  });
43380
43419
  transport.onBuffering(() => {
43381
43420
  if (this.transport !== transport) return;
43421
+ this.stalled = true;
43382
43422
  this.emit("buffering", void 0);
43383
43423
  });
43384
43424
  }