Sync one video to another - javascript

The situation is like this - I have two videos, positioned on top of each other. These videos are almost the same (content wise), when you enter the page, video 1 starts playing. There is also a button, which when pressed should sync the second video to the first one and slowly fadeIn (it's hidden at first).
The problem I am having is with the syncing, when I try to set the currentTime of the second video, the player obviously first buffers some frames and after that starts playing, causing desync. I also tried some syncing timeouts with setting currentTime each 10ms until the second video's readyState turns to 4, but that causes pretty big delay.
The code I am using so far:
function switchVideo(first, second) {
var currentTime = first.currentTime;
second.pause();
second.currentTime = currentTime;
second.play();
syncAllowed = true;
var videoInterval = setInterval(function() {
if(next.readyState >= 4) {
second.addClass('show');
syncAllowed = false;
setTimeout(function() {
first.pause();
}, 500);
clearInterval(videoInterval);
}
}, 100);
}
var syncVideos = setInterval(function() {
if(syncAllowed) {
second.currentTime = first.currentTime;
}
}, 10);
So the question is, is there any way to sync one video to another one that is already playing, without having too much delay for the first video to get enought data for playing after setting it's current time? Also I noticed that there is a little bit of lag going backwards in time, which is weird, because that portion of video should already be buffered.
Thanks for any tips!

Related

How to keep a javascript stopwatch/alarm running while switching window/tab

I'm working on a kind of stopwatch/alarm, that's supposed to be running on a website using a modified wordpress plugin. It's function will later be, to start the stopwatch, and once it reaches 15 minutes, there will be an alarm sound. If the watch then doesn't get reset within the next 3 minutes, there will be another action (notification or something, not important right now, that part I already figured out), and if it's reset, it starts the whole thing again (will run for multiple hours).
I've set up the infrastructure via wordpress, and now my only problem is the stopwatch itself.
My problem right now is, that it "kind of" stops running, whenever someone moves to another tab or program. With "kind of", I mean: If the alarm sound was played ONCE, and I then reset it, it keeps counting even when switching tab after that. But on the first page refresh, it doesn't; except sometimes it does, but very slowly (1 "second" takes 3 real seconds). This is what my code looks like right now:
var silence = new Audio('exampleSilence.mp3'); //Audio-file with no content (so just silence)
var audio = new Audio('exampleAlarm.mp3'); //Audio-file with random song/alarm sound
var time=0;
var running=0;
function strtpause () { //Function responsible for start/pause button
if(running==0){
running=1;
increment();
document.getElementById("strtpause").innerHTML="Pause"
}
else{
running=0;
document.getElementById("strtpause").innerHTML="Resume"
return;
}
}
function reset(){ //Function responsible for resetting the stopwatch timer. At first also stopped
//the counting, but I disabled that part as I want it to keep going
//running=0;
time=0; //Sets time back to 0, so timer can start from 0 again
//document.getElementById("strtpause").innerHTML="Start"
document.getElementById("output").innerHTML="00:00"
audio.pause(); //Will also stop the alarm
audio.currentTime = 0; //And set the alarm time back to 0
}
function increment(){ //The actual stopwatch timer function
if(running==1){
setTimeout(function(){
time++;
var mins=Math.floor(time/60);
var secs=Math.floor((time)-(mins*60));
if(mins<10){
mins="0"+mins;
}
if(secs<10){
secs="0"+secs;
}
document.getElementById("output").innerHTML=mins+":"+secs;
if(1<time<9){ //I attempted to play a silent audio file in the background
silence.play(); //to keep the process running, but didn't have an effect
}
if(1<time<9){ //Also tried playing the audio file but very very silent.
audio.volume = 0.001; //It did work, but made timer go slower than regular seconds
audio.play();
}
if (time>10){ //Once the timer reaches a certain time, the alarm is played
audio.volume = 0.5; //Adjusts sound volume
audio.play();
}
increment();
},1000);
}
}
My solution ideas until now were:
-Change the setTimeout increments -> No effect, except with smaller increments it seemed to go slower
-Let it play a "silent" audio file with no actual sound content, so it "has something to do" (no idea if that makes sense, but audio files keep playing in background)
-Let it play an audio file with content while waiting, but very quietly (volume = 0.001). Did work, but made the stopwatch go way too slow, with 1 "second" = 3 actual seconds.
Ideas on how to keep the code running on any OS/Browser are appreciated! I'd prefer not to write/setup a different file or language, as my webdevelopment skills are very very basic, and I don't have rights to edit everything on the website.
Time delay for setTimeout() / setInterval() methods executing on inactive browser tabs are set to one second regardless of their value defined in code.
More about that here: https://usefulangle.com/post/280/settimeout-setinterval-on-inactive-tab

Why is video.readyState always 1 in the onseeking event?

I'm building a video player and I want a loader to be shown whenever the video is paused and the user seeks to a certain time in the video that's not yet buffered.
The waiting event only works if the video isn't paused, so it wouldn't work for me in this particular case.
So, to work around this limitation of the waiting event I've attached an event handler to the seeking event, which fires whenever currentTime changes manually, that is to say whenever "seeking" starts to happen, and in that event handler I'm checking the video's readyState property to see if the new place in the video that the user has seeked to is ready to be played or not.
But the problem is that readyState is always 1 in the event handler of the seeking event no matter what, even if the area that you seek to is actually completely buffered and can be played.
In the snippet below, do the following:
Pause the video.
Seek to a time in the video that's not buffered.
The seeking event fires and readyState is 1, which means that point in the video is not yet downloaded.
Now, seek back to an area that's already buffered.
The seeking event fires again, but readyState is STILL 1, but expectedly it should be >= 2 because that area is buffered. Why is that?!
const video = document.getElementsByTagName('video')[0];
video.addEventListener('waiting', () => {
console.log('waiting FIRED');
});
video.addEventListener('seeking', () => {
console.log('seeking FIRED');
console.log('seeking FIRED | currentTime:', video.currentTime);
console.log('seeking FIRED | readyState:', video.readyState);
});
video {
width: 100%;
margin-bottom: 50px;
}
<video src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" controls></video>
I also can't use the seeked event because it fires after the new area that the user has seemed to is buffered, which defeats the whole purpose of what I'm trying to achieve in the first place. I want to be noticed when the user seeks to a new point in the video before that point is buffered, so that I can show a loader or something.
Since the full video is not buffered, a readyState of 1 sounds correct.
Remember that seeking is async, (even though the .currentTime value is updated synchronously, it doesn't represent the "current playback position"). So when the seeking event fires, the browser already left the current position but didn't yet "set the current playback position to the new playback position" nor "established whether or not the media data for the new playback position is available". (specs)
So at this time it can't tell if it has any data at the will-be current playback position, and all it can say is that it has some metadata.
But for what you are trying to do, you'd be better using the HTMLMediaElement.buffered property, which returns a TimeRanges object representing the time-ranges already buffered.
const video = document.getElementsByTagName('video')[0];
video.addEventListener('seeking', () => {
const buffered = video.buffered;
let buffering = true;
// loop through all ranges
for ( let i = 0; i < buffered.length; i++ ) {
const start = buffered.start(i);
const end = buffered.end(i);
// note that currentTime returns the "official playback position", not the "current playback position"
// its value is set synchronously
if( start <= video.currentTime && end >= video.currentTime ) {
buffering = false;
break;
}
}
console.clear();
console.log('buffering:', buffering);
});
video {
width: 100%;
max-height: 100vh;
}
<video src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" controls></video>

HTML 5 Video becomes corrupted when setting currentTime

After the video ends i want it to be played again, but this time from a different start time. I have added a video ended event handler :
video.addEventListener('ended', handleVideoEnd);
And my handleVideoEnd looks like this:
function handleVideoEnd(e) {
if (currentVideo.loop) {
var video = document.getElementById('video');
video.currentTime = 1;
video.play();
}
}
Unfortunately when i use that code my video becomes corrupted for a couple of seconds resulting in that effect: http://screenshot.sh/m1UaF580NNx8B
This problem, however does not appear if i set the currentTime to 0.
I'm using webm video and testing it in chrome currently. Did anyone have a similar issue when seeking a video part?

Play selected audio while pausing/resetting others

I have two audio elements that play through a button's click event. I've successfully managed to pause one if another is selected but also need to set the paused element back to 0.0 seconds (i.e pause and reset).
I'm aware that Javascript currently doesn't have a stop() method which led assume that this would be done by setting its currentTime to 0. If so I just haven't been able to figure out the best way to incorporate this method in my code.
Right now I'm pausing all audio elements in the latter half of the conditional using $(".audio").trigger("pause"); which doesn't too efficient performance wise. What would be the best way to pause and reset only the previously played audio file and not every one on the page?
http://jsfiddle.net/txrcxfpy/
use below code . check DEMO
$(function() {
$('.track-button').click(function() {
var reSet = $('.track-button').not($(this)).siblings(".audio").get(0);
reSet.pause();
reSet.currentTime = 0;
var $this = $(this),
trackNum = $this.text(),
currentAudio = $this.siblings(".audio"),
audioIsPaused = currentAudio.get(0).paused;
if (audioIsPaused) {
currentAudio.get(0).play();
} else {
currentAudio.get(0).pause();
}
});
});

Control start position and duration of play in HTML5 video

We have a video (13 minutes long) which we would like to control using HTML5. We want to be able to let our users control and select the parts of the video they want to play. Preferably this control would be through 2 input fields. They would input start time (in seconds) in first box and input duration to play (in seconds) in second box. For example, they might want to start the video 10 seconds in and play for 15 seconds. Any suggestions or guidance on the Javascript needed to do this?
Note: I have found the following:
Start HTML5 video at a particular position when loading?
But it addresses only starting at a particular time, and nothing with playing the video for a specified length of time.
You could use the timeupdate event listener.
Save the start time and duration time to variable after loadedmetadata event.
// Set video element to variable
var video = document.getElementById('player1');
var videoStartTime = 0;
var durationTime = 0;
video.addEventListener('loadedmetadata', function() {
videoStartTime = 2;
durationTime = 4;
this.currentTime = videoStartTime;
}, false);
If current time is greater than start time plus duration, pauses the video.
video.addEventListener('timeupdate', function() {
if(this.currentTime > videoStartTime + durationTime){
this.pause();
}
});
If you are able to set start time and end time of video while setting the video url.
you can specify the start and end time in the url itself like
src="future technology_n.mp4#t=20,50"
it will play from 20th second to 50th second.
There are a lot of nuances to using the javascript solution proposed by Paul Sham. A much easier course of action is to use the Media Fragment URI Spec. It will allow you to specify a small segment of a larger audio or video file to play. To use it simply alter the source for the file you are streaming and add #t=start,end where start is the start time in seconds and end is the end time in seconds.
For example:
var start = document.getElementById('startInput').value;
var end = document.getElementById('endInput').value;
document.getElementById('videoPlayer').src = 'http://www.example.com/example.ogv#t='+start+','+end;
This will update the player to start the source video at the specified time and end at the specified time. Browser support for media fragments is also pretty good so it should work in any browser that supports HTML5.
Extend to michael hanon comments:
IE returns buffered.length = 0 and seekable.length = 0. Video doesn't play. So solution:
src="video.mp4#t=10,30"
will not works in IE. If you would like to support IE only way is to use javascript to seek video just after start from 0 second.

Categories