diff --git a/src/controller/audio-stream-controller.ts b/src/controller/audio-stream-controller.ts index 77232e5858b..02135f6f271 100644 --- a/src/controller/audio-stream-controller.ts +++ b/src/controller/audio-stream-controller.ts @@ -311,13 +311,7 @@ class AudioStreamController } protected onTickEnd() { - const { media } = this; - if (!media?.readyState) { - // Exit early if we don't have media or if the media hasn't buffered anything yet (readyState 0) - return; - } - - this.lastCurrentTime = media.currentTime; + super.onTickEnd(); this.checkFragmentChanged(); } diff --git a/src/controller/base-stream-controller.ts b/src/controller/base-stream-controller.ts index f5f83baba83..934b8d007b9 100644 --- a/src/controller/base-stream-controller.ts +++ b/src/controller/base-stream-controller.ts @@ -166,11 +166,14 @@ export default class BaseStreamController hls.off(Events.ERROR, this.onError, this); } - protected doTick() { - this.onTickEnd(); - } + protected doTick() {} - protected onTickEnd() {} + protected onTickEnd() { + const { media } = this; + if (media?.readyState && media.seeking === false) { + this.lastCurrentTime = media.currentTime; + } + } public startLoad(startPosition: number): void {} @@ -380,7 +383,7 @@ export default class BaseStreamController this.levelLastLoaded = this.fragCurrent = null; - this.lastCurrentTime = 0; + this.lastCurrentTime = this.nextLoadPosition = 0; this.startPosition = -1; this.startFragRequested = false; } @@ -477,6 +480,8 @@ export default class BaseStreamController this.nextLoadPosition = currentTime; if (bufferEmpty) { this.startPosition = currentTime; + } else { + this.lastCurrentTime = currentTime; } } @@ -489,7 +494,7 @@ export default class BaseStreamController protected onMediaEnded = () => { // reset startPosition and lastCurrentTime to restart playback @ stream beginning this.log(`setting startPosition to 0 because media ended`); - this.startPosition = this.lastCurrentTime = 0; + this.startPosition = this.nextLoadPosition = this.lastCurrentTime = 0; }; protected onManifestLoaded( diff --git a/src/controller/stream-controller.ts b/src/controller/stream-controller.ts index 61b031f5bf9..51bbc237900 100644 --- a/src/controller/stream-controller.ts +++ b/src/controller/stream-controller.ts @@ -236,9 +236,6 @@ export default class StreamController protected onTickEnd() { super.onTickEnd(); - if (this.media?.readyState && this.media.seeking === false) { - this.lastCurrentTime = this.media.currentTime; - } this.checkFragmentChanged(); } diff --git a/tests/unit/controller/base-stream-controller.ts b/tests/unit/controller/base-stream-controller.ts index e476f87fdc7..c2db2909f31 100644 --- a/tests/unit/controller/base-stream-controller.ts +++ b/tests/unit/controller/base-stream-controller.ts @@ -164,7 +164,13 @@ describe('BaseStreamController', function () { describe('Seeking Logic', function () { describe('onMediaSeeking', function () { - it('should handle backward seek behavior correctly', function () { + it('should handle backward seek after media buffered behavior correctly', function () { + // Mock hasEnoughToStart to return false: + // lastCurrentTime is updated with startPosition and nextLoadPosition before start + const hasEnoughToStartStub = sinon + .stub(baseStreamController.hls, 'hasEnoughToStart') + .get(() => true); + // Setup initial state for backward seek baseStreamController.lastCurrentTime = 10.5; media.currentTime = 5.2; @@ -172,6 +178,8 @@ describe('BaseStreamController', function () { // Call onMediaSeeking - should execute without error expect(() => baseStreamController.onMediaSeeking()).to.not.throw(); + hasEnoughToStartStub.restore(); + // Verify lastCurrentTime is not updated on backward seek expect(baseStreamController.lastCurrentTime).to.equal(10.5); });