@give-tech/ec-player 0.0.1-beta.30 → 0.0.1-beta.32
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 +82 -13
- package/dist/index.js.map +1 -1
- package/dist/player/FLVPlayer.d.ts +1 -0
- package/dist/player/FLVPlayer.d.ts.map +1 -1
- package/dist/player/HLSPlayer.d.ts +5 -1
- package/dist/player/HLSPlayer.d.ts.map +1 -1
- package/dist/prefetch/SegmentPrefetcher.d.ts +2 -1
- package/dist/prefetch/SegmentPrefetcher.d.ts.map +1 -1
- package/dist/prefetch/types.d.ts +2 -0
- package/dist/prefetch/types.d.ts.map +1 -1
- package/package.json +1 -4
- package/wasm/decoder.js +19 -0
- package/wasm/decoder.wasm +0 -0
package/dist/index.js
CHANGED
|
@@ -118,9 +118,11 @@ class H264Decoder {
|
|
|
118
118
|
}
|
|
119
119
|
const DEFAULT_PLAYER_CONFIG = {
|
|
120
120
|
wasmPath: "/wasm/decoder-simd.js",
|
|
121
|
-
targetBufferSize:
|
|
121
|
+
targetBufferSize: 90,
|
|
122
|
+
// 90 帧(约 3.6 秒 @25fps),适应高分辨率视频
|
|
122
123
|
decodeBatchSize: 2,
|
|
123
|
-
maxQueueSize:
|
|
124
|
+
maxQueueSize: 300,
|
|
125
|
+
// 增加 sample 队列上限
|
|
124
126
|
isLive: false
|
|
125
127
|
};
|
|
126
128
|
class BasePlayer {
|
|
@@ -1236,7 +1238,8 @@ class SegmentPrefetcher extends BasePrefetcher {
|
|
|
1236
1238
|
this.prefetchQueue.push({
|
|
1237
1239
|
data,
|
|
1238
1240
|
segmentIndex: nextIndex,
|
|
1239
|
-
fetchTime
|
|
1241
|
+
fetchTime,
|
|
1242
|
+
segmentDuration: segment.duration
|
|
1240
1243
|
});
|
|
1241
1244
|
this.fetchedSegmentCount++;
|
|
1242
1245
|
this.updateStatus({
|
|
@@ -1274,7 +1277,7 @@ class SegmentPrefetcher extends BasePrefetcher {
|
|
|
1274
1277
|
this.prefetchQueue.shift();
|
|
1275
1278
|
this.updateStatus({ prefetchQueueSize: this.prefetchQueue.length });
|
|
1276
1279
|
const parseStart = performance.now();
|
|
1277
|
-
const itemCount = this.parseSegment(item.data, item.segmentIndex);
|
|
1280
|
+
const itemCount = this.parseSegment(item.data, item.segmentIndex, item.segmentDuration);
|
|
1278
1281
|
const parseTime = performance.now() - parseStart;
|
|
1279
1282
|
this.currentSegmentIndex++;
|
|
1280
1283
|
this.updateStatus({ currentSegmentIndex: this.currentSegmentIndex });
|
|
@@ -1510,7 +1513,8 @@ const HLS_PREFETCHER_CONFIG = {
|
|
|
1510
1513
|
...DEFAULT_SEGMENT_PREFETCHER_CONFIG,
|
|
1511
1514
|
lowWaterMark: 30,
|
|
1512
1515
|
highWaterMark: 100,
|
|
1513
|
-
prefetchAhead:
|
|
1516
|
+
prefetchAhead: 6,
|
|
1517
|
+
// 预载 6 个分片(约 30 秒),适应高分辨率视频
|
|
1514
1518
|
prefetchInterval: 10,
|
|
1515
1519
|
dynamicInterval: true
|
|
1516
1520
|
};
|
|
@@ -1545,11 +1549,11 @@ class HLSSegmentPrefetcher extends SegmentPrefetcher {
|
|
|
1545
1549
|
/**
|
|
1546
1550
|
* 解析分片数据
|
|
1547
1551
|
*/
|
|
1548
|
-
parseSegment(data, index) {
|
|
1552
|
+
parseSegment(data, index, segmentDuration) {
|
|
1549
1553
|
if (this.player.isFMP4) {
|
|
1550
1554
|
return this.player.parseFMP4Data(data);
|
|
1551
1555
|
} else {
|
|
1552
|
-
return this.player.parseTSData(data);
|
|
1556
|
+
return this.player.parseTSData(data, segmentDuration);
|
|
1553
1557
|
}
|
|
1554
1558
|
}
|
|
1555
1559
|
/**
|
|
@@ -1581,14 +1585,36 @@ class HLSPlayer extends BasePlayer {
|
|
|
1581
1585
|
this.currentPlaylistUrl = "";
|
|
1582
1586
|
this._nalQueue = [];
|
|
1583
1587
|
this._sampleQueue = [];
|
|
1588
|
+
this._currentSegmentDuration = 0;
|
|
1584
1589
|
this.prefetcher = null;
|
|
1590
|
+
this._nalCountInCurrentSegment = 0;
|
|
1585
1591
|
this.lastRenderTime = 0;
|
|
1586
1592
|
this.playStartTime = 0;
|
|
1587
1593
|
this.accumulatedMediaTime = 0;
|
|
1588
1594
|
this._lastLogTime = 0;
|
|
1595
|
+
this._lastRafTime = 0;
|
|
1589
1596
|
this.renderLoop = (timestamp = 0) => {
|
|
1590
1597
|
if (!this.isPlaying) return;
|
|
1591
1598
|
const now = performance.now();
|
|
1599
|
+
const BACKGROUND_PAUSE_THRESHOLD = 500;
|
|
1600
|
+
if (this._lastRafTime > 0 && now - this._lastRafTime > BACKGROUND_PAUSE_THRESHOLD) {
|
|
1601
|
+
const pauseDuration = now - this._lastRafTime;
|
|
1602
|
+
console.log("[renderLoop] Background pause detected, duration:", pauseDuration.toFixed(0), "ms");
|
|
1603
|
+
if (this.config.isLive) {
|
|
1604
|
+
const KEEP_FRAMES = 5;
|
|
1605
|
+
if (this.frameBuffer.length > KEEP_FRAMES) {
|
|
1606
|
+
const droppedCount = this.frameBuffer.length - KEEP_FRAMES;
|
|
1607
|
+
this.frameBuffer = this.frameBuffer.slice(-KEEP_FRAMES);
|
|
1608
|
+
this.droppedFrames += droppedCount;
|
|
1609
|
+
console.log("[renderLoop] Live resume: dropped", droppedCount, "old frames, keeping latest", KEEP_FRAMES);
|
|
1610
|
+
}
|
|
1611
|
+
this.playStartTime = now;
|
|
1612
|
+
this.accumulatedMediaTime = 0;
|
|
1613
|
+
} else {
|
|
1614
|
+
this.playStartTime = now - this.accumulatedMediaTime;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
this._lastRafTime = now;
|
|
1592
1618
|
if (!this._lastLogTime || now - this._lastLogTime > 1e3) {
|
|
1593
1619
|
this._lastLogTime = now;
|
|
1594
1620
|
console.log("[renderLoop] frameBuffer=", this.frameBuffer.length, "renderer=", !!this.renderer);
|
|
@@ -1693,6 +1719,8 @@ class HLSPlayer extends BasePlayer {
|
|
|
1693
1719
|
isLoaded: true,
|
|
1694
1720
|
totalSegments: segments.length
|
|
1695
1721
|
});
|
|
1722
|
+
this.initPrefetcher();
|
|
1723
|
+
this.prefetcher.start();
|
|
1696
1724
|
}
|
|
1697
1725
|
/**
|
|
1698
1726
|
* 初始化解码器(覆盖基类方法,添加 fMP4 支持)
|
|
@@ -1789,11 +1817,12 @@ class HLSPlayer extends BasePlayer {
|
|
|
1789
1817
|
while (this.nalQueue.length > 0 && decodedInBatch < batchSize) {
|
|
1790
1818
|
const queuedNal = this.nalQueue.shift();
|
|
1791
1819
|
const nalUnit = queuedNal.nalUnit;
|
|
1820
|
+
const segmentDuration = queuedNal.segmentDuration;
|
|
1792
1821
|
if (!this.decoderInitialized && (nalUnit.type === 7 || nalUnit.type === 8)) {
|
|
1793
1822
|
this.initDecoderParamsSync([nalUnit]);
|
|
1794
1823
|
}
|
|
1795
1824
|
if (nalUnit.type === 5 || nalUnit.type === 1) {
|
|
1796
|
-
const frame = this.decodeNAL(nalUnit);
|
|
1825
|
+
const frame = this.decodeNAL(nalUnit, segmentDuration);
|
|
1797
1826
|
if (frame) {
|
|
1798
1827
|
this.frameBuffer.push(frame);
|
|
1799
1828
|
decodedInBatch++;
|
|
@@ -1907,7 +1936,7 @@ class HLSPlayer extends BasePlayer {
|
|
|
1907
1936
|
/**
|
|
1908
1937
|
* 解析 TS 数据
|
|
1909
1938
|
*/
|
|
1910
|
-
parseTSData(tsData) {
|
|
1939
|
+
parseTSData(tsData, segmentDuration) {
|
|
1911
1940
|
const packets = this.tsDemuxer.parse(tsData);
|
|
1912
1941
|
let nalCount = 0;
|
|
1913
1942
|
if (packets.video.length > 0) {
|
|
@@ -1917,8 +1946,15 @@ class HLSPlayer extends BasePlayer {
|
|
|
1917
1946
|
if (!this.decoderInitialized) {
|
|
1918
1947
|
this.initDecoderParamsSync(nalUnits);
|
|
1919
1948
|
}
|
|
1949
|
+
if (segmentDuration !== void 0) {
|
|
1950
|
+
this._currentSegmentDuration = segmentDuration;
|
|
1951
|
+
}
|
|
1920
1952
|
for (const nalUnit of nalUnits) {
|
|
1921
|
-
this._nalQueue.push({
|
|
1953
|
+
this._nalQueue.push({
|
|
1954
|
+
nalUnit,
|
|
1955
|
+
pts: 0,
|
|
1956
|
+
segmentDuration: this._currentSegmentDuration
|
|
1957
|
+
});
|
|
1922
1958
|
}
|
|
1923
1959
|
nalCount = nalUnits.length;
|
|
1924
1960
|
}
|
|
@@ -1975,7 +2011,13 @@ class HLSPlayer extends BasePlayer {
|
|
|
1975
2011
|
this._sampleQueue.length = 0;
|
|
1976
2012
|
this._nalQueue.length = 0;
|
|
1977
2013
|
const url = uri.startsWith("http") ? uri : this.currentPlaylistUrl.substring(0, this.currentPlaylistUrl.lastIndexOf("/") + 1) + uri;
|
|
1978
|
-
const
|
|
2014
|
+
const headers = {};
|
|
2015
|
+
if (this.initSegment?.uri === uri && this.initSegment.byteRange) {
|
|
2016
|
+
const { start, end } = this.initSegment.byteRange;
|
|
2017
|
+
headers["Range"] = `bytes=${start}-${end}`;
|
|
2018
|
+
console.log("[fMP4] Using byte range for init segment:", { start, end });
|
|
2019
|
+
}
|
|
2020
|
+
const response = await fetch(url, { headers });
|
|
1979
2021
|
const data = new Uint8Array(await response.arrayBuffer());
|
|
1980
2022
|
console.log("[fMP4] New init segment data size:", data.length, "bytes");
|
|
1981
2023
|
const initInfo = this.fmp4Demuxer.parseInitSegment(data);
|
|
@@ -2294,16 +2336,22 @@ class HLSPlayer extends BasePlayer {
|
|
|
2294
2336
|
/**
|
|
2295
2337
|
* 解码单个 NAL 单元
|
|
2296
2338
|
*/
|
|
2297
|
-
decodeNAL(nalUnit) {
|
|
2339
|
+
decodeNAL(nalUnit, segmentDuration) {
|
|
2298
2340
|
if (!this.decoder) return null;
|
|
2299
2341
|
const nalWithStartCode = new Uint8Array(nalUnit.size + 4);
|
|
2300
2342
|
nalWithStartCode.set([0, 0, 0, 1], 0);
|
|
2301
2343
|
nalWithStartCode.set(nalUnit.data, 4);
|
|
2302
|
-
|
|
2344
|
+
const frame = this.decoder.decode({
|
|
2303
2345
|
type: nalUnit.type,
|
|
2304
2346
|
data: nalWithStartCode,
|
|
2305
2347
|
size: nalWithStartCode.length
|
|
2306
2348
|
});
|
|
2349
|
+
if (frame && segmentDuration && segmentDuration > 0) {
|
|
2350
|
+
const timescale = 1e3;
|
|
2351
|
+
const frameDuration = timescale / 60;
|
|
2352
|
+
frame.duration = frameDuration;
|
|
2353
|
+
}
|
|
2354
|
+
return frame;
|
|
2307
2355
|
}
|
|
2308
2356
|
}
|
|
2309
2357
|
const FLV_SIGNATURE = [70, 76, 86];
|
|
@@ -2839,6 +2887,7 @@ class FLVPlayer extends BasePlayer {
|
|
|
2839
2887
|
this.bufferEmptyStartTime = 0;
|
|
2840
2888
|
this.playStartTimeOffset = 0;
|
|
2841
2889
|
this.resyncCount = 0;
|
|
2890
|
+
this._lastRafTime = 0;
|
|
2842
2891
|
this.renderLoop = () => {
|
|
2843
2892
|
if (!this.isPlaying) return;
|
|
2844
2893
|
this.updateState({
|
|
@@ -2848,12 +2897,32 @@ class FLVPlayer extends BasePlayer {
|
|
|
2848
2897
|
});
|
|
2849
2898
|
const now = performance.now();
|
|
2850
2899
|
const isLive = this.config.isLive;
|
|
2900
|
+
const BACKGROUND_PAUSE_THRESHOLD = 500;
|
|
2901
|
+
if (this._lastRafTime > 0 && now - this._lastRafTime > BACKGROUND_PAUSE_THRESHOLD) {
|
|
2902
|
+
const pauseDuration = now - this._lastRafTime;
|
|
2903
|
+
console.log("[FLVPlayer] Background pause detected, duration:", pauseDuration.toFixed(0), "ms, isLive:", isLive, "queue:", this._videoTagQueue.length, "buffer:", this._timedFrameBuffer.length);
|
|
2904
|
+
if (isLive) {
|
|
2905
|
+
const droppedDecoded = this._timedFrameBuffer.length;
|
|
2906
|
+
const droppedQueue = this._videoTagQueue.length;
|
|
2907
|
+
this._timedFrameBuffer = [];
|
|
2908
|
+
this._videoTagQueue = [];
|
|
2909
|
+
this.droppedFrames += droppedDecoded;
|
|
2910
|
+
this.playStartTime = 0;
|
|
2911
|
+
this.firstFrameDts = -1;
|
|
2912
|
+
this.pausedTime = 0;
|
|
2913
|
+
console.log("[FLVPlayer] Live resume: cleared all buffers (decoded:", droppedDecoded, ", queue:", droppedQueue, "), waiting for new data");
|
|
2914
|
+
} else {
|
|
2915
|
+
this.playStartTimeOffset += pauseDuration;
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
this._lastRafTime = now;
|
|
2851
2919
|
if (this._timedFrameBuffer.length > 0 && this.renderer) {
|
|
2852
2920
|
this.consecutiveEmptyBuffer = 0;
|
|
2853
2921
|
if (this.playStartTime <= 0) {
|
|
2854
2922
|
this.firstFrameDts = this._timedFrameBuffer[0].dts;
|
|
2855
2923
|
this.playStartTime = now;
|
|
2856
2924
|
this.playStartTimeOffset = 0;
|
|
2925
|
+
this.pausedTime = 0;
|
|
2857
2926
|
console.log("[FLVPlayer] RenderLoop initialized, firstFrameDts:", this.firstFrameDts, "isLive:", isLive);
|
|
2858
2927
|
}
|
|
2859
2928
|
if (isLive) {
|