Skip to content

Conversation

@lukasstorck
Copy link
Contributor

@lukasstorck lukasstorck commented Dec 11, 2025

Pull Request Type

  • Bugfix
  • Feature Implementation
  • Documentation
  • Other

Related issue

closes #8226

Description

Fixes a bug with the playback rate jumping to a wrong value (bug described in issue #8226).

Bug details

When loading the video player the playback rate is set based on a prop, which keeps track of the playback rate of a session. So, when playing the next video in a playlist or selecting a video from the next videos section, this correctly allows for keeping the same playback rate. While the current playback rate is set correctly, it is also used to set the default playback rate:

// ft-shaka-video-player.js line 2576-2577
videoElement.playbackRate = props.currentPlaybackRate
videoElement.defaultPlaybackRate = props.currentPlaybackRate

Now, when we set the playback rate back to the actual default playback rate, we correctly stop the trick play mode, but this makes the video player use the default playback rate of the video element, which is wrong from the snippet above, and propagate the value through an event update (update value in UI).

// ft-shaka-video-player.js line 1955-1970
function changePlayBackRate(step) {
  const newPlaybackRateString = (player.getPlaybackRate() + step).toFixed(2)
  const newPlaybackRate = parseFloat(newPlaybackRateString)

  // The following error is thrown if you go below 0.07:
  // The provided playback rate (0.05) is not in the supported playback range.
  if (newPlaybackRate > 0.07 && newPlaybackRate <= maxVideoPlaybackRate.value) {
    if (newPlaybackRate === defaultPlaybackRate.value) {
      player.cancelTrickPlay()
    } else {
      player.trickPlay(newPlaybackRate, false)
    }

    showValueChange(`${newPlaybackRateString}x`)
  }
}

So we can fix this first bug by correctly providing the default playback rate, but this introduces a second bug where after loading the video player, the current playback rate is always reset to the default playback rate. This is because the current playback rate value of the video element is reset to the default value set when the video element is attached to the local player.

// ft-shaka-video-player.js line 2576-2590
videoElement.playbackRate = props.currentPlaybackRate
videoElement.defaultPlaybackRate = props.currentPlaybackRate

const localPlayer = new shaka.Player()

ui = new shaka.ui.Overlay(
  localPlayer,
  container.value,
  videoElement,
  vrCanvas.value
)

await localPlayer.attach(videoElement)  // videoElement.playbackRate is reset to default value

I assume this is the reason, why the default playback rate of the video element was set to the current playback rate and not the actual default playback rate.

Fixes

  • set the default playback rate value of the video element (videoElement.defaultPlaybackRate) based on global default playback rate (defaultPlaybackRate.value) instead of the current playback rate (props.currentPlaybackRate)
  • set the playback rate and default playback rate on the video element after calling .attach() to avoid the playback rate to be overridden
    • an alternative solution would be to figure out why shaka player resets the value and maybe fix it there

Testing

  1. open a video
  2. set a non-default playback rate
  3. select a new video
    • either via "Up next" section
    • or via a playlist
  4. set playback rate back to the default playback rate (not via the UI button)
    • either via ctrl+mouse scroll
    • or via hotkeys P or O
  5. when reaching the default playback rate, is now should not jump to the playback rate of the previous video, but correctly stay at the default playback rate

Desktop

  • OS: Ubuntu
  • OS Version: 24.04.2 LTS
  • FreeTube version: v0.23.12 Beta

Additional context

The reason why await localPlayer.attach(videoElement) resets the playback rate to the default value of the video element is, that the source of the HTMLMediaElement is changed, which resets the playback rate to the set default value.
Excerpts from the code of the shaka video player:

// lib/player.js
async attach(mediaElement, initializeMediaSource = true) {
  // ...
  this.video_ = mediaElement;
  // ...
  await this.initializeMediaSourceEngineInner_();  // <-- calls
}

// ...

async initializeMediaSourceEngineInner_() {
  // ...
  const mediaSourceEngine = this.createMediaSourceEngine(  // <-- calls
    this.video_,
    // ...
  )
  // ...
}

// ...

createMediaSourceEngine(mediaElement, textDisplayer, playerInterface,
                        lcevcDec, config) {
  return new shaka.media.MediaSourceEngine(  // <-- calls
    mediaElement,
    textDisplayer,
    playerInterface,
    config,
    lcevcDec);
  }
// lib/media/media_source_engine.js
shaka.media.MediaSourceEngine = class {
  constructor(video, textDisplayer, playerInterface, config, lcevcDec) {
    // ...
    this.mediaSource_ = this.createMediaSource(this.mediaSourceOpen_);  // <-- calls
    // ...
  }
  
  createMediaSource(p) {
    // ...
    // Store the object URL for releasing it later.
    this.url_ = shaka.media.MediaSourceEngine.createObjectURL(mediaSource);
    if (this.config_.useSourceElements) {
      this.video_.removeAttribute('src');
      if (this.source_) {
        this.video_.removeChild(this.source_);
      }
      if (this.secondarySource_) {
        this.video_.removeChild(this.secondarySource_);
      }
      this.source_ = shaka.util.Dom.createSourceElement(this.url_);
      this.video_.appendChild(this.source_);  // source replace here
      if (this.secondarySource_) {
        this.video_.appendChild(this.secondarySource_);
      }
      this.video_.load();
    } else {
      this.video_.src = this.url_;  // or source attribute replaced here
    }
  }
}

This is therefore not directly the fault of the shaka player, but based on the HTMLMediaElement. This can also be tested with this example:

const video = document.createElement("video");
video.playbackRate = 2;
video.src = "media/video.mp4"; // some valid url
video.playbackRate; // now reset to 1

Alternatively to the second commit, this could be fixed in the shaka player by storing the playback rate before setting a new (or first) source. But the fix would work either way.

@lukasstorck lukasstorck marked this pull request as ready for review December 11, 2025 17:44
@FreeTubeBot FreeTubeBot enabled auto-merge (squash) December 11, 2025 17:44
@github-actions github-actions bot added the PR: waiting for review For PRs that are complete, tested, and ready for review label Dec 11, 2025
@lukasstorck lukasstorck changed the title fix/video player playback rate fix: video player playback rate Dec 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: waiting for review For PRs that are complete, tested, and ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Playback speed incorrectly increases if keyboard shortcuts are being used

1 participant