HTML5 audio on IOS: how can currentTime be less than initial value? - javascript

I have to play a short fragment of bigger audio. I use currentTime to set starting point in time, and timeupdate event to stop audio when required.
I noticed that on few early timeupdate events sometimes currentTime is less than its initial value, despite there is (obviously) no "rewind" actions.
Here is code example:
var start = 1;
var end = 1.3;
var audio = document.getElementById('audio');
audio.addEventListener('timeupdate', function () {
console.log(audio.currentTime);
if (audio.currentTime < start || audio.currentTime > end) {
audio.pause();
}
});
audio.currentTime = start;
audio.play();
For example, output of console log can be this:
1
0.85
0.85
1
1.02
...
Here's an example.
Tested on iPad with iOS 11.4.1. This problem appears only on very short time ranges ~0.3sec.

At first you have a wrong question because you want that your script works on iOS. And because of this your question in title is irrelevant. We have a lot of bugs on iOS and the iOS developers do not want to correct them. It is a bug. Write to support from iOS and ask there "How is it possible?".
At second you have two questions in your post because your script does not work on iOS like you it want.
I wrote for you the solution. Make sure you are not trying to call the audio before the audio element has metadata loaded (first mistake). Then you have the second mistake: instead of:
if(audio.currentTime < start || audio.currentTime > end) //your mistake
you have to write:
if(audio.currentTime >= end)
The solution (it works on iOS too)
var start = 1,
end = 1.3,
audio = document.getElementById('audio'),
button = document.getElementById('button'),
log = document.getElementById('log');
audio.addEventListener('timeupdate', function()
{
writeLog(audio.currentTime);
//your mistake: if(audio.currentTime < start || audio.currentTime > end)
if(audio.currentTime >= end)
{
audio.pause();
}
});
function writeLog(value)
{
var div = document.createElement('div');
div.innerHTML = value;
log.insertBefore(div, log.firstChild);
}
audio.addEventListener('loadedmetadata', function()
{
audio.pause();
button.removeAttribute('disabled');
});
button.addEventListener('click', function()
{
log.insertBefore(log.lastChild.cloneNode(!0), log.firstChild);
audio.currentTime = start;
audio.play();
});
#log
{
width:100%;
height:18em;
overflow-y:auto;
font:3em 'Courier New';
background:#069;
color:#7e0
}
#log div:last-child{display:none}
<!--
For this link from your example (on jsfiddle.net) is only a download is possible, but not using as source:
http://techslides.com/demos/samples/sample.m4a
-->
<audio id="audio" preload="auto">
<source src="http://techslides.com/demos/samples/sample.mp3" type="audio/mpeg"/>
<source src="http://techslides.com/demos/samples/sample.ogg" type="audio/ogg"/>
<source src="http://techslides.com/demos/samples/sample.aac" type="audio/aac"/>
</audio>
<button id="button" disabled style="width:9em;height:3em;font-size:3em">Play</button>
<br><br>
<div id="log"><div><hr></div></div>
The snippet on SO does not work because it is in sandbox.
See this working example on codepen.io.

Related

How to buffer / preload html5 videos using javascript

