@give-tech/ec-player 0.0.1-beta.41 → 0.0.1-beta.42

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.js CHANGED
@@ -230,6 +230,7 @@ class BasePlayer {
230
230
  this.decodeLoopAbort = false;
231
231
  this._currentTime = 0;
232
232
  this._duration = 0;
233
+ this._playbackRate = 1;
233
234
  this.state = {
234
235
  isPlaying: false,
235
236
  isLoaded: false,
@@ -245,7 +246,8 @@ class BasePlayer {
245
246
  totalSegments: 0,
246
247
  downloadSpeed: 0,
247
248
  currentTime: 0,
248
- duration: 0
249
+ duration: 0,
250
+ playbackRate: 1
249
251
  };
250
252
  this.renderLoop = () => {
251
253
  if (!this.isPlaying) return;
@@ -326,7 +328,8 @@ class BasePlayer {
326
328
  totalSegments: 0,
327
329
  downloadSpeed: 0,
328
330
  currentTime: 0,
329
- duration: 0
331
+ duration: 0,
332
+ playbackRate: 1
330
333
  };
331
334
  this.decoder = null;
332
335
  this.renderer = null;
@@ -350,6 +353,26 @@ class BasePlayer {
350
353
  getDuration() {
351
354
  return this._duration;
352
355
  }
356
+ /**
357
+ * 获取播放倍速
358
+ */
359
+ getPlaybackRate() {
360
+ return this._playbackRate;
361
+ }
362
+ /**
363
+ * 设置播放倍速
364
+ * @param rate 倍速值(1 = 正常,2 = 2倍速等)
365
+ */
366
+ setPlaybackRate(rate) {
367
+ if (rate <= 0) {
368
+ console.warn("[BasePlayer] Invalid playback rate:", rate);
369
+ return;
370
+ }
371
+ this._playbackRate = rate;
372
+ this.updateState({ playbackRate: rate });
373
+ this.callbacks.onPlaybackRateChange?.(rate);
374
+ console.log("[BasePlayer] Playback rate set to:", rate);
375
+ }
353
376
  /**
354
377
  * 设置当前时间(供子类调用)
355
378
  */
@@ -1762,19 +1785,41 @@ class HLSPlayer extends BasePlayer {
1762
1785
  totalSegments
1763
1786
  });
1764
1787
  if (this.frameBuffer.length > 0 && this.renderer) {
1765
- const frame = this.frameBuffer[0];
1766
1788
  const timescale = this.fmp4Demuxer.getTimescale();
1767
1789
  if (this.playStartTime === 0) {
1768
1790
  this.playStartTime = now;
1769
1791
  this.accumulatedMediaTime = 0;
1770
1792
  console.log("[renderLoop] Init: timescale=", timescale);
1771
1793
  }
1772
- const elapsedWallTime = now - this.playStartTime;
1773
- const frameDurationMs = frame.duration ? frame.duration * 1e3 / timescale : 33.33;
1774
- if (Math.floor(elapsedWallTime / 1e3) !== Math.floor((elapsedWallTime - 20) / 1e3)) {
1775
- console.log("[renderLoop] elapsed=", Math.floor(elapsedWallTime), "accumulated=", Math.floor(this.accumulatedMediaTime), "frameDuration=", frame.duration, "frameDurationMs=", frameDurationMs.toFixed(2), "frameBuffer=", this.frameBuffer.length);
1794
+ const elapsedWallTime = (now - this.playStartTime) * this._playbackRate;
1795
+ if (Math.floor(elapsedWallTime / 1e3) !== Math.floor((elapsedWallTime - 20 * this._playbackRate) / 1e3)) {
1796
+ console.log("[renderLoop] elapsed=", Math.floor(elapsedWallTime), "accumulated=", Math.floor(this.accumulatedMediaTime), "rate=", this._playbackRate, "frameBuffer=", this.frameBuffer.length);
1776
1797
  }
1798
+ const getFrameDurationMs = (frame2) => {
1799
+ return frame2.duration ? frame2.duration * 1e3 / timescale : 33.33;
1800
+ };
1777
1801
  const bufferMs = 50;
1802
+ if (this._playbackRate > 1) {
1803
+ const behindTime = elapsedWallTime - this.accumulatedMediaTime;
1804
+ if (behindTime > 50 && this.frameBuffer.length > 1) {
1805
+ const avgFrameDuration = getFrameDurationMs(this.frameBuffer[0]);
1806
+ const framesToSkip = Math.min(
1807
+ Math.floor(behindTime / avgFrameDuration),
1808
+ this.frameBuffer.length - 1
1809
+ // 保留至少 1 帧
1810
+ );
1811
+ if (framesToSkip > 0) {
1812
+ for (let i = 0; i < framesToSkip; i++) {
1813
+ const skipFrame = this.frameBuffer.shift();
1814
+ const skipDuration = getFrameDurationMs(skipFrame);
1815
+ this.accumulatedMediaTime += skipDuration;
1816
+ }
1817
+ console.log("[renderLoop] Skipped", framesToSkip, "frames at rate", this._playbackRate, ", behind=", Math.floor(behindTime));
1818
+ }
1819
+ }
1820
+ }
1821
+ const frame = this.frameBuffer[0];
1822
+ const frameDurationMs = getFrameDurationMs(frame);
1778
1823
  if (elapsedWallTime >= this.accumulatedMediaTime - bufferMs) {
1779
1824
  this.frameBuffer.shift();
1780
1825
  this.renderer.render(frame);
@@ -1786,10 +1831,8 @@ class HLSPlayer extends BasePlayer {
1786
1831
  this.callbacks.onFrameRender?.(frame);
1787
1832
  }
1788
1833
  } else {
1789
- if (this.playStartTime !== 0) {
1790
- const oldPlayStartTime = this.playStartTime;
1791
- this.playStartTime = now - this.accumulatedMediaTime;
1792
- console.log("[renderLoop] No frames, adjusting playStartTime from", oldPlayStartTime, "to", this.playStartTime, "accumulated=", this.accumulatedMediaTime);
1834
+ if (this.playStartTime === 0) {
1835
+ console.log("[renderLoop] Waiting for frames...");
1793
1836
  }
1794
1837
  }
1795
1838
  this.frameTimer = requestAnimationFrame(this.renderLoop);
@@ -1939,6 +1982,24 @@ class HLSPlayer extends BasePlayer {
1939
1982
  this.setCurrentTime(time);
1940
1983
  console.log("[HLSPlayer] Seek to", time, "ms, segment:", targetIndex);
1941
1984
  }
1985
+ /**
1986
+ * 设置播放倍速(覆盖基类方法)
1987
+ * 切换倍速时需要重置时间基准,避免跳跃
1988
+ */
1989
+ setPlaybackRate(rate) {
1990
+ if (rate <= 0) {
1991
+ console.warn("[HLSPlayer] Invalid playback rate:", rate);
1992
+ return;
1993
+ }
1994
+ const now = performance.now();
1995
+ if (this.playStartTime > 0) {
1996
+ this.playStartTime = now - this.accumulatedMediaTime / rate;
1997
+ console.log("[HLSPlayer] Playback rate changed to", rate, ", playStartTime reset, accumulated=", this.accumulatedMediaTime);
1998
+ }
1999
+ this._playbackRate = rate;
2000
+ this.updateState({ playbackRate: rate });
2001
+ this.callbacks.onPlaybackRateChange?.(rate);
2002
+ }
1942
2003
  /**
1943
2004
  * 解码循环
1944
2005
  */
@@ -1951,7 +2012,11 @@ class HLSPlayer extends BasePlayer {
1951
2012
  if (sampleQueueSize < maxSamplesBeforePause && this.prefetcher) {
1952
2013
  this.prefetcher.processQueue(1);
1953
2014
  }
1954
- if (!this.config.isLive && this.frameBuffer.length >= this.config.targetBufferSize) {
2015
+ const dynamicTargetSize = Math.max(
2016
+ this.config.targetBufferSize,
2017
+ this._playbackRate > 1 ? 30 * this._playbackRate : this.config.targetBufferSize
2018
+ );
2019
+ if (!this.config.isLive && this.frameBuffer.length >= dynamicTargetSize) {
1955
2020
  await this.sleep(10);
1956
2021
  continue;
1957
2022
  }
@@ -1960,27 +2025,57 @@ class HLSPlayer extends BasePlayer {
1960
2025
  if (this.isFMP4) {
1961
2026
  const queueSize = this.sampleQueue.length;
1962
2027
  if (queueSize > 0 || batchCount % 50 === 0) {
1963
- console.log("[DecodeLoop] fMP4: sampleQueue=", queueSize, "frameBuffer=", this.frameBuffer.length, "batch=", batchCount, "decoderInit=", this.decoderInitialized);
2028
+ console.log("[DecodeLoop] fMP4: sampleQueue=", queueSize, "frameBuffer=", this.frameBuffer.length, "batch=", batchCount, "decoderInit=", this.decoderInitialized, "rate=", this._playbackRate);
1964
2029
  }
1965
- while (this.sampleQueue.length > 0 && decodedInBatch < batchSize) {
1966
- const queuedSample = this.sampleQueue.shift();
1967
- const sample = queuedSample.sample;
1968
- const frame = this.decodeSample(sample);
1969
- if (frame) {
1970
- this.frameBuffer.push(frame);
1971
- decodedInBatch++;
1972
- if (!this.readyFired) {
1973
- this.readyFired = true;
1974
- this.callbacks.onReady?.();
2030
+ if (this._playbackRate > 1) {
2031
+ const skipInterval = this._playbackRate;
2032
+ let samplesProcessed = 0;
2033
+ while (this.sampleQueue.length > 0 && decodedInBatch < batchSize * 2) {
2034
+ const queuedSample = this.sampleQueue.shift();
2035
+ const sample = queuedSample.sample;
2036
+ samplesProcessed++;
2037
+ if (sample.isSync || samplesProcessed % skipInterval === 0) {
2038
+ const frame = this.decodeSample(sample);
2039
+ if (frame) {
2040
+ this.frameBuffer.push(frame);
2041
+ decodedInBatch++;
2042
+ if (!this.readyFired) {
2043
+ this.readyFired = true;
2044
+ this.callbacks.onReady?.();
2045
+ }
2046
+ if (this.config.isLive) {
2047
+ while (this.frameBuffer.length > this.config.targetBufferSize) {
2048
+ this.frameBuffer.shift();
2049
+ this.droppedFrames++;
2050
+ }
2051
+ }
2052
+ }
2053
+ } else {
2054
+ this.droppedFrames++;
1975
2055
  }
1976
- if (this.config.isLive) {
1977
- while (this.frameBuffer.length > this.config.targetBufferSize) {
1978
- this.frameBuffer.shift();
1979
- this.droppedFrames++;
2056
+ }
2057
+ if (samplesProcessed > 10 && this.droppedFrames > 0) {
2058
+ console.log("[DecodeLoop] High rate", this._playbackRate, "x: processed", samplesProcessed, "samples, decoded", decodedInBatch, "dropped", this.droppedFrames);
2059
+ }
2060
+ } else {
2061
+ while (this.sampleQueue.length > 0 && decodedInBatch < batchSize) {
2062
+ const queuedSample = this.sampleQueue.shift();
2063
+ const sample = queuedSample.sample;
2064
+ const frame = this.decodeSample(sample);
2065
+ if (frame) {
2066
+ this.frameBuffer.push(frame);
2067
+ decodedInBatch++;
2068
+ if (!this.readyFired) {
2069
+ this.readyFired = true;
2070
+ this.callbacks.onReady?.();
2071
+ }
2072
+ if (this.config.isLive) {
2073
+ while (this.frameBuffer.length > this.config.targetBufferSize) {
2074
+ this.frameBuffer.shift();
2075
+ this.droppedFrames++;
2076
+ }
1980
2077
  }
1981
2078
  }
1982
- } else {
1983
- console.warn("[DecodeLoop] decodeSample returned null, remaining samples=", this.sampleQueue.length);
1984
2079
  }
1985
2080
  }
1986
2081
  } else {
@@ -3146,7 +3241,7 @@ class FLVPlayer extends BasePlayer {
3146
3241
  this.pausedTime = 0;
3147
3242
  console.log("[FLVPlayer] Live first frame rendered immediately, dts:", firstFrame.dts);
3148
3243
  } else {
3149
- const elapsed = now - this.playStartTime - this.pausedTime;
3244
+ const elapsed = (now - this.playStartTime - this.pausedTime) * this._playbackRate;
3150
3245
  const currentTargetDts = this.firstFrameDts + elapsed;
3151
3246
  this.setCurrentTime(Math.floor(currentTargetDts));
3152
3247
  let frameToRender = null;
@@ -3162,7 +3257,7 @@ class FLVPlayer extends BasePlayer {
3162
3257
  }
3163
3258
  } else {
3164
3259
  const nextFrame = this._timedFrameBuffer[0];
3165
- const elapsed = now - this.playStartTime - this.playStartTimeOffset;
3260
+ const elapsed = (now - this.playStartTime - this.playStartTimeOffset) * this._playbackRate;
3166
3261
  const currentTargetPts = this.firstFrameDts + elapsed;
3167
3262
  if (this.lastRenderedDts >= 0) {
3168
3263
  this.setCurrentTime(Math.floor(this.lastRenderedDts));
@@ -3458,6 +3553,26 @@ class FLVPlayer extends BasePlayer {
3458
3553
  this.setCurrentTime(time);
3459
3554
  console.log("[FLVPlayer] Seek to", time, "ms");
3460
3555
  }
3556
+ /**
3557
+ * 设置播放倍速(覆盖基类方法)
3558
+ * 切换倍速时需要重置时间基准,避免跳跃
3559
+ */
3560
+ setPlaybackRate(rate) {
3561
+ if (rate <= 0) {
3562
+ console.warn("[FLVPlayer] Invalid playback rate:", rate);
3563
+ return;
3564
+ }
3565
+ const now = performance.now();
3566
+ if (this.playStartTime > 0 && this.firstFrameDts >= 0) {
3567
+ const offset = this.config.isLive ? this.pausedTime : this.playStartTimeOffset;
3568
+ const currentElapsed = (now - this.playStartTime - offset) * this._playbackRate;
3569
+ this.playStartTime = now - offset - currentElapsed / rate;
3570
+ console.log("[FLVPlayer] Playback rate changed to", rate, ", playStartTime reset, currentElapsed=", currentElapsed);
3571
+ }
3572
+ this._playbackRate = rate;
3573
+ this.updateState({ playbackRate: rate });
3574
+ this.callbacks.onPlaybackRateChange?.(rate);
3575
+ }
3461
3576
  /**
3462
3577
  * 解码循环
3463
3578
  */
@@ -4124,6 +4239,19 @@ class EcPlayerCore {
4124
4239
  getDuration() {
4125
4240
  return this.player?.getDuration() ?? 0;
4126
4241
  }
4242
+ /**
4243
+ * 获取播放倍速
4244
+ */
4245
+ getPlaybackRate() {
4246
+ return this.player?.getPlaybackRate() ?? 1;
4247
+ }
4248
+ /**
4249
+ * 设置播放倍速
4250
+ * @param rate 倍速值(1 = 正常,2 = 2倍速等)
4251
+ */
4252
+ setPlaybackRate(rate) {
4253
+ this.player?.setPlaybackRate(rate);
4254
+ }
4127
4255
  /**
4128
4256
  * 获取播放状态
4129
4257
  */
@@ -4144,7 +4272,8 @@ class EcPlayerCore {
4144
4272
  totalSegments: 0,
4145
4273
  downloadSpeed: 0,
4146
4274
  currentTime: 0,
4147
- duration: 0
4275
+ duration: 0,
4276
+ playbackRate: 1
4148
4277
  };
4149
4278
  }
4150
4279
  return this.player.getState();