progress bar for playing videos - javascript

I'm trying to add a progress bar that shows how far a videos into it's duration, but one that can be placed outside the video itself(like on another part of the screen). I've been looking around for some time, and have only found ones for showing the loading progress, which is what I don't need. Can anyone help me find where to find that, or supply one themselves even. just in case it's needed, here's the script for the video
var numb = $(this).index(),
videos = ['images/talking1.m4v', 'images/talking2.m4v', 'images/talking1.m4v', 'images/talking2.m4v', 'images/talking1.m4v', 'images/talking2.m4v'
],
myVideo = document.getElementById('myVid');
myVideo.src = videos[numb];
myVideo.load();
setTimeout(function(){
myVideo.play();
}, 200);

You could bind an event listener to the timeupdate event:
myVideo.addEventListener("timeupdate", function() {
// if the video is loaded and duration is known
if(!isNaN(this.duration)) {
var percent_complete = this.currentTime / this.duration;
// use percent_complete to draw a progress bar
}
});
Pick a max length for your progress bar, multiply it by percent_complete (which is between 0 and 1), and use that product as the current length of the bar.

Related

HTML5 Video - Pause when get to middle of video

Aim: video clip is in 2 parts: first half of content is the playing forwards, the second half is the first half in reverse. The video plays when mouseover and will then pause at the halfway stage. On mouseout #1 if paused at the halfway stage it unpauses and plays to end: #2 if you mouseout for before it pauses it will jump to the corresponding timestamp in the 2nd half and play - eg if 10sec video and you mouse at 3sec mark it will jump to 7sec and play from there to the end. See Fiddle for a rough example:
https://jsfiddle.net/cucocreative/1n3Lyqd5/422/
or see this site for what we are trying toreplicate:
https://www.polestar.com/uk/polestar-2/ (first section under hero banner)
Fiddle has latest version of code (not below).
var myVideo=document.getElementById("video");
var halfway=0;
document.getElementById("halfway").innerHTML=halfway;
function Rewind() {
var duration = video.duration;
var currentTime = video.currentTime;
var jumpto = duration - currentTime;
var pause = duration/2;
document.getElementById("jumpto").innerHTML=jumpto;
myVideo.currentTime = jumpto;
myVideo.play();
}
function Play() {
if (myVideo.paused)
var duration = video.duration;
var pause = duration/2;
document.getElementById("pause").innerHTML=pause;
myVideo.play();
this.on('timeupdate', function () {
console.log(this.currentTime());
});
}
$("#video").on(
"timeupdate",
function(event){
var duration = video.duration;
var pause = duration/2;
onTrackedVideoFrame(this.currentTime, this.duration);
// Pause when hit the halfway stage
// check to see if halfway yet
// can't get the below to play nice with the rewind function
if(this.currentTime >= pause) {
//this.pause();
//var halfway = 1;
//document.getElementById("halfway").innerHTML=halfway;
}
});
function onTrackedVideoFrame(currentTime, duration){
$("#current").text(currentTime); //Change #current to currentTime
$("#duration").text(duration)
}
After a lot of deadends, I have figured out the rewind effect and can get it to pause at the halfway stage BUT when both blocks of code are together they don't play nice together so have commented out the pause code for the time being (see lines 36-40).
I can't figure out the logic to get the Rewind function to know if the video has gotten as far as being paused yet.
So currently, as long as you mouseout before the 2sec mark it works, after that's I need help with.
update: I did think that putting an if statement at line 12 to check if the currentTime is >= than the Duration would work but it's not.
if(this.currentTime >= pause) {
myVideo.currentTime = pause;
} else {
myVideo.currentTime = jumpto;
}
I managed to get a rough solution working, it probably needs a little fine tuning tho.
HTML
<div style="text-align:center" onmouseenter="Play()" onmouseleave="Rewind()">
<video
id="video"
onended="onVideoEnd()"
class="video"
width="480">
<source src="https://www.cucostaging.co.uk/side-reversed.mp4" type="video/mp4" />
Your browser does not support HTML5 video.
</video>
</div>
Script
//var myVideo=document.getElementById("video");
const myVideo = document.getElementById('video')
var status = 'STOP' // STOP | PLAY | REWIND
function Rewind() {
var currentTime = myVideo.currentTime;
var duration = myVideo.duration;
myVideo.currentTime = duration - currentTime;
status = 'REWIND'
myVideo.play();
}
function Play() {
var currentTime = myVideo.currentTime;
var duration = myVideo.duration;
let tempCurrent = status === 'STOP' ? 0 : duration - currentTime;
myVideo.currentTime = tempCurrent;
status = 'PLAY'
myVideo.play();
setTimeout(() => {
if(status === 'PLAY')
myVideo.pause()},
(2 - tempCurrent) * 1000)
}
function onVideoEnd() {
status = 'STOP'
}

How to enable proper scrolling after scrub effect in a webpage using javascript?

