@give-tech/ec-player 0.0.1-beta.52 → 0.0.1-beta.54
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 +254 -207
- package/dist/index.js.map +1 -1
- package/dist/player/BasePlayer.d.ts.map +1 -1
- package/dist/player/EcPlayerCore.d.ts.map +1 -1
- package/dist/player/FLVPlayer.d.ts +2 -0
- package/dist/player/FLVPlayer.d.ts.map +1 -1
- package/dist/player/HLSPlayer.d.ts.map +1 -1
- package/dist/renderer/Canvas2DRenderer.d.ts +3 -0
- package/dist/renderer/Canvas2DRenderer.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -253,17 +253,21 @@ class BasePlayer {
|
|
|
253
253
|
};
|
|
254
254
|
this.renderLoop = () => {
|
|
255
255
|
if (!this.isPlaying) return;
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
256
|
+
try {
|
|
257
|
+
this.updateState({
|
|
258
|
+
decoded: this.frameBuffer.length,
|
|
259
|
+
droppedFrames: this.droppedFrames
|
|
260
|
+
});
|
|
261
|
+
if (this.frameBuffer.length > 0 && this.renderer) {
|
|
262
|
+
const frame = this.frameBuffer.shift();
|
|
263
|
+
this.renderer.render(frame);
|
|
264
|
+
this.updateState({ resolution: `${frame.width}x${frame.height}` });
|
|
265
|
+
const fps = this.renderer.updateFps();
|
|
266
|
+
this.updateState({ fps });
|
|
267
|
+
this.callbacks.onFrameRender?.(frame);
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.error("[BasePlayer] Render error:", error);
|
|
267
271
|
}
|
|
268
272
|
this.frameTimer = requestAnimationFrame(this.renderLoop);
|
|
269
273
|
};
|
|
@@ -1846,105 +1850,109 @@ class HLSPlayer extends BasePlayer {
|
|
|
1846
1850
|
this._lastRafTime = 0;
|
|
1847
1851
|
this.renderLoop = (timestamp = 0) => {
|
|
1848
1852
|
if (!this.isPlaying) return;
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1853
|
+
try {
|
|
1854
|
+
const now = performance.now();
|
|
1855
|
+
const BACKGROUND_PAUSE_THRESHOLD = 500;
|
|
1856
|
+
if (this._lastRafTime > 0 && now - this._lastRafTime > BACKGROUND_PAUSE_THRESHOLD) {
|
|
1857
|
+
const pauseDuration = now - this._lastRafTime;
|
|
1858
|
+
console.log("[renderLoop] Background pause detected, duration:", pauseDuration.toFixed(0), "ms");
|
|
1859
|
+
if (this.config.isLive) {
|
|
1860
|
+
const KEEP_FRAMES = 5;
|
|
1861
|
+
if (this.frameBuffer.length > KEEP_FRAMES) {
|
|
1862
|
+
const droppedCount = this.frameBuffer.length - KEEP_FRAMES;
|
|
1863
|
+
this.frameBuffer = this.frameBuffer.slice(-KEEP_FRAMES);
|
|
1864
|
+
this.droppedFrames += droppedCount;
|
|
1865
|
+
console.log("[renderLoop] Live resume: dropped", droppedCount, "old frames, keeping latest", KEEP_FRAMES);
|
|
1866
|
+
}
|
|
1867
|
+
this.playStartTime = now;
|
|
1868
|
+
this.accumulatedMediaTime = 0;
|
|
1869
|
+
} else {
|
|
1870
|
+
this.playStartTime = now - this.accumulatedMediaTime;
|
|
1861
1871
|
}
|
|
1862
|
-
this.playStartTime = now;
|
|
1863
|
-
this.accumulatedMediaTime = 0;
|
|
1864
|
-
} else {
|
|
1865
|
-
this.playStartTime = now - this.accumulatedMediaTime;
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
this._lastRafTime = now;
|
|
1869
|
-
if (!this._lastLogTime || now - this._lastLogTime > 1e3) {
|
|
1870
|
-
this._lastLogTime = now;
|
|
1871
|
-
console.log("[renderLoop] frameBuffer=", this.frameBuffer.length, "renderer=", !!this.renderer);
|
|
1872
|
-
}
|
|
1873
|
-
const downloaded = this.isFMP4 ? this.sampleQueue.length : this.nalQueue.length;
|
|
1874
|
-
const segments = this.isFMP4 ? this.fmp4Segments : this.segments;
|
|
1875
|
-
const totalSegments = segments.length;
|
|
1876
|
-
this.updateState({
|
|
1877
|
-
decoded: this.frameBuffer.length,
|
|
1878
|
-
downloaded,
|
|
1879
|
-
droppedFrames: this.droppedFrames,
|
|
1880
|
-
segmentIndex: this.currentSegmentIndex,
|
|
1881
|
-
totalSegments
|
|
1882
|
-
});
|
|
1883
|
-
if (this.frameBuffer.length > 0 && this.renderer) {
|
|
1884
|
-
const wasBuffering = this.state.isBuffering;
|
|
1885
|
-
if (this.state.isBuffering) {
|
|
1886
|
-
this.updateState({ isBuffering: false, bufferingProgress: void 0 });
|
|
1887
|
-
}
|
|
1888
|
-
this._consecutiveEmptyBuffer = 0;
|
|
1889
|
-
const timescale = this.fmp4Demuxer.getTimescale();
|
|
1890
|
-
if (this.playStartTime === 0) {
|
|
1891
|
-
this.playStartTime = now;
|
|
1892
|
-
this.accumulatedMediaTime = 0;
|
|
1893
|
-
console.log("[renderLoop] Init: timescale=", timescale);
|
|
1894
1872
|
}
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1873
|
+
this._lastRafTime = now;
|
|
1874
|
+
if (!this._lastLogTime || now - this._lastLogTime > 1e3) {
|
|
1875
|
+
this._lastLogTime = now;
|
|
1876
|
+
console.log("[renderLoop] frameBuffer=", this.frameBuffer.length, "renderer=", !!this.renderer);
|
|
1898
1877
|
}
|
|
1899
|
-
const
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1878
|
+
const downloaded = this.isFMP4 ? this.sampleQueue.length : this.nalQueue.length;
|
|
1879
|
+
const segments = this.isFMP4 ? this.fmp4Segments : this.segments;
|
|
1880
|
+
const totalSegments = segments.length;
|
|
1881
|
+
this.updateState({
|
|
1882
|
+
decoded: this.frameBuffer.length,
|
|
1883
|
+
downloaded,
|
|
1884
|
+
droppedFrames: this.droppedFrames,
|
|
1885
|
+
segmentIndex: this.currentSegmentIndex,
|
|
1886
|
+
totalSegments
|
|
1887
|
+
});
|
|
1888
|
+
if (this.frameBuffer.length > 0 && this.renderer) {
|
|
1889
|
+
const wasBuffering = this.state.isBuffering;
|
|
1890
|
+
if (this.state.isBuffering) {
|
|
1891
|
+
this.updateState({ isBuffering: false, bufferingProgress: void 0 });
|
|
1892
|
+
}
|
|
1893
|
+
this._consecutiveEmptyBuffer = 0;
|
|
1894
|
+
const timescale = this.fmp4Demuxer.getTimescale();
|
|
1895
|
+
if (this.playStartTime === 0) {
|
|
1896
|
+
this.playStartTime = now;
|
|
1897
|
+
this.accumulatedMediaTime = 0;
|
|
1898
|
+
console.log("[renderLoop] Init: timescale=", timescale);
|
|
1899
|
+
}
|
|
1900
|
+
if (wasBuffering && this.accumulatedMediaTime > 0) {
|
|
1901
|
+
this.playStartTime = now - this.accumulatedMediaTime / this._playbackRate;
|
|
1902
|
+
console.log("[renderLoop] Buffer recovered, reset playStartTime to continue from", Math.floor(this.accumulatedMediaTime), "ms");
|
|
1903
|
+
}
|
|
1904
|
+
const elapsedWallTime = (now - this.playStartTime) * this._playbackRate;
|
|
1905
|
+
if (Math.floor(elapsedWallTime / 1e3) !== Math.floor((elapsedWallTime - 20 * this._playbackRate) / 1e3)) {
|
|
1906
|
+
console.log("[renderLoop] elapsed=", Math.floor(elapsedWallTime), "accumulated=", Math.floor(this.accumulatedMediaTime), "rate=", this._playbackRate, "frameBuffer=", this.frameBuffer.length);
|
|
1907
|
+
}
|
|
1908
|
+
const getFrameDurationMs = (frame2) => {
|
|
1909
|
+
return frame2.duration ? frame2.duration * 1e3 / timescale : 33.33;
|
|
1910
|
+
};
|
|
1911
|
+
const bufferMs = 50;
|
|
1912
|
+
if (this._playbackRate > 1) {
|
|
1913
|
+
const behindTime = elapsedWallTime - this.accumulatedMediaTime;
|
|
1914
|
+
if (behindTime > 50 && this.frameBuffer.length > 1) {
|
|
1915
|
+
const avgFrameDuration = getFrameDurationMs(this.frameBuffer[0]);
|
|
1916
|
+
const framesToSkip = Math.min(
|
|
1917
|
+
Math.floor(behindTime / avgFrameDuration),
|
|
1918
|
+
this.frameBuffer.length - 1
|
|
1919
|
+
// 保留至少 1 帧
|
|
1920
|
+
);
|
|
1921
|
+
if (framesToSkip > 0) {
|
|
1922
|
+
for (let i = 0; i < framesToSkip; i++) {
|
|
1923
|
+
const skipFrame = this.frameBuffer.shift();
|
|
1924
|
+
const skipDuration = getFrameDurationMs(skipFrame);
|
|
1925
|
+
this.accumulatedMediaTime += skipDuration;
|
|
1926
|
+
}
|
|
1927
|
+
console.log("[renderLoop] Skipped", framesToSkip, "frames at rate", this._playbackRate, ", behind=", Math.floor(behindTime));
|
|
1921
1928
|
}
|
|
1922
|
-
console.log("[renderLoop] Skipped", framesToSkip, "frames at rate", this._playbackRate, ", behind=", Math.floor(behindTime));
|
|
1923
1929
|
}
|
|
1924
1930
|
}
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1931
|
+
const frame = this.frameBuffer[0];
|
|
1932
|
+
const frameDurationMs = getFrameDurationMs(frame);
|
|
1933
|
+
if (elapsedWallTime >= this.accumulatedMediaTime - bufferMs) {
|
|
1934
|
+
this.frameBuffer.shift();
|
|
1935
|
+
this.renderer.render(frame);
|
|
1936
|
+
this.accumulatedMediaTime += frameDurationMs;
|
|
1937
|
+
this.setCurrentTime(this.accumulatedMediaTime);
|
|
1938
|
+
this.updateState({ resolution: `${frame.width}x${frame.height}` });
|
|
1939
|
+
const fps = this.renderer.updateFps();
|
|
1940
|
+
this.updateState({ fps });
|
|
1941
|
+
this.callbacks.onFrameRender?.(frame);
|
|
1942
|
+
}
|
|
1943
|
+
} else {
|
|
1944
|
+
if (this.isPlaying && !this.state.isBuffering) {
|
|
1945
|
+
this._consecutiveEmptyBuffer++;
|
|
1946
|
+
if (this._consecutiveEmptyBuffer >= 15) {
|
|
1947
|
+
this.updateState({ isBuffering: true });
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
if (this.playStartTime === 0) {
|
|
1951
|
+
console.log("[renderLoop] Waiting for frames...");
|
|
1943
1952
|
}
|
|
1944
1953
|
}
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
}
|
|
1954
|
+
} catch (error) {
|
|
1955
|
+
console.error("[HLSPlayer] Render error:", error);
|
|
1948
1956
|
}
|
|
1949
1957
|
this.frameTimer = requestAnimationFrame(this.renderLoop);
|
|
1950
1958
|
};
|
|
@@ -2611,7 +2619,16 @@ class HLSPlayer extends BasePlayer {
|
|
|
2611
2619
|
}
|
|
2612
2620
|
throw error;
|
|
2613
2621
|
}
|
|
2614
|
-
|
|
2622
|
+
let content;
|
|
2623
|
+
try {
|
|
2624
|
+
content = await response.text();
|
|
2625
|
+
} catch (error) {
|
|
2626
|
+
if (error.name === "AbortError") {
|
|
2627
|
+
console.log("[HLSPlayer] Read playlist body aborted");
|
|
2628
|
+
return { isMaster: true, isFMP4: false, segments: [], fmp4Segments: [], variants: 0 };
|
|
2629
|
+
}
|
|
2630
|
+
throw error;
|
|
2631
|
+
}
|
|
2615
2632
|
const lines = content.split("\n");
|
|
2616
2633
|
const segments = [];
|
|
2617
2634
|
const fmp4Segments = [];
|
|
@@ -3370,120 +3387,135 @@ class FLVPlayer extends BasePlayer {
|
|
|
3370
3387
|
this.playStartTimeOffset = 0;
|
|
3371
3388
|
this.resyncCount = 0;
|
|
3372
3389
|
this._lastRafTime = 0;
|
|
3390
|
+
this._prevRenderDts = -1;
|
|
3391
|
+
this._frameIntervalSamples = [];
|
|
3373
3392
|
this.renderLoop = () => {
|
|
3374
3393
|
if (!this.isPlaying) return;
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
this._lastRafTime = now;
|
|
3401
|
-
if (this._timedFrameBuffer.length > 0 && this.renderer) {
|
|
3402
|
-
if (this.state.isBuffering) {
|
|
3403
|
-
this.updateState({ isBuffering: false, bufferingProgress: void 0 });
|
|
3404
|
-
}
|
|
3405
|
-
this.consecutiveEmptyBuffer = 0;
|
|
3406
|
-
if (this.playStartTime <= 0) {
|
|
3407
|
-
this.firstFrameDts = this._timedFrameBuffer[0].dts;
|
|
3408
|
-
this.playStartTime = now;
|
|
3409
|
-
this.playStartTimeOffset = 0;
|
|
3410
|
-
this.pausedTime = 0;
|
|
3411
|
-
console.log("[FLVPlayer] RenderLoop initialized, firstFrameDts:", this.firstFrameDts, "isLive:", isLive);
|
|
3394
|
+
try {
|
|
3395
|
+
this.updateState({
|
|
3396
|
+
decoded: this._timedFrameBuffer.length,
|
|
3397
|
+
downloaded: this._videoTagQueue.length,
|
|
3398
|
+
droppedFrames: this.droppedFrames
|
|
3399
|
+
});
|
|
3400
|
+
const now = performance.now();
|
|
3401
|
+
const isLive = this.config.isLive;
|
|
3402
|
+
const BACKGROUND_PAUSE_THRESHOLD = 500;
|
|
3403
|
+
if (this._lastRafTime > 0 && now - this._lastRafTime > BACKGROUND_PAUSE_THRESHOLD) {
|
|
3404
|
+
const pauseDuration = now - this._lastRafTime;
|
|
3405
|
+
console.log("[FLVPlayer] Background pause detected, duration:", pauseDuration.toFixed(0), "ms, isLive:", isLive, "queue:", this._videoTagQueue.length, "buffer:", this._timedFrameBuffer.length);
|
|
3406
|
+
if (isLive) {
|
|
3407
|
+
const droppedDecoded = this._timedFrameBuffer.length;
|
|
3408
|
+
const droppedQueue = this._videoTagQueue.length;
|
|
3409
|
+
this._timedFrameBuffer = [];
|
|
3410
|
+
this._videoTagQueue = [];
|
|
3411
|
+
this.droppedFrames += droppedDecoded;
|
|
3412
|
+
this.playStartTime = 0;
|
|
3413
|
+
this.firstFrameDts = -1;
|
|
3414
|
+
this.pausedTime = 0;
|
|
3415
|
+
console.log("[FLVPlayer] Live resume: cleared all buffers (decoded:", droppedDecoded, ", queue:", droppedQueue, "), waiting for new data");
|
|
3416
|
+
} else {
|
|
3417
|
+
this.playStartTimeOffset += pauseDuration;
|
|
3418
|
+
}
|
|
3412
3419
|
}
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
this.
|
|
3417
|
-
|
|
3420
|
+
this._lastRafTime = now;
|
|
3421
|
+
if (this._timedFrameBuffer.length > 0 && this.renderer) {
|
|
3422
|
+
if (this.state.isBuffering) {
|
|
3423
|
+
this.updateState({ isBuffering: false, bufferingProgress: void 0 });
|
|
3424
|
+
}
|
|
3425
|
+
this.consecutiveEmptyBuffer = 0;
|
|
3426
|
+
if (this.playStartTime <= 0) {
|
|
3427
|
+
this.firstFrameDts = this._timedFrameBuffer[0].dts;
|
|
3418
3428
|
this.playStartTime = now;
|
|
3429
|
+
this.playStartTimeOffset = 0;
|
|
3419
3430
|
this.pausedTime = 0;
|
|
3420
|
-
console.log("[FLVPlayer]
|
|
3421
|
-
}
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3431
|
+
console.log("[FLVPlayer] RenderLoop initialized, firstFrameDts:", this.firstFrameDts, "isLive:", isLive);
|
|
3432
|
+
}
|
|
3433
|
+
if (isLive) {
|
|
3434
|
+
if (this.lastRenderedDts < 0 && this._timedFrameBuffer.length > 0) {
|
|
3435
|
+
const firstFrame = this._timedFrameBuffer.shift();
|
|
3436
|
+
this.renderFrame(firstFrame, now);
|
|
3437
|
+
this.firstFrameDts = firstFrame.dts;
|
|
3438
|
+
this.playStartTime = now;
|
|
3439
|
+
this.pausedTime = 0;
|
|
3440
|
+
console.log("[FLVPlayer] Live first frame rendered immediately, dts:", firstFrame.dts);
|
|
3441
|
+
} else {
|
|
3442
|
+
const elapsed = (now - this.playStartTime - this.pausedTime) * this._playbackRate;
|
|
3443
|
+
const currentTargetDts = this.firstFrameDts + elapsed;
|
|
3444
|
+
this.setCurrentTime(Math.floor(currentTargetDts));
|
|
3445
|
+
let frameToRender = null;
|
|
3446
|
+
while (this._timedFrameBuffer.length > 0 && this._timedFrameBuffer[0].dts <= currentTargetDts) {
|
|
3447
|
+
if (frameToRender) {
|
|
3448
|
+
this.droppedFrames++;
|
|
3449
|
+
}
|
|
3450
|
+
frameToRender = this._timedFrameBuffer.shift();
|
|
3451
|
+
}
|
|
3427
3452
|
if (frameToRender) {
|
|
3428
|
-
this.
|
|
3453
|
+
this.renderFrame(frameToRender, now);
|
|
3429
3454
|
}
|
|
3430
|
-
frameToRender = this._timedFrameBuffer.shift();
|
|
3431
3455
|
}
|
|
3432
|
-
|
|
3456
|
+
} else {
|
|
3457
|
+
const nextFrame = this._timedFrameBuffer[0];
|
|
3458
|
+
const elapsed = (now - this.playStartTime - this.playStartTimeOffset) * this._playbackRate;
|
|
3459
|
+
const currentTargetPts = this.firstFrameDts + elapsed;
|
|
3460
|
+
if (this.lastRenderedDts >= 0) {
|
|
3461
|
+
this.setCurrentTime(Math.floor(this.lastRenderedDts));
|
|
3462
|
+
}
|
|
3463
|
+
if (nextFrame.dts <= currentTargetPts) {
|
|
3464
|
+
const frameToRender = this._timedFrameBuffer.shift();
|
|
3433
3465
|
this.renderFrame(frameToRender, now);
|
|
3466
|
+
this.lastRenderedDts = frameToRender.dts;
|
|
3467
|
+
if (this._prevRenderDts >= 0) {
|
|
3468
|
+
this._frameIntervalSamples.push(frameToRender.dts - this._prevRenderDts);
|
|
3469
|
+
if (this._frameIntervalSamples.length >= 30) {
|
|
3470
|
+
const avg = this._frameIntervalSamples.reduce((a, b) => a + b, 0) / this._frameIntervalSamples.length;
|
|
3471
|
+
console.log("[FLVPlayer] 帧率诊断: 平均帧间隔", avg.toFixed(1), "ms, 约", (1e3 / avg).toFixed(1), "fps, 样本数:", this._frameIntervalSamples.length);
|
|
3472
|
+
this._frameIntervalSamples = [];
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
this._prevRenderDts = frameToRender.dts;
|
|
3476
|
+
const lag = currentTargetPts - frameToRender.dts;
|
|
3477
|
+
if (lag > 1e3) {
|
|
3478
|
+
this.playStartTime = now;
|
|
3479
|
+
this.playStartTimeOffset = 0;
|
|
3480
|
+
this.firstFrameDts = frameToRender.dts;
|
|
3481
|
+
this.resyncCount++;
|
|
3482
|
+
if (this.resyncCount <= 3) {
|
|
3483
|
+
console.log("[FLVPlayer] Major resync, lag:", Math.floor(lag), "ms");
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3434
3486
|
}
|
|
3435
3487
|
}
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3488
|
+
if (now - this.lastRenderLogTime > 5e3) {
|
|
3489
|
+
console.log("[FLVPlayer] RenderLoop stats:", {
|
|
3490
|
+
renderedFrames: this.renderedFrames,
|
|
3491
|
+
droppedFrames: this.droppedFrames,
|
|
3492
|
+
decoded: this._timedFrameBuffer.length,
|
|
3493
|
+
downloaded: this._videoTagQueue.length,
|
|
3494
|
+
mode: isLive ? "live" : "vod"
|
|
3495
|
+
});
|
|
3496
|
+
this.lastRenderLogTime = now;
|
|
3442
3497
|
}
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
this.
|
|
3446
|
-
|
|
3447
|
-
const lag = currentTargetPts - frameToRender.dts;
|
|
3448
|
-
if (lag > 1e3) {
|
|
3449
|
-
this.playStartTime = now;
|
|
3450
|
-
this.playStartTimeOffset = 0;
|
|
3451
|
-
this.firstFrameDts = frameToRender.dts;
|
|
3452
|
-
this.resyncCount++;
|
|
3453
|
-
if (this.resyncCount <= 3) {
|
|
3454
|
-
console.log("[FLVPlayer] Major resync, lag:", Math.floor(lag), "ms");
|
|
3455
|
-
}
|
|
3498
|
+
} else {
|
|
3499
|
+
if (this.isPlaying && !this.state.isBuffering) {
|
|
3500
|
+
if (this.consecutiveEmptyBuffer >= 15) {
|
|
3501
|
+
this.updateState({ isBuffering: true });
|
|
3456
3502
|
}
|
|
3457
3503
|
}
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
this.
|
|
3468
|
-
|
|
3469
|
-
} else {
|
|
3470
|
-
if (this.isPlaying && !this.state.isBuffering) {
|
|
3471
|
-
if (this.consecutiveEmptyBuffer >= 15) {
|
|
3472
|
-
this.updateState({ isBuffering: true });
|
|
3504
|
+
if (this.consecutiveEmptyBuffer === 0 && this.lastRenderedDts >= 0) {
|
|
3505
|
+
this.bufferEmptyStartTime = now;
|
|
3506
|
+
}
|
|
3507
|
+
this.consecutiveEmptyBuffer++;
|
|
3508
|
+
if (isLive && this.bufferEmptyStartTime > 0) {
|
|
3509
|
+
this.pausedTime = now - this.bufferEmptyStartTime;
|
|
3510
|
+
}
|
|
3511
|
+
if (this.consecutiveEmptyBuffer === 1) {
|
|
3512
|
+
console.warn("[FLVPlayer] Buffer empty, waiting for frames... queue:", this._videoTagQueue.length);
|
|
3513
|
+
} else if (this.consecutiveEmptyBuffer % 60 === 0) {
|
|
3514
|
+
console.warn("[FLVPlayer] Buffer still empty after", Math.floor(this.consecutiveEmptyBuffer / 60), "seconds");
|
|
3473
3515
|
}
|
|
3474
3516
|
}
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
}
|
|
3478
|
-
this.consecutiveEmptyBuffer++;
|
|
3479
|
-
if (isLive && this.bufferEmptyStartTime > 0) {
|
|
3480
|
-
this.pausedTime = now - this.bufferEmptyStartTime;
|
|
3481
|
-
}
|
|
3482
|
-
if (this.consecutiveEmptyBuffer === 1) {
|
|
3483
|
-
console.warn("[FLVPlayer] Buffer empty, waiting for frames... queue:", this._videoTagQueue.length);
|
|
3484
|
-
} else if (this.consecutiveEmptyBuffer % 60 === 0) {
|
|
3485
|
-
console.warn("[FLVPlayer] Buffer still empty after", Math.floor(this.consecutiveEmptyBuffer / 60), "seconds");
|
|
3486
|
-
}
|
|
3517
|
+
} catch (error) {
|
|
3518
|
+
console.error("[FLVPlayer] Render error:", error);
|
|
3487
3519
|
}
|
|
3488
3520
|
this.frameTimer = requestAnimationFrame(this.renderLoop);
|
|
3489
3521
|
};
|
|
@@ -3704,6 +3736,8 @@ class FLVPlayer extends BasePlayer {
|
|
|
3704
3736
|
this.playStartTime = performance.now();
|
|
3705
3737
|
this.playStartTimeOffset = 0;
|
|
3706
3738
|
this._lastRafTime = 0;
|
|
3739
|
+
this._prevRenderDts = -1;
|
|
3740
|
+
this._frameIntervalSamples = [];
|
|
3707
3741
|
console.log("[FLVPlayer] Play time initialized, firstFrameDts:", this.firstFrameDts);
|
|
3708
3742
|
}
|
|
3709
3743
|
this.isPlaying = true;
|
|
@@ -4303,7 +4337,7 @@ class FLVPlayer extends BasePlayer {
|
|
|
4303
4337
|
* 渲染一帧
|
|
4304
4338
|
*/
|
|
4305
4339
|
renderFrame(frame, now) {
|
|
4306
|
-
if (!this.renderer) return;
|
|
4340
|
+
if (!this.renderer || !frame.frame) return;
|
|
4307
4341
|
this.renderer.render(frame.frame);
|
|
4308
4342
|
this.renderedFrames++;
|
|
4309
4343
|
this.lastRenderedDts = frame.dts;
|
|
@@ -4378,6 +4412,9 @@ class Canvas2DRenderer {
|
|
|
4378
4412
|
this.frameCount = 0;
|
|
4379
4413
|
this.lastFpsUpdate = performance.now();
|
|
4380
4414
|
this.lastFps = 0;
|
|
4415
|
+
this.cachedImageData = null;
|
|
4416
|
+
this.cachedWidth = 0;
|
|
4417
|
+
this.cachedHeight = 0;
|
|
4381
4418
|
this.canvas = canvas;
|
|
4382
4419
|
this.ctx = canvas.getContext("2d");
|
|
4383
4420
|
if (!this.ctx) {
|
|
@@ -4392,12 +4429,18 @@ class Canvas2DRenderer {
|
|
|
4392
4429
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
4393
4430
|
}
|
|
4394
4431
|
render(frame) {
|
|
4432
|
+
if (!frame || !frame.data) return;
|
|
4395
4433
|
const { width, height, data } = frame;
|
|
4396
4434
|
if (this.canvas.width !== width || this.canvas.height !== height) {
|
|
4397
4435
|
this.canvas.width = width;
|
|
4398
4436
|
this.canvas.height = height;
|
|
4437
|
+
this.cachedImageData = this.ctx.createImageData(width, height);
|
|
4438
|
+
this.cachedWidth = width;
|
|
4439
|
+
this.cachedHeight = height;
|
|
4399
4440
|
}
|
|
4400
|
-
const imageData =
|
|
4441
|
+
const imageData = this.cachedImageData;
|
|
4442
|
+
const dest = imageData.data;
|
|
4443
|
+
dest.set(data);
|
|
4401
4444
|
this.ctx.putImageData(imageData, 0, 0);
|
|
4402
4445
|
}
|
|
4403
4446
|
updateFps() {
|
|
@@ -4467,6 +4510,10 @@ class EcPlayerCore {
|
|
|
4467
4510
|
await this.player.finishInitDecoder(initResult);
|
|
4468
4511
|
this.callbacks.onStateChange?.({ isLoaded: true });
|
|
4469
4512
|
} catch (error) {
|
|
4513
|
+
if (error?.name === "AbortError") {
|
|
4514
|
+
console.log("[EcPlayerCore] Load aborted (expected)");
|
|
4515
|
+
return;
|
|
4516
|
+
}
|
|
4470
4517
|
console.error("[EcPlayerCore] Load failed:", error);
|
|
4471
4518
|
this.callbacks.onError?.(error);
|
|
4472
4519
|
throw error;
|
|
@@ -4810,12 +4857,12 @@ const _EnvDetector = class _EnvDetector {
|
|
|
4810
4857
|
// function index: 0
|
|
4811
4858
|
// Code section (10)
|
|
4812
4859
|
10,
|
|
4813
|
-
|
|
4860
|
+
23,
|
|
4814
4861
|
1,
|
|
4815
|
-
// section id=10, size=
|
|
4816
|
-
|
|
4862
|
+
// section id=10, size=23, 1 func
|
|
4863
|
+
21,
|
|
4817
4864
|
0,
|
|
4818
|
-
// func body size=
|
|
4865
|
+
// func body size=21, 0 locals
|
|
4819
4866
|
// v128.const i32x4 0 0 0 0
|
|
4820
4867
|
253,
|
|
4821
4868
|
12,
|