video.js
video.js copied to clipboard
Safest way to switch/resume video player sources?
Description
I have written a menu button component that allows users to switch video sources. If the video was playing while sources are switched, it will resume playback at the time it was at when the source was switched. I was wondering what the safest/recommended way was to do this. I attached a snippet of what I basically have now below.
let currentTime = player.currentTime();
let paused = player.paused();
player.one("loadedmetadata", function() {
player.hasStarted(true);
player.currentTime(currentTime);
if(!paused) player.play();
});
player.src({ src, type: "video/mp4" }); //src is defined earlier through a function argument
I essentially just set an event listener to resume playback at the current time was metadata is loaded on the new source. However, I am weary how solid this solution is. I wonder if it's ok that errors are fired when the player.play()
is interrupted when sources are switched again before the player.play()
was able to be fulfilled. Brightcove's playlist plugin looks like it does something similar to what I am doing. Except it uses player.ready()
and silences errors from player.play()
. Is player.ready()
better to use that loadedmetadata
?
My apologies as this isn't really an issue, but more so just a discussion/advice seeking. I hope I am still able to get some advice though!
you shouldn't need to set hasStarted
manually, also, loadedmetadata
definitely works, though, depending on the preload settings (default browser or current player) things may not preload, so, ready
might be better across browsers and preload settings.
You don't have to silence the promise on the play, but it may log a warning.
videojs-playlist has been around for a long time and used in production in many places, including Brightcove's player. Definitely good to emulate it.
@gkatsev I set hasStarted
just in case the video was paused while source switching. That way the video doesn't stay displayed with the poster/big play button but displays as if the video was already paused at the current time. Or is there something else I am not understanding?
Hm interesting, I want to switch to ready
to emulate Brightcove's plugin, but I still don't fully understand how ready
works. If I were to use ready
instead of player.one('loadedmetadata')
, would multiple callbacks added by ready
be fired? My understanding from the docs is that a new callback would be added each time, but I wouldn't want to add a new callback on ready
if I haven't removed the old one first. Does that make sense?
Ah I see, I will keep that in mind. Thank you!
Or is there something else I am not understanding?
I don't think setting hasStarted
manually is necessarily going to be harmful. I would say that you'd probably want to dig into what the potential side effects of that are. I had the same immediate reaction that Gary did.
If I were to use ready instead of player.one('loadedmetadata'), would multiple callbacks added by ready be fired?
Using player.ready()
essentially means you're waiting for tech selection to finish.
- When you use
.ready(cb)
with a callback, it will call it once. - If you use
on('ready', cb)
, it will call the callback each time the player is "ready".
@misteroneill Thank you so much for the advice! Ah interesting. So are you saying there is a better way to remove the big play button and display the player controls? Or are you saying you just wouldn't do that at all and lean away from messing with hasStarted
?
Oh that is great! I didn't know .ready(cb)
worked like that. I think it would be a nice addition if that was mentioned in the docs for the player. I'm not sure if it's just me, but I wouldn't have guessed it would only be called once based on the description in the docs.
Also, when exactly does ready
fire? I tried searching around the docs/guides but didn't have much luck. When ready
is fired, is it safe to seek with currentTime
? Based on the brightcove playlist plugin I linked earlier, it looks like it is safe to at least play
. In addition, it looks like the brightcove player docs say a different opinion:
Note that using ready() functions correctly if you wish to interact with the player, for instance programmatically add a plugin. If you wish to immediately interact with the video, for instance use play(), the code snippet above will not always work correctly. A better approach would be to listen for the loadedmetadata to interact with the video, for instance:
Is the brightcove player different from this library? The code in the docs references videojs
so I am not sure if it applies to the same player in this repository.
Again, thank you so much for answering my questions!
The docs do say that for ready()
, but maybe in a not too clear way:
Bind a listener to the component's ready state. Different from event listeners in that if the ready event has already happened it will trigger the function immediately.
Video.js will handle waiting on seeking for you if the video hasn't loaded yet. Some internals of Video.js are asynchronous, so, ready fires when those are available for usage, which can be earlier than when the video has started loading.
@gkatsev Ah I see a bit what you are saying with the ready()
docs. At least for me, I read that assuming it will fire immediately, but will still hang around after to fire again on another ready event. However if it's just a me thing then at least it's clarified for me now!
Good to know that seeking will wait if the video hasn't loaded yet! I'm assuming if that is handled, currentTime()
will also handle itself if a seek request is interuppted? Like if a new source is loaded, a new time is seeked, or if the video is paused? Stuff like that?
And lastly, just to make sure I am getting you right, ready
fires when when those internals are available for usage, which can be earlier than the video starting to load. Is that before even metadata has loaded? It sounds like ready
is more dependent on when the player is ready as opposed the video itself, if I am getting that right.