I am running the following code which changes the source of an html5 video at the end of each video, so that it constantly plays one video after another. After the first video finishes, it runs the second, then the third and eventually starts again from the beginning, resulting in an endless cycle. It also chooses a random starting point, but that is not important for my question.
In the code below you will only find two video sources but the final code would use around ten or so.
My problem is that there is a small pause between the end of a video and the beginning of the next one. I have made the background-color of the video tag red so that you will be able to see a red flash between the playback of each video.
I'm guessing that this could be solved by preloading all videos specified inside the javascript code. So what I would like to achieve is to preload only the next video in the list specified inside the javascript code when the current video is playing. So when video nr. 5 is playing, it should preload video nr. 6 etc..
Or is this not something that could be solved by effective buffering / preloading? I'm happy about any other suggestions as well..
var vidElement = document.getElementById('video');
var vidSources = [
"http://www.w3schools.com/html/mov_bbb.mp4",
"http://www.w3schools.com/html/movie.mp4"
];
var activeVideo = Math.floor((Math.random() * vidSources.length));
vidElement.src = vidSources[activeVideo];
vidElement.addEventListener('ended', function(e) {
// update the active video index
activeVideo = (++activeVideo) % vidSources.length;
if(activeVideo === vidSources.length){
activeVideo = 0;
}
// update the video source and play
vidElement.src = vidSources[activeVideo];
vidElement.play();
});
video { background-color: red }
<video src="http://www.w3schools.com/html/mov_bbb.mp4" id="video" autoplay muted playsinline></video>
<p>(each video is just ~ 10 seconds)</p>
You can create video elements with preload attribute and add it to div containar like follows:
function initVideoElement(videoEl)
{
videoEl.playsinline = true;
videoEl.muted = false;
videoEl.preload = 'auto'; //but do not set autoplay, because it deletes preload
//loadedmetadata is wrong because if we use it then we get endless loop
videoEl.onplaying = function(e)
{
if(++nextActiveVideo == 2)
nextActiveVideo = 0;
//replace the video elements against each other:
if(this.inx == 0)
nextVideoElement = videoElements[1];
else
nextVideoElement = videoElements[0];
nextVideoElement.src = vidSources[nextActiveVideo];
nextVideoElement.pause();
};
videoEl.onended = function(e)
{
this.style.display = 'none';
nextVideoElement.style.display = 'block';
nextVideoElement.play();
};
}
var videoContainer = document.getElementById('videoContainer'),
nextActiveVideo = 0,
nextVideoElement,
videoElements =
[
document.createElement('video'),
document.createElement('video')
],
vidSources =
[
"http://www.w3schools.com/html/mov_bbb.mp4",
"http://www.w3schools.com/html/movie.mp4"
];
videoElements[0].inx = 0; //set index
videoElements[1].inx = 1;
initVideoElement(videoElements[0]);
initVideoElement(videoElements[1]);
videoElements[0].autoplay = true;
videoElements[0].src = vidSources[0];
videoContainer.appendChild(videoElements[0]);
videoElements[1].style.display = 'none';
videoContainer.appendChild(videoElements[1]);
video{background-color: red}
<div id="videoContainer"></div>

Is it possible to embed actions in html5 video?

Is there any way to play a video in html5, stop and execute javascript at known points within the video?
Yes, try this. You have to use the currentTime property. Start with that and start your javascript functions from there. Is that what you're looking for?
var vid = document.getElementById("video");
//Add a timeupdate listener
video.addEventListener("timeupdate", function(){
//Check for video properties from within the function
if (this.currentTime == 5)
this.pause();
//cal javascript
}
}
Looking at the accepted answer over here it looks like it is possible.
To pause the video you just do this:
var mediaElement = document.getElementById("video"); // create a reference to your HTML5 video
mediaElement.pause(); // pauses the video
If you want to play the video, do this:
mediaElement.play(); // plays the video
To get the current time in the video, do this:
mediaElement.currentTime; // gets the current time
Here's an example linking them all up:
var mediaElement = document.getElementById("video");
if(mediaElement.currentTime == 35){
mediaElement.pause();
// do whatever else you would like
mediaElement.play();
}
The MDL documentation is here, there are plenty of other properties you might find helpful.
Hope this helps!
Yes, by doing like this you can.
Note that you have to look for a time using bigger than > bigger than (as the chance to match an exact millisecond is almost zero), and have a variable in one way or the other to know which ones is done.
window.addEventListener('load', function() {
var cur = document.querySelector('#cur'),
vid = document.querySelector('#vid')
})
var appDone = {"done7":false,"done4":false}
vid.addEventListener('timeupdate', function(e) {
if (e.target.currentTime > 7 && !appDone.done7) {
appDone.done7 = true;
e.target.pause();
//do something
cur.textContent += ", " + "done7 once";
e.target.play();
}
if (e.target.currentTime > 4 && !appDone.done4) {
appDone.done4 = true;
e.target.pause();
//do something
cur.textContent += ", " + "done4 once";
e.target.play();
}
})
<video id="vid" width="320" height="176" controls>
<source src="http://www.w3schools.com/tags/mov_bbb.mp4" type="video/mp4">
<source src="http://www.w3schools.com/tags/mov_bbb.ogg" type="video/ogg">
Your browser does not support HTML5 video.
</video>
<p id="cur"></p>

