fix: Emit ratechange event when playbackRate stays at 0 (#10130)

This can happen when using playbackRate values ​​that are outside the
range supported by the platform, and we have to use our internal
alternative. See the implementation at
https://github.com/shaka-project/shaka-player/blob/main/lib/media/play_rate_controller.js
This commit is contained in:
Álvaro Velad Galván
2026-05-27 14:19:32 +02:00
committed by Álvaro Velad Galván
parent 8d81f7fcb1
commit 2c6a2ee87d
2 changed files with 58 additions and 0 deletions
+18
View File
@@ -5275,6 +5275,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
return;
}
const prevRealRate = this.video_.playbackRate;
const prevRate = this.playRateController_.getRealRate();
this.playRateController_.set(rate);
if (this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE) {
@@ -5282,6 +5285,21 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
this.useTrickPlayTrackIfAvailable(useTrickPlayTrack && rate != 1);
}
this.setupTrickPlayEventListeners_(rate);
// When trick play falls back to manual seeking (for example, rewind or
// unsupported playback rates), PlayRateController keeps the media element
// playbackRate at 0 and advances the playhead with a timer instead.
//
// Because the underlying media element playbackRate remains unchanged,
// the browser will not fire a native 'ratechange' event in this case.
//
// Invoke onRateChange_() manually so the player can run the normal
// playback-rate update flow and dispatch the corresponding RateChange
// event for the effective trick play rate change.
if (prevRealRate === 0 && this.video_.playbackRate === 0 &&
rate != prevRate) {
this.onRateChange_();
}
}
/**
+40
View File
@@ -351,6 +351,46 @@ describe('Player', () => {
expect(video.playbackRate).toBe(1);
});
it('dispatch ratechange event', async () => {
await player.load('test:sintel_compiled');
await video.play();
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 1, 10);
video.pause();
let numberOfRatechangeEvents = 0;
eventManager.listen(player, 'ratechange', () => {
numberOfRatechangeEvents++;
});
player.trickPlay(2);
expect(player.getPlaybackRate()).toBe(2);
await shaka.test.Util.shortDelay();
player.trickPlay(32);
expect(player.getPlaybackRate()).toBe(32);
await shaka.test.Util.shortDelay();
player.trickPlay(64);
expect(player.getPlaybackRate()).toBe(64);
await shaka.test.Util.shortDelay();
player.trickPlay(-1);
expect(player.getPlaybackRate()).toBe(-1);
await shaka.test.Util.shortDelay();
player.cancelTrickPlay();
expect(player.getPlaybackRate()).toBe(1);
await shaka.test.Util.shortDelay();
expect(numberOfRatechangeEvents).toBe(5);
});
it('in sequence mode', async () => {
if (!deviceDetected.supportsSequenceMode()) {
pending('Sequence mode is not supported by the platform.');