I found a code from codepen to use it for scrub effect in video. After the video has ended I want to enable complete scrolling. How can I acheive that? Thanks in advance!
var frameNumber = 0, // start video at frame 0
// lower numbers = faster playback
playbackConst = 500,
// get page height from video duration
setHeight = document.getElementById("set-height"),
// select video element
vid = document.getElementById('v0');
// var vid = $('#v0')[0]; // jquery option
// dynamically set the page height according to video length
vid.addEventListener('loadedmetadata', function() {
setHeight.style.height = Math.floor(vid.duration) * playbackConst + "px";
});
// Use requestAnimationFrame for smooth playback
function scrollPlay(){
var frameNumber = window.pageYOffset/playbackConst;
vid.currentTime = frameNumber;
window.requestAnimationFrame(scrollPlay);
}
window.requestAnimationFrame(scrollPlay);

HTML progress bar click and update

I've added a progress bar in my javascript code, to show the progress of a music track.
audioElement.addEventListener("timeupdate", function(){
var progress = document.getElementById("progress");
var value = 0;
if (audioElement.currentTime > 0) {
value = Math.floor((100 / audioElement.duration) * audioElement.currentTime);
}
progress.style.width = value + "%";}, false);
The code works fine, and I can see that my bar progresses as the music progresses as well.
Now I would like to be able to click anywhere on the progressbar, and be able to forward the music accordingly. In other words, I would like to be able to control the rewind/forward the track by clicking on the progress bar itself.
Could someone help me, and gave me some idea how to do this.
I am not an expert with HTML and JavaScripting, so I would appreciate some help.
Thanks,
--Rudy
I would do it like this, using jQuery to make things slightly easier:
http://jsfiddle.net/LQqGS/3/
$('.clickable').bind('click', function (ev) {
var $div = $(ev.target);
var $display = $div.find('.display');
var offset = $div.offset();
var x = ev.clientX - offset.left;
$('.progress').width(x);
});
<div class='clickable'>
<div class='display'>
<div class="progress"></div>
</div>
</div>
You can then use that x coordinate to manipulate your music player as a percentage of the div width.
I came across this question today while creating a custom HTML5 video player. Just in regards to video instead of audio. The process should work the same though for your audio needs.
I found this article and was able to incorporate the progress bar part of it into my player. https://msdn.microsoft.com/en-us/library/ie/gg589528%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
Instead of using a progressbar element, like I was doing, the method I used here is to use a canvas element instead.
<canvas id='progress-bar' width="200" height="20" style="border:1px solid green;">canvas not supported</canvas>
Then in your JavaScript, create a handle to reference it by
var mediaPlayer;
var progressBar;
var canvas;
When the document loads, initialize everything including the progress bar items
mediaPlayer = document.getElementById('media-video');
progressBar = document.getElementById('progress-bar');
canvas = document.getElementById('progress-bar');
canvas.addEventListener("click", function(e) {
var canvas = document.getElementById('progress-bar');
if (!e) {
e = window.event;
} //get the latest windows event if it isn't set
try {
//calculate the current time based on position of mouse cursor in canvas box
mediaPlayer.currentTime = mediaPlayer.duration * (e.offsetX / canvas.clientWidth);
}
catch (err) {
// Fail silently but show in F12 developer tools console
if (window.console && console.error("Error:" + err));
}
}, true);
mediaPlayer.addEventListener('timeupdate', updateProgressBar, false);
Then create a function outside of your initialization function for the timeupdate listener to call and automatically update the progress bar for you
function updateProgressBar() {
mediaPlayer = document.getElementById('media-video');
//get current time in seconds
var elapsedTime = Math.round(mediaPlayer.currentTime);
//update the progress bar
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
//clear canvas before painting
ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
ctx.fillStyle = "rgb(255,0,0)";
var fWidth = (elapsedTime / mediaPlayer.duration) * (canvas.clientWidth);
if (fWidth > 0) {
ctx.fillRect(0, 0, fWidth, canvas.clientHeight);
}
}
}
I haven't completely cleaned it up yet. Hence the redundant handles to the same id. But I'm sure you get the picture.

Capture frames from video with HTML5 and JavaScript

