@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 +161 -32
- package/dist/index.js.map +1 -1
- package/dist/player/BasePlayer.d.ts +10 -0
- package/dist/player/BasePlayer.d.ts.map +1 -1
- package/dist/player/EcPlayerCore.d.ts +9 -0
- package/dist/player/EcPlayerCore.d.ts.map +1 -1
- package/dist/player/FLVPlayer.d.ts +5 -0
- package/dist/player/FLVPlayer.d.ts.map +1 -1
- package/dist/player/HLSPlayer.d.ts +5 -0
- package/dist/player/HLSPlayer.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
1774
|
-
|
|
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
|
|
1790
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1966
|
-
const
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
if (
|
|
1973
|
-
|
|
1974
|
-
|
|
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
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
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();
|