loop a html5 video in specific timline with js

i had a html5 video box with only one video inside. in this video their is a animated figure with different moves. as an example: second 1-5 the figure walks inside, second 5-7 the figure say "hello", ... i would like to control these animations with js and i would like to loop the main animation.
this is the html5 videoplayer:
<video id="vidplayer">
<source src="animation.mp4" type="video/mp4"</source>
</video>
i have tested following code to loop the video between the second 7-9 and it doesnt work:
var vid = $("#vidplayer").get(0);
vid.ontimeupdate = function(e) {
if(this.currentTime = 9) {
vid.currentTime = 7;
}
};
it works only with
(this.currentTime >= 9)
but with this ">=" i cant use animations above 9 seconds (like 21-29 seconds).
can you please help me? Thx!
you will need to check for both the start/stop of the fragments you want to loop within in order to constrain the test.
In the example below it will loop from the 15s mark (or greater) back to 10s; from the 9s mark back to 7; from the 5s mark back to 1.
For a production version I'd probably create an array of the jump points and use that to avoid having to type the times as an upper/lower, but this should give you a framework to go on from:
<video preload="auto" controls id="vidplayer">
<source src="BigBuck.m4v" type='video/mp4;'"></source></video>
<script>
var vid = document.getElementById("vidplayer")
vid.addEventListener('timeupdate', timeupdate, false);
function timeupdate() {
if (vid.currentTime >= 15) {
vid.currentTime = 10;
} else if (vid.currentTime >= 9 && vid.currentTime < 10) {
vid.currentTime = 7;
} else if (vid.currentTime >= 5 && vid.currentTime < 7) {
vid.currentTime = 1;
}
}
</script>
I had the same problem and wrote a plugin to solve it. See https://github.com/phhu/videojs-abloop . This allows javascript commands to be used to control looping of section of video. The example shows how you might control switching different sections of video using callbacks or otherwise.

HTML5 Video Slideshow not working on Mac FF or Safari

Here's my jsfiddle for review: http://jsfiddle.net/Z2Nq8/81/
The code works perfect in Chrome on my Mac. Firefox and Safari don't. I had another contributor who did the code edits, and he says everything is working on Windows. Please review and let me know if you see something that I'm doing wrong. Thanks!
js:
$(function () {
var $videos = $('.video'),
numVideos = $videos.length,
counter = 0;
setInterval(function () {
++counter;
var $currentVideo = $('.video[data-state="playing"]'),
$nextVideo = $currentVideo.next();
$currentVideo.attr('data-state', '');
if (counter == numVideos) {
counter = 0;
$nextVideo = $videos.first();
}
$nextVideo.attr('data-state', 'playing');
$currentVideo.toggleClass('hidden');
$nextVideo.toggleClass('hidden');
$currentVideo.find('video')[0].pause();
$currentVideo.find('video')[0].currentTime = 0;
$nextVideo.find('video')[0].play();
}, 3000); // do this every 3 seconds
});
And for some reason my HTML and CSS code won't display here (i've added 4 spaces and tried quotations) so, you'll have to look at jsfiddle (see above).
Thanks for helping this noob!
S
I have tested your fiddle and I can see straight away that on Firefox won't work because you are using mp4. You can embed an alternative format for the video and you can use .ogg for firefox and opera.
Check this MDN resource: https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats?redirectlocale=en-US&redirectslug=Media_formats_supported_by_the_audio_and_video_elements
<video controls>
<source src="somevideo.webm" type="video/webm">
<source src="somevideo.mp4" type="video/mp4">
I'm sorry; your browser doesn't support HTML5 video in WebM with VP8 or MP4 with H.264.
<!-- You can embed a Flash player here, to play your mp4 video in older browsers -->
</video>
Modified the fiddle a bit. It works on Safari, but to get more support you also need to include webm & ogg.
http://jsfiddle.net/me2loveit2/Z2Nq8/82/
var content = [
['http://bwpcommunications.com/videobg/001.mp4','Let\'s get together and discuss your brand.'],
['http://bwpcommunications.com/videobg/002.mp4','Let\'s build a strategy together face to face.'],
['http://bwpcommunications.com/videobg/003.mp4','Let\'s create a compelling campaign and tell your story.']
];
var numVideos = content.length,
counter = 0,
$video = $('#bgvid');
$title = $('#title');
setInterval(function () {
++counter;
if (counter == numVideos) {
counter = 0;
}
$video.attr('src',content[counter][0]);
$title.html(content[counter][1]);
}, 5000); // do this every 3 seconds

setinterval function using previous/ old value for a variable

I want to play portion of video (html5) and loop through those portions on "onclick" of 2 textareas. I am passing starttime (st) and endtime (et) to the javascript function.
textarea1 : st=6 et=12
textarea2 : st=15 et=20
I am using setinterval to check the endtime and calling method again to loop.
scenario :
Click on textarea1. st=6 et=12. While it is playing click on textarea2. The condition video.currentTime > et is true as 15 > 12 which is wrong result. The et value is not changing on "onclick". Please help.
Here is my code:
function playVideo(st,et) {
var video = document.getElementById('player');
video.currentTime = st;
video.play();
int = setInterval(function() {
if (video.currentTime > et) { // here the et still has the previous value
playVideo(st,et); // loop
}
}, 10);
}
<video width="320" height="240" controls="controls" id="player">
<source src="abc.mp4" type="video/mp4" />
</video>
<textarea onclick="playVideo(6,12)" >Part1 </textarea>
<textarea onclick="playVideo(15,20)">Part2</textarea>
It's not that et has the previous value, it's that you're never getting rid of the old interval.
Instead of calling playVideo again, you would be better off just calling video.currentTime = st;. However, bear in mind that you will need something that stops the interval. Personally, I'd do something like this:
function playVideo(st,et) {
var video = document.getElementById('player');
clearInterval(arguments.callee.interval);
video.currentTime = st;
video.play();
arguments.callee.interval = setInterval(function() {
if( video.currentTime > et) video.currentTime = st;
},10);
}
I'm saving the interval as a property of the function, which is essentially JS's equivalent to static variables. This way, when the user clicks on the other textarea it will clear out the existing interval to ensure it doesn't interfere anymore.
Get rid of your old interval by using clearInterval!
Your problem might be that you're not clearing the interval callback. Try checking for a previous (but still active) interval, and clearing it before assigning a new one.
var videoEndInterval = null;
function playVideo(st, et) {
...
if (videoEndInterval !== null) {
clearInterval(videoEndInterval);
}
videoEndInterval = setInterval(function() {
...
}, 10);
}
Your 'loop' currently creates each interval a new interval, and you get a mess of intervals. setInterval does execute every x ms, where setTimeout does execute once after x ms. You need to clear your interval, when you stop the video.
Try this:
// var declared outside, to clear an old interval,
// but also to not have a global variable.
var stopInterval;
var playVideo = function (st, et) {
var video = document.getElementById('player');
video.currentTime = st;
video.play();
if (stopInterval) {
// clear an old interval, if playVideo is called twice
window.clearInterval(stopInterval);
stopInterval = 0;
}
stopInterval = window.setInterval(function() {
if (video.currentTime > et) {
// here your video is after et, so clear the interval and stop.
window.clearInterval(stopInterval);
stopInterval = 0;
video.pause();
}
}, 10);
};
You should also hint if your video isn't buffered, is not playable and is paused (to clear your interval).
See also
HTMLVideoElement Interface, HTMLMediaElement Interface,
video HTML5 element, and
Using HTML5 audio and video on Mozilla's Developer Network.

Categories