I want to capture a frame from video every 5 seconds.
This is my JavaScript code:
video.addEventListener('loadeddata', function() {
var duration = video.duration;
var i = 0;
var interval = setInterval(function() {
video.currentTime = i;
generateThumbnail(i);
i = i+5;
if (i > duration) clearInterval(interval);
}, 300);
});
function generateThumbnail(i) {
//generate thumbnail URL data
var context = thecanvas.getContext('2d');
context.drawImage(video, 0, 0, 220, 150);
var dataURL = thecanvas.toDataURL();
//create img
var img = document.createElement('img');
img.setAttribute('src', dataURL);
//append img in container div
document.getElementById('thumbnailContainer').appendChild(img);
}
The problem I have is the 1st two images generated are the same and the duration-5 second image is not generated. I found out that the thumbnail is generated before the video frame of the specific time is displayed in < video> tag.
For example, when video.currentTime = 5, image of frame 0s is generated. Then the video frame jump to time 5s. So when video.currentTime = 10, image of frame 5s is generated.
Cause
The problem is that seeking video (by setting it's currentTime) is asynchronous.
You need to listen to the seeked event or else it will risk take the actual current frame which is likely your old value.
As it is asynchronous you must not use the setInterval() as it is asynchronous too and you will not be able to properly synchronize when the next frame is seeked to. There is no need to use setInterval() as we will utilize the seeked event instead which will keep everything is sync.
Solution
By re-writing the code a little you can use the seeked event to go through the video to capture the correct frame as this event ensures us that we are actually at the frame we requested by setting the currentTime property.
Example
// global or parent scope of handlers
var video = document.getElementById("video"); // added for clarity: this is needed
var i = 0;
video.addEventListener('loadeddata', function() {
this.currentTime = i;
});
Add this event handler to the party:
video.addEventListener('seeked', function() {
// now video has seeked and current frames will show
// at the time as we expect
generateThumbnail(i);
// when frame is captured, increase here by 5 seconds
i += 5;
// if we are not past end, seek to next interval
if (i <= this.duration) {
// this will trigger another seeked event
this.currentTime = i;
}
else {
// Done!, next action
}
});
If you'd like to extract all frames from a video, see this answer. The example below assumes that you want to extract a frame every 5 seconds, as OP requested.
This answer requires WebCodecs which is supported in Chrome and Edge as of writing.
<canvas id="canvasEl"></canvas>
<script type="module">
import getVideoFrames from "https://deno.land/x/get_video_frames#v0.0.8/mod.js"
let ctx = canvasEl.getContext("2d");
// `getVideoFrames` requires a video URL as input.
// If you have a file/blob instead of a videoUrl, turn it into a URL like this:
let videoUrl = URL.createObjectURL(fileOrBlob);
const saveFrameEverySeconds = 5;
let elapsedSinceLastSavedFrame = 0;
await getVideoFrames({
videoUrl,
onFrame(frame) { // `frame` is a VideoFrame object:
elapsedSinceLastSavedFrame += frame.duration / 1e6; // frame.duration is in microseconds, so we convert to seconds
if(elapsedSinceLastSavedFrame > saveFrameEverySeconds) {
ctx.drawImage(frame, 0, 0, canvasEl.width, canvasEl.height);
elapsedSinceLastSavedFrame = 0;
}
frame.close();
},
onConfig(config) {
canvasEl.width = config.codedWidth;
canvasEl.height = config.codedHeight;
},
});
URL.revokeObjectURL(fileOrBlob); // revoke URL to prevent memory leak
</script>
Demo: https://jsbin.com/qovapeziqi/edit?html,output
Github: https://github.com/josephrocca/getVideoFrames.js

Capture group of HTML5 video screenshots

I am trying to generate a group of thumbnails in the browser out of a HTML5 video using canvas with this code:
var fps = video_model.getFps(); //frames per second, comes from another script
var start = shot.getStart(); //start time of capture, comes from another script
var end = shot.getEnd(); //end time of capture, comes from another script
for(var i = start; i <= end; i += 50){ //capture every 50 frames
video.get(0).currentTime = i / fps;
var capture = $(document.createElement("canvas"))
.attr({
id: video.get(0).currentTime + "sec",
width: video.get(0).videoWidth,
height: video.get(0).videoHeight
})
var ctx = capture.get(0).getContext("2d");
ctx.drawImage(video.get(0), 0, 0, video.get(0).videoWidth, video.get(0).videoHeight);
$("body").append(capture, " ");
}
The the amount of captures is correct, but the problem is that in Chrome all the canvases appear black and in Firefox they always show the same image.
Maybe the problem is that the loop is too fast to let the canvases be painted, but I read that .drawImage() is asynchronous, therefore, in theory, it should let the canvases be painted before jumping to the next line.
Any ideas on how to solve this issue?
Thanks.
After hours of fighting with this I finally came up with a solution based on the "seeked" event. For this to work, the video must be completely loaded:
The code goes like this:
var fps = video_model.getFps(); //screenshot data, comes from another script
var start = shot.getStart();
var end = shot.getEnd();
video.get(0).currentTime = start/fps; //make the video jump to the start
video.on("seeked", function(){ //when the time is seeked, capture screenshot
setTimeout( //the trick is in giving the canvas a little time to be created and painted, 500ms should be enough
function(){
if( video.get(0).currentTime <= end/fps ){
var capture = $(document.createElement("canvas")) //create canvas element on the fly
.attr({
id: video.get(0).currentTime + "sec",
width: video.get(0).videoWidth,
height: video.get(0).videoHeight
})
.appendTo("body");
var ctx = capture.get(0).getContext("2d"); //paint canvas
ctx.drawImage(video.get(0), 0, 0, video.get(0).videoWidth, video.get(0).videoHeight);
if(video.get(0).currentTime + 50/fps > end/fps){
video.off("seeked"); //if last screenshot was captured, unbind
}else{
video.get(0).currentTime += 50/fps; //capture every 50 frames
}
}
}
, 500); //timeout of 500ms
});
This has worked for me in Chrome and Firefox, I've read that the seeked event can be buggy in some version of particular browsers.
Hope this can be useful to anybody. If anyone comes up with a cleaner, better solution, it would be nice to see it.

Categories