eb-player 2.0.10 → 2.0.11

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.
@@ -4,7 +4,7 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.EBPlayer = {}));
5
5
  })(this, (function (exports) { 'use strict';
6
6
 
7
- var __EB_PLAYER_VERSION__ = "2.0.10";
7
+ var __EB_PLAYER_VERSION__ = "2.0.11";
8
8
 
9
9
  /**
10
10
  * Finite State Machine for player playback state transitions.
@@ -4284,12 +4284,20 @@
4284
4284
  video.addEventListener('ended', () => {
4285
4285
  state.playbackState = 'ended';
4286
4286
  }, { signal });
4287
- // Safety net: clear stale 'buffering' state when playback is clearly advancing.
4288
- // In some edge cases (live stream segment boundaries), the browser fires 'waiting'
4289
- // but never fires 'playing' even though video resumes from buffer. The timeupdate
4290
- // event fires reliably while time advances, so we use it to recover.
4287
+ // Safety net: reconcile stale FSM state when playback is clearly advancing.
4288
+ // Covers two scenarios:
4289
+ // 1. Live stream segment boundaries where browser fires 'waiting' but never
4290
+ // fires 'playing' even though video resumes from buffer.
4291
+ // 2. Safari autoplay: when the browser starts playback via the autoplay attribute,
4292
+ // the 'playing' event can be missed or arrive out of order, leaving the FSM
4293
+ // stuck in 'loading' or 'buffering' while the video is actually playing.
4294
+ // The timeupdate event fires reliably while time advances, so we use it to recover.
4295
+ // Note: Safari with MSE/hls.js may report readyState=2 (HAVE_CURRENT_DATA) even
4296
+ // while actively playing and advancing currentTime. We don't gate on readyState
4297
+ // here — the fact that timeupdate fires with !paused is sufficient proof of playback.
4291
4298
  video.addEventListener('timeupdate', () => {
4292
- if (state.playbackState === 'buffering' && !video.paused && video.readyState >= 3) {
4299
+ const stuck = state.playbackState === 'buffering' || state.playbackState === 'loading';
4300
+ if (stuck && !video.paused) {
4293
4301
  state.playbackState = 'playing';
4294
4302
  }
4295
4303
  }, { signal });
@@ -5398,6 +5406,39 @@
5398
5406
  }, { signal });
5399
5407
  // Start stall watchdog
5400
5408
  this.startWatchdog();
5409
+ // Post-init state reconciliation: during the async init() above, the browser
5410
+ // may have started or blocked playback in ways that leave the FSM out of sync.
5411
+ // Two cases:
5412
+ // A) Video is playing but FSM missed the 'playing' event (Safari event ordering)
5413
+ // B) Autoplay was blocked — Safari fires 'waiting' (→ buffering) but may not
5414
+ // fire 'pause', leaving the spinner visible over a paused video
5415
+ // A short periodic check covers both cases and self-cleans after convergence.
5416
+ const reconcileVideo = video;
5417
+ const reconcileState = state;
5418
+ let reconcileCount = 0;
5419
+ const reconcileTimer = setInterval(() => {
5420
+ reconcileCount++;
5421
+ if (reconcileCount >= 10) {
5422
+ clearInterval(reconcileTimer);
5423
+ return;
5424
+ }
5425
+ const fsm = reconcileState.playbackState;
5426
+ // Case A: video is playing but FSM disagrees
5427
+ // Note: Safari/MSE may report readyState=2 while actively playing, so we
5428
+ // check currentTime advancement instead of readyState >= 3.
5429
+ if (fsm !== 'playing' && !reconcileVideo.paused && reconcileVideo.currentTime > 0) {
5430
+ reconcileState.playbackState = 'playing';
5431
+ clearInterval(reconcileTimer);
5432
+ return;
5433
+ }
5434
+ // Case B: autoplay blocked — video is paused but FSM stuck in buffering/loading
5435
+ if ((fsm === 'buffering' || fsm === 'loading') && reconcileVideo.paused) {
5436
+ reconcileState.playbackState = 'paused';
5437
+ clearInterval(reconcileTimer);
5438
+ }
5439
+ }, 200);
5440
+ // Clean up on engine detach
5441
+ signal.addEventListener('abort', () => clearInterval(reconcileTimer), { once: true });
5401
5442
  }
5402
5443
  // -------------------------------------------------------------------------
5403
5444
  // Driver event mapping
@@ -5873,6 +5914,28 @@
5873
5914
  };
5874
5915
  player.on(dashjs.MediaPlayer.events['STREAM_INITIALIZED'], onInit);
5875
5916
  });
5917
+ // Post-init state reconciliation (same as HLS engine — see comment there)
5918
+ const reconcileVideo = video;
5919
+ const reconcileState = state;
5920
+ let reconcileCount = 0;
5921
+ const reconcileTimer = setInterval(() => {
5922
+ reconcileCount++;
5923
+ if (reconcileCount >= 10) {
5924
+ clearInterval(reconcileTimer);
5925
+ return;
5926
+ }
5927
+ const fsm = reconcileState.playbackState;
5928
+ if (fsm !== 'playing' && !reconcileVideo.paused && reconcileVideo.currentTime > 0) {
5929
+ reconcileState.playbackState = 'playing';
5930
+ clearInterval(reconcileTimer);
5931
+ return;
5932
+ }
5933
+ if ((fsm === 'buffering' || fsm === 'loading') && reconcileVideo.paused) {
5934
+ reconcileState.playbackState = 'paused';
5935
+ clearInterval(reconcileTimer);
5936
+ }
5937
+ }, 200);
5938
+ signal.addEventListener('abort', () => clearInterval(reconcileTimer), { once: true });
5876
5939
  }
5877
5940
  // -------------------------------------------------------------------------
5878
5941
  // Event mapping