Switching sources after html5 video has ended - javascript

I know this should be straight forward if I just use the onended event, like this:
<video src="video.ogv" id="myVideo">
</video>
<script type='text/javascript'>
document.getElementById('myVideo').addEventListener('ended',myHandler,false);
function myHandler(e) {
// What you want to do after the event
}
</script>
however, the ended event is firing the evn after I switch tracks. I am using the videojs player.
In this particular case, I'm looking play Clip B, and the moment it finishes, switch to Clip C.
Here is what my code looks like:
// clipA is currently playing......
// User hits a button which calls switchVideo();
// video = videojs('video-player');
var switchVideo = function (clipB, clipC) {
video.src(clipB);
video.play();
// When clipB has ended...
video.ended(function () {
console.log("clipB ended");
video.src(clipC);
video.play();
})
};
The moment I call this function, I can see that it jumps to clipB for a moment, then the onended event is fired, and then the video feed jumps to clipC. How can I ignore the first onended event for clipA, and instead only listen for clipB?
UPDATE: Here is what the final correct answer looks like:
// video = videojs('video-player');
var switchVideo = function (clipB, clipC) {
video.src(clipB);
video.play();
// When clipB has ended...
video.one('ended', function () {
console.log("clibB ended");
video.src(clipC);
video.play();
})
};
UPDATE 2: I discovered that the above code will only work once. (It is called 'one' afterall. In my particular case, I needed to be able to do this several times.
I made one small change, and that was to use video.on('ended', myfunction) instead of video.one('ended', myFunction). Now I can call it as many times as needed.
I came to this conclusion after reading this Stack Overflow response.
"addEvent" and "removeEvent" were replaced by "on" and "off" per the videojs API. - #Lyn Headley
Final solution:
// video = videojs('video-player');
var switchVideo = function (clipB, clipC) {
video.src(clipB);
video.play();
// When clipB has ended...
video.on('ended', function () {
console.log("clibB ended");
video.src(clipC);
video.play();
})
};

For readers: asker is using a library called videojs, which envelops the native JS-<video> interface. Of which, it provides event functions: on, off, one.
The original code created a new event listener for every time the switchVideo was called, causing the ended callback to be called multiple times.
The solution is to use one('ended') instead of ended [equivalent to on('ended')]:
video.one('ended', function () {
whatever;
});

Related

JavaScript pass parameters error, the click event pass all parameters in the pass

I am making a game by js and pixi.js, I am having a trouble in pass parameters for function. The code below
newGame()
{
// Some code before, then I get the audio which I want to play
let audio = this.soundsArray[this.shuffleQuestionsInLevel[this.rightAnswer].sound];
// Auto play audio at the begining of game
this.playSound(audio);
// Click to repeat the sound
this.soundBtn.on('pointerdown', this.playSound.bind(this, audio));
}
// Play audio after 5 seconds
playSound(audio)
{
setTimeout(() => audio.play(), 5000);
}
At the first game, everything works perfectly, the exactly sound be played. However, from the second game, the click event this.soundBtn.on('pointerdown', this.playSound.bind(this, audio)); play all the sound in the pass, it mean in the 2nd game, there are 2 sounds be played, in the 3rd game, there are 3 sounds be played.
The code to auto play audio at the begining this.playSound(audio) work well every time. Only sound in this game be played.
I do not know why I call the same function and pass the same parameter but only the code to auto play audio work. I want the click event work exactly like that. Anyone know what's the problem? Thank you.
It looks like you are attaching the event handler when you start a game (when you call newGame(), but you are never detaching it:
// This line attaches the handler, but it attaches a new handler everytime!
this.soundBtn.on('pointerdown', this.playSound.bind(this, audio));
// To understand why, let's write it more explicitly
//
// First a new "listener" function is created from this.playSound by calling bind
const listener = this.playSound.bind(this, audio);
// Then this function is attached as an event handler
this.soundBtn.on('pointerdown', listener);
// But since listener is not the same function as this.playSound anymore
// (because .bind produces a new function) the following line will not work
// and listener will stay attached
this.soundBtn.off('pointerdown', this.playSound);
In order to fix the problem you will most probably need to store the listener function somewhere so that you can detach it later:
newGame() {
// ...
this.__playAudio = this.playAudio.bind(this, audio);
this.soundBtn.on('pointerdown', this.__playAudio);
}
// And then when the game is over
this.soundBtn.off('pointerdown', this.__playAudio);
Or, if the soundBtn supports it, just detach all the pointerdown handlers when a game is over:
this.soundBtn.off('pointerdown');

How to correctly use events relating to loading of audio

I'm making an audio player and trying to add functionality such that when any audio element (there are multiple coordinated tracks) hasn't loaded enough data to keep playing, the Play button turns into a loading Button and other audio elements are paused, etc. etc. Then when audio has loaded sufficiently to play, play resumes.
For the latter point, it seems clear enough that there are two relevant event handlers: canplay and canplaythrough, and I'm clear on the distinction between them.
However, for the former part, the loading, I'm confused which event I should use for my functionality. These all seem to be relevant:
stalled
suspend
waiting
The W3Schools reference would suggest that waiting is my go-to. (Even though Mozilla reference has a completely different description of it). But if that's the case, do I need to use any of the others as well, in specific edge cases? If one of those others fires, does waiting fire as well?
Then there's also playing - should I use that to trigger resuming audio after loading event?
Perhaps someone could provide the barebones code for implementing what I want?
Edit
Here is some code showing my current setup. As you can see, I'm hedging my bets as to which event might be fired once buffering has completed.
// main Player object
function Player() {
this.playing = false;
this.waitForLoad = false;
this.audio = new Audio(filepath);
this.narration.preload = "auto";
this.narration.addEventListener('canplaythrough', () => { this.loaded(); });
this.narration.addEventListener('waiting', () => { this.audioWaiting(); });
this.narration.addEventListener('playing', () => { this.audioUnwaiting(); });
}
Player.prototype =
{
play: function() {
this.playing = true;
this.narration.play();
playButton.addClass('pause');
},
pause: function() {
this.playing = false;
this.narration.pause();
playButton.removeClass('pause');
},
// for onLoaded event
loaded: function() {
playButton.removeClass('loading'); // playButton starts off with class="play loading"
if (this.playing && this.waitForLoad) {
this.audioUnwaiting();
}
},
// if any element that needs to play now hasn't loaded (or onWaiting events)
audioWaiting: function() {
if (!this.waitForLoad) {
this.waitForLoad = true;
this.pause();
playButton.addClass('loading');
}
},
// onPlaying - once an element that needs to play now has loaded
audioUnwaiting: function() {
if (this.waitForLoad) {
this.waitForLoad = false;
playButton.removeClass('loading');
if (this.playing) this.play();
}
},
};
seems you may be trying to access some read-only properties for your project. changing icons based on status is no problem with libraries like jquery.
I think 'buffered' is the property that you really want:
How do I make a loading bar for an HTML5 audio element?
https://developer.mozilla.org/en-US/Apps/Fundamentals/Audio_and_video_delivery/buffering_seeking_time_ranges
I think seeing how much has loaded and how far you are already playing in your audio file should be enough information to change the icons and get the functionality you're looking for
I think i need to see what it is you are building to really understand. Do you have a link or something?

Javascript function not fired on video timeupdate

Currently working on a page containing a video that has to be paused at certain points (like chapters). So I made a function that will stop the video when it hits the next "time marker" which looks like this:
function vidPause(nextMarker){
var timeMarker = nextMarker;
if(videoPlayer.currentTime >= timeMarker) {
videoPlayer.pause();
videoPlayer.removeEventListener('timeupdate', vidPause());
}
};
And I'm trying to fire it this way:
videoPlayer.addEventListener('timeupdate', vidPause(nextMarker));
But it only seems to fire when the video is loaded. Nothing happens when the video is playing (tested by using a console.log(videoPlayer.currentTime); inside the vidPause function).
Note: I need the function to be called that way so that I can remove the event listener when it hits the time marker, that way it won't stop when the user wants to play the video from that point on.
Thanks in advance for any suggestions!
The function is being called once in the addEventListener line, but that's not actually passing it as a callback.
Try this:
function videoUpdate(e) {
vidPause(nextMarker, videoPlayer.currentTime;); // Now call your function
}
function vidPause(nextMarker, timeStamp){
var timeMarker = nextMarker;
if (timeStamp >= timeMarker) {
videoPlayer.pause();
videoPlayer.removeEventListener('timeupdate', videoUpdate);
}
};
videoPlayer.addEventListener('timeupdate', videoUpdate); // Note no brackets, as it's passing a ref to the function rather than calling it
I don't know what the scope of nextMarker is, but you should be able to start console logging and find out.

Why does an anonymous function get called, whereas a named function doesn't?

I'm creating a CoffeeScript application that overlays a webcam video with a canvas element (this is just for context and doesn't seem to relate to my problem though). To get the proper canvas size to overlay on the video, I attach an event handler function to the loadedmetadata event like this:
WebcamWizard.prototype.initializeUserMedia = function(stream) {
// ...
video = document.getElementById('webcam');
video.addEventListener('loadedmetadata', function(e) {
// ...
v = e.srcElement;
// ...
});
// ...
}
This works nicely. However, my preference in this case goes to defining this handler method in a different method of this particular class like so:
WebcamWizard.prototype.initializeUserMedia = function(stream) {
// ...
video = document.getElementById('webcam');
video.addEventListener('loadedmetadata', this.initializeCanvas);
// ...
}
WebcamWizard.prototype.initializeCanvas = function(e) {
// ...
video = e.srcElement;
// ...
}
The reason I prefer this is because it makes the CoffeeScript look neater and allows me to access the canvas DOM object within the class I'm working in more easily. When I do the second however, the initializeCanvas method does not seem to be called. There is no error reported on the console either. Why is that?
Curiously, calling methods this way seems to work in the exact same way in the same file.
The problem is probably that "initializeCanvas" will be missing a useful this reference when it's called after the event happens. You're passing a reference to the function, but the this binding is ephemeral and will not survive.
You can wrap it in another function or use .bind():
var wiz = this;
video.addEventListener('loadedmetadata', function() { wiz.initializeCanvas });

Use of reference to calling object (this) using HTML5 audio and jQuery

I'm creating my own HTML5 audio player capable of handling playlists. I have created a custom myPlaylist object with includes all play(), pause(), stop() and other needed functionality. This is all working correctly, but moreover, I need to be aware about when an audio file has ended in order to automatically start playing the next one.
Here's the relevant parts of the code I'm using:
function myPlaylist(){
var player = document.createElement('audio');
var audio = $(player).get(0);
this.next = function next(){
// Picks next song in the playlist and plays it
...
};
$(audio).bind('ended', function() {
alert("Song is finished!");
// Here I want to call to my next() function
});
}
I haven't been able to figure out how to do it. I've tried already several combinations, like $(this).next(), which seems the most reasonable and actually displays the alert, but then does nothing ¿?, also this.next(), which also displays the alert but then shows an error since this refers to the HTML5 audio element, which does not have a next() function.
I've also tried another approach, using
audio.onended = function(){
alert("Song is finished!");
$(this).next();
};
But those do not even trigger the alert. Also audio.ended does not work.
So, I'm basically clueless right now, does anyone have any idea what am I doing wrong? Thanks in advance.
Oh, and I've tested all this in the latest versions of Google Chrome and Safari in Mac OS X.
EDIT Following the advice given in HTML5 audio playlist - how to play a second audio file after the first has ended?, I've also tried the following code
player.addEventListener("ended", function() {
alert("Song is finished!");
$(this).next();
});
And
player.addEventListener("ended", next);
None of them work either, although the first one shows the alert properly.
EDIT 2 Using the search I came across this question, which might also have something to do with my problem, so in order to get rid of any possible troubles with the reference to this, I added a new variable referring to the object itself, so now I'm basically working with:
function myPlaylist(){
var player = document.createElement('audio');
var audio = $(player).get(0);
var me = $(this);
this.next = function next(){
// Picks next song in the playlist and plays it
...
};
$(audio).bind('ended', function() {
alert("Song is finished!");
me.next();
});
}
But then I get an error saying that the Object does not have a method next().
I don't know what else can I try... Any extra information will be highly appreciated, thank you!
there's an HTML5 playlist example handling the ended event here, if that helps?
in your event handler you reference this, but in this context this refers to the DOM element that caught the event, i.e. your audio element.. try this instead:
function myPlaylist(){
var self = this;
var player = document.createElement('audio');
this.next = function (){
// Picks next song in the playlist and plays it
...
};
player.addEventListener("ended", function() {
alert("Song is finished!");
self.next();
});
}
see the MDN for more info on the this keyword

Categories

Resources