@mebius-io/web 0.4.1 → 0.4.2

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.
@@ -42705,6 +42705,20 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
42705
42705
  };
42706
42706
 
42707
42707
  // src/internal/publish-transport.ts
42708
+ function preferH264(pc) {
42709
+ const caps = RTCRtpSender.getCapabilities?.("video");
42710
+ if (!caps?.codecs) return;
42711
+ const h264 = caps.codecs.filter((c) => c.mimeType.toLowerCase() === "video/h264");
42712
+ if (h264.length === 0) return;
42713
+ const rest = caps.codecs.filter((c) => c.mimeType.toLowerCase() !== "video/h264");
42714
+ for (const tr of pc.getTransceivers()) {
42715
+ if (tr.sender.track?.kind !== "video") continue;
42716
+ try {
42717
+ tr.setCodecPreferences?.([...h264, ...rest]);
42718
+ } catch {
42719
+ }
42720
+ }
42721
+ }
42708
42722
  var WhipPublishTransport = class {
42709
42723
  constructor(signaling) {
42710
42724
  this.signaling = signaling;
@@ -42717,6 +42731,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
42717
42731
  for (const track of stream.getTracks()) {
42718
42732
  pc.addTrack(track, stream);
42719
42733
  }
42734
+ preferH264(pc);
42720
42735
  const offer = await pc.createOffer();
42721
42736
  await pc.setLocalDescription(offer);
42722
42737
  await waitForIceGathering(pc);
@@ -42883,6 +42898,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
42883
42898
  this.video = null;
42884
42899
  this.endedCb = null;
42885
42900
  this.bufferingCb = null;
42901
+ /** Aborts this attempt's element listeners; see start(). */
42902
+ this.listeners = null;
42886
42903
  /** True when playback only started because the element had to be muted. */
42887
42904
  this.mutedByPolicy = false;
42888
42905
  }
@@ -42895,8 +42912,10 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
42895
42912
  async start(streamId, video) {
42896
42913
  this.video = video;
42897
42914
  const url = this.deliveryPath ? this.signaling.deliveryUrl(this.deliveryPath) : this.signaling.scalePlaylistUrl(streamId);
42898
- video.addEventListener("ended", () => this.endedCb?.());
42899
- video.addEventListener("waiting", () => this.bufferingCb?.());
42915
+ this.listeners = new AbortController();
42916
+ const { signal } = this.listeners;
42917
+ video.addEventListener("ended", () => this.endedCb?.(), { signal });
42918
+ video.addEventListener("waiting", () => this.bufferingCb?.(), { signal });
42900
42919
  if (video.canPlayType("application/vnd.apple.mpegurl")) {
42901
42920
  video.src = url;
42902
42921
  this.mutedByPolicy = (await playWithAutoplayFallback(video)).mutedByPolicy;
@@ -42912,7 +42931,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
42912
42931
  if (!Hls2.isSupported()) {
42913
42932
  throw mebiusError("CONNECTION_FAILED", "Scale playback is not supported in this browser.");
42914
42933
  }
42915
- const hls = new Hls2();
42934
+ const hls = new Hls2({ maxLiveSyncPlaybackRate: 1.5 });
42916
42935
  this.hls = hls;
42917
42936
  hls.on(Hls2.Events.ERROR, (_evt, data) => {
42918
42937
  if (data.fatal) this.bufferingCb?.();
@@ -42922,6 +42941,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
42922
42941
  this.mutedByPolicy = (await playWithAutoplayFallback(video)).mutedByPolicy;
42923
42942
  }
42924
42943
  async stop() {
42944
+ this.listeners?.abort();
42945
+ this.listeners = null;
42925
42946
  this.hls?.destroy();
42926
42947
  this.hls = null;
42927
42948
  if (this.video) {
@@ -42941,6 +42962,40 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
42941
42962
  };
42942
42963
 
42943
42964
  // src/internal/balanced-view-transport.ts
42965
+ var LIVE_FLV_CONFIG = {
42966
+ enableStashBuffer: false,
42967
+ stashInitialSize: 128,
42968
+ lazyLoad: false,
42969
+ autoCleanupSourceBuffer: true,
42970
+ autoCleanupMaxBackwardDuration: 30,
42971
+ autoCleanupMinBackwardDuration: 10,
42972
+ reuseRedirectedURL: true
42973
+ };
42974
+ var MAX_DRIFT_S = 2;
42975
+ var EDGE_MARGIN_S = 0.4;
42976
+ var AUDIO_RETRY_MS = 2500;
42977
+ function stalledAtZero(video, ms) {
42978
+ if (video.currentTime > 0) return Promise.resolve(false);
42979
+ return new Promise((resolve) => {
42980
+ const done = (stalled) => {
42981
+ clearTimeout(timer);
42982
+ video.removeEventListener("timeupdate", onTime);
42983
+ resolve(stalled);
42984
+ };
42985
+ const onTime = () => {
42986
+ if (video.currentTime > 0) done(false);
42987
+ };
42988
+ const timer = setTimeout(() => done(true), ms);
42989
+ video.addEventListener("timeupdate", onTime);
42990
+ });
42991
+ }
42992
+ function chaseLiveEdge(video) {
42993
+ const ranges = video.buffered;
42994
+ if (ranges.length === 0) return;
42995
+ const edge = ranges.end(ranges.length - 1);
42996
+ if (edge - video.currentTime <= MAX_DRIFT_S) return;
42997
+ video.currentTime = edge - EDGE_MARGIN_S;
42998
+ }
42944
42999
  var FlvViewTransport = class {
42945
43000
  constructor(signaling, deliveryPath) {
42946
43001
  this.signaling = signaling;
@@ -42949,6 +43004,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
42949
43004
  this.video = null;
42950
43005
  this.endedCb = null;
42951
43006
  this.bufferingCb = null;
43007
+ /** Aborts this attempt's element listeners; see start(). */
43008
+ this.listeners = null;
42952
43009
  /** True when playback only started because the element had to be muted. */
42953
43010
  this.mutedByPolicy = false;
42954
43011
  }
@@ -42961,8 +43018,11 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
42961
43018
  async start(_streamId, video) {
42962
43019
  this.video = video;
42963
43020
  const url = this.signaling.deliveryUrl(this.deliveryPath);
42964
- video.addEventListener("ended", () => this.endedCb?.());
42965
- video.addEventListener("waiting", () => this.bufferingCb?.());
43021
+ this.listeners = new AbortController();
43022
+ const { signal } = this.listeners;
43023
+ video.addEventListener("ended", () => this.endedCb?.(), { signal });
43024
+ video.addEventListener("waiting", () => this.bufferingCb?.(), { signal });
43025
+ video.addEventListener("timeupdate", () => chaseLiveEdge(video), { signal });
42966
43026
  let mod;
42967
43027
  try {
42968
43028
  mod = await Promise.resolve().then(() => __toESM(require_flv(), 1));
@@ -42973,20 +43033,38 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
42973
43033
  if (!flvjs.isSupported()) {
42974
43034
  throw mebiusError("CONNECTION_FAILED", "Balanced playback is not supported in this browser.");
42975
43035
  }
42976
- const player = flvjs.createPlayer({ type: "flv", url, isLive: true });
43036
+ this.attachPlayer(flvjs, video, url, true);
43037
+ const firstPlay = playWithAutoplayFallback(video);
43038
+ firstPlay.catch(() => void 0);
43039
+ if (await stalledAtZero(video, AUDIO_RETRY_MS)) {
43040
+ this.teardownPlayer();
43041
+ this.attachPlayer(flvjs, video, url, false);
43042
+ this.mutedByPolicy = (await playWithAutoplayFallback(video)).mutedByPolicy;
43043
+ return;
43044
+ }
43045
+ this.mutedByPolicy = (await firstPlay).mutedByPolicy;
43046
+ }
43047
+ attachPlayer(flvjs, video, url, withAudio) {
43048
+ const player = flvjs.createPlayer(
43049
+ { type: "flv", url, isLive: true, ...withAudio ? {} : { hasAudio: false } },
43050
+ LIVE_FLV_CONFIG
43051
+ );
42977
43052
  this.player = player;
42978
43053
  player.on(flvjs.Events.ERROR ?? "error", () => this.bufferingCb?.());
42979
43054
  player.attachMediaElement(video);
42980
43055
  player.load();
42981
- this.mutedByPolicy = (await playWithAutoplayFallback(video)).mutedByPolicy;
43056
+ }
43057
+ teardownPlayer() {
43058
+ if (!this.player) return;
43059
+ this.player.unload();
43060
+ this.player.detachMediaElement();
43061
+ this.player.destroy();
43062
+ this.player = null;
42982
43063
  }
42983
43064
  async stop() {
42984
- if (this.player) {
42985
- this.player.unload();
42986
- this.player.detachMediaElement();
42987
- this.player.destroy();
42988
- this.player = null;
42989
- }
43065
+ this.listeners?.abort();
43066
+ this.listeners = null;
43067
+ this.teardownPlayer();
42990
43068
  if (this.video) {
42991
43069
  this.video.removeAttribute("src");
42992
43070
  this.video.load();
@@ -43035,7 +43113,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
43035
43113
  }
43036
43114
 
43037
43115
  // src/internal/telemetry.ts
43038
- var SDK_VERSION = "web/0.4.1";
43116
+ var SDK_VERSION = "web/0.4.2";
43039
43117
  var FLUSH_INTERVAL_MS = 15e3;
43040
43118
  var MAX_BATCH = 64;
43041
43119
  function describeDevice() {
@@ -43095,7 +43173,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli
43095
43173
  if (beacon && typeof navigator !== "undefined" && navigator.sendBeacon) {
43096
43174
  const url = `${this.target.url}${this.target.url.includes("?") ? "&" : "?"}token=${encodeURIComponent(this.target.token)}`;
43097
43175
  try {
43098
- navigator.sendBeacon(url, new Blob([body], { type: "application/json" }));
43176
+ navigator.sendBeacon(url, new Blob([body], { type: "text/plain" }));
43099
43177
  } catch {
43100
43178
  }
43101
43179
  return;