Tracking how long a viewer has viewed a certain video - javascript

I am trying to track how long a viewer has viewed a certain video and I'm a bit confused as to what some of the media events do in React JS.
When a video is skipped to a later part of the video and stops for a bit does it call onPause without actually needing to press the pause button?
Also I am confused as to what onTimeUpdate does.

Please take a look at the HTML Audio/Video Event Listeners reference here:
https://www.w3schools.com/tags/ref_av_dom.asp
You'll probably need the 'readystate' property :
Represents the ready state of the audio/video element:
0 = HAVE_NOTHING - no information whether or not the audio/video is
ready
1 = HAVE_METADATA - metadata for the audio/video is ready
2 = HAVE_CURRENT_DATA - data for the current playback position is
available, but not enough data to play next frame/millisecond
3 = HAVE_FUTURE_DATA - data for the current and at least the next
frame is available
4 = HAVE_ENOUGH_DATA - enough data available to start playing
So, I would imagine you use a counter on a setTimeout to count how long the video has been played for:
var counter = 0;
var timer = setTimeout(function() {
var vid = document.getElementById("myVideo");
var state = vid.readystate;
if( state != 0 ) counter++;
console.log( counter + " seconds its been played" );
} , 1000 );

Related

Web Audio Api precise looping in different browsers

So what I want is to have constant looping interchanging from different audio sources. For demo purpose I made a little puzzle game - you align numbers in order from 0 to 8 and depending on how you align them different loops are playing. I managed to get the result I want on Chrome Browser, but not on Safari or Firefox. I tried adding a different audio destination or multiple audio contexts but no matter what loop just stops after one iteration in Safari and other browsers except for Chrome.
Here is a link to the demo on code-pen Demo Puzzle with music
please turn down your sound as music might be a little too loud, I didn't master it. And here is basic code I have for Web Audio Api manipulation.
Thanks
*Also it does not work for mobile at all.
const AudioContext = window.AudioContext || window.webkitAudioContext;
var audioContext = new AudioContext();
const audio1 = document.getElementById("aud1");
const audio2 = document.getElementById("aud2");
const audio3 = document.getElementById("aud3");
const audio4 = document.getElementById("aud4");
var chosenTrack = audio2;
let gameStarted = false;
function startGame() {
document.getElementById("sHold").style.display = "none";
document.getElementById("container").style.display = "block";
gameStarted = true;
audioContext.resume();
audioContext = new AudioContext();
audio1.pause();
audio1.play();
audio1.currentTime = 0;
}
setInterval(function() {
if (gameStarted) {
//console.log(audioContext.currentTime );
if (audioContext.currentTime >= 6.4) {
audioContext = new AudioContext();
chosenTrack.pause();
chosenTrack.play();
chosenTrack.currentTime = 0;
}
}
}, 5);
Some thoughts:
You're not really using Web Audio this way, you're still using audio elements as the source which doesn't help if you want to be able to achieve precise timing. You should load them into AudioBuffers and play them using an AudioBufferSourceNode.
If you absolutely want to use audio elements (because the files you use are really massive and you want to stream them) you probably want to use the loop property on it although i doubt if that ends up being precise and gapless.
Never use setInterval to get a callback every frame, use requestAnimationFrame
Don't use setInterval OR requestAnimationFrame to be able to achieve precise audio looping, the javascript thread is not precise enough to do that AND can be held up when other things take a bit more time, too many enemies in screen for example. You should be scheduling ahead of time now and then: https://www.html5rocks.com/en/tutorials/audio/scheduling/
AudioBufferSourceNodes have a loop boolean property which will loop them as precise as possible
Do realise that different audio-decoders (so: different browsers) MIGHT decode audiofiles slightly differently: some may have a few more ms on the start for example. This might become an issue when using multiple looping AudioBufferSourceNodes, which may all be running out of sync after an x amount of time. I always reschedule something on the exact time needed instead of using the loop property.

an audio is playing and you reload the page but the audio should start from that point where the page was reloaded

How to start playback of audio from a specific time point?
let suppose an audio is playing, you reload the page and the audio should resume from that point where the page was reloaded. I already have code storing the time as audio plays, but I don't know how to use that value as new audio start time.
li = $('#plList li').click(function () {
var id = parseInt($(this).index());
if (id !== index) {
playTrack(id);
}
}),
loadTrack = function (id) {
$('.plSel').removeClass('plSel');
$('#plList li:eq(' + id + ')').addClass('plSel');
npTitle.text(tracks[id].name);
index = id;
audio.src = mediaPath + tracks[id].file + extension;
// Assign an ontimeupdate event to the video element, and execute a function if the current playback position has changed
audio.ontimeupdate = function() {myFunction()};
function myFunction()
{
// Display the current position of the audio.
$('#cookieVal').val(audio.currentTime);
localStorage.setItem("audioPlayTime", audio.currentTime);
console.log(audio.currentTime);
}
},
playTrack = function (id) {
loadTrack(id);
audio.play();
};
extension = audio.canPlayType('audio/mpeg') ? '.mp3' : audio.canPlayType('audio/ogg') ? '.ogg' : '';
loadTrack(index);
}
You could use currentTime property. Store currentTime in localstorage or cookie.
This question already answered . You can find more detail . Check this out at this link Setting HTML5 audio position
Hello!
It's really hard to say how to do it without me seeing your code, but I'll give you steps that I would try that could make you achieve this!
note that I am not sure if this is the best way to do this!
1. Use onbeforeunload event to check when user is leaving the page.
2. When this event triggers, save the time and the song to a cookie variable here you can see how
3. And finally use DOMContentLoaded event, so when your page loads, you try to load those variables you saved into cookies.
4. If they exist, you've got your time, just play your song from that time, if not, just start from 0:00.
I'm sorry...
But I can't provide you with ready code, cause you didn't show any either.
But I hope that you'll find my answer somewhat useful! Cheers!

How can I get an event when an audio file reaches a certain point?

I am experimenting with interactive audio applications in HTML, and I would like to be able to seamlessly start playing one audio file just as another audio file reaches a particular playback location; for example, if audio file A is playing, then I would like to receive an event when it reaches 16 seconds in exactly.
I have tried using audio.play(); setTimeout(myCallback, 16000); for this, but I am finding it to be incredibly unstable in some browsers (Safari especially, particularly when the window is in the background), and will sometimes fire slightly early, and other times fire very late. This remains the case even if I explicitly stop, rewind, and .load() the upcoming audio segment when scheduling the callback.
My current (simple) looper that simply toggles between two players every 16 seconds is below:
audio = []
for (var i = 0; i < 2; i++) {
audio[i] = new Audio("mew" + i + ".mp3");
}
var which = 0;
var pump = function() {
audio[which].play();
which = (which + 1) % audio.length;
window.setTimeout(pump, 16000);
}
audio[which].addEventListener('canplaythrough', function() {
pump();
});
for (i in audio) {
audio[i].load();
}
Also, note that while in this case I could use setInterval(), that will not always be the case (and anyway, setInterval() suffers from the same stability problem).

Web Audio API and real current time when playing an audio file

I am having problems when I want to know the current time of a file playing using the Web Audio API. My code plays the file nicely and the current time returned by the getCurrentTime() function is accurate enough when it comes to short files which load fast.
But when I try to load big files, sometimes the current time returned by the getCurrentTime() function is accurate and sometimes not. Sometimes, after waiting for example for 20 seconds to hear the file playing, when it starts playing it says that the current time is about 20 seconds (which is not true because it is just playing the beginning of the file). It happens with any audio format (OGG, MP3, WAV...) but only sometimes.
I am using a slow system (Asus EEE PC 901 with an Intel Atom 1.60 Ghz and 2 GB RAM with Windows XP Home Edition and SP3) and Firefox 41.0.1.
I am not sure, but it seems that the source.start() method starts playing the sound way too late, so the line after calling that method, where I set the value for the startTime variable, is not the real starting time.
Here is the code (simplified):
var context, buffer, startTime, source;
var stopped = true;
function load(file, startAt)
{
//Here creates the AudioContext and loads the file through XHR (AJAX) and gets the buffer. All works fine.
//When it gots the buffer through XHR (AJAX) and all is fine, it calls play(startAt) function immediately.
//Note: normally, startAt is 0.
}
function play(startAt)
{
source = context.createBufferSource(); //Context created before.
source.buffer = buffer; //Buffer got before from XHR (AJAX).
//Creates a gain node to be able to set the volume later:
var gainNode = context.createGain();
source.connect(gainNode);
gainNode.connect(context.destination);
//Plays the sound:
source.loop = false;
source.start(startAt, 0, buffer.duration - 3); //I don't want the last 3 seconds.
//Stores the start time (useful for pause/resume):
startTime = context.currentTime - startAt; //Here I store the startTime but maybe the file has still not begun to play (should it be just startTime = context.currentTime?).
stopped = false;
}
function stop()
{
source.stop(0);
stopped = true;
}
function getCurrentTime()
{
return (stopped) ? 0 : context.currentTime - startTime;
}
How can I detect when exactly the source.start() method starts playing the file? So I can set the startTime variable value just at that moment, and never before.
Thank you very much in advance. I would really appreciate any kind of help.
From MDN (https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/start), about the first parameter of the start() function:
when (Optional)
The time, in seconds, at which the sound should begin to play, in the same time coordinate system used by the AudioContext. If when is less than (AudioContext.currentTime, or if it's 0, the sound begins to play at once. The default value is 0.
There is no evident issue with your code (although there is no example a call to play()): if you call play(0) or play(context.currentTime + someDelayInSeconds), start() should behave as expected. Unfortunately here the issue is that AudioBufferSource is not meant for big files. Again from the MDN doc of AudioBuffer (https://developer.mozilla.org/en-US/docs/Web/API/AudioBuffer):
Objects of these types are designed to hold small audio snippets, typically less than 45 s.
I suspect that for big something doesn't work very well with the "sound begins play at once" assumption (I also experienced it, although 20 seconds seems way too much...). Unfortunately there is no way to get the exact start time of AudioBufferSource in WebAudio yet.
If you don't have any real reason to load this big file with AudioBufferSource, I suggest you use a MediaElementSourceNode (https://developer.mozilla.org/en-US/docs/Web/API/MediaElementAudioSourceNode): as you can see from the example on the linked doc, it allows you to plug a simple HTML5 Audio element into the AudioContext. You then can have all usual control over the element itself, i.e. you also have access to the audioElement.currentTime property, which tells you the current playout time (in this case of the file itself, which is what you need). Additionally, you don't have to handle loading of the file in memory and could start playing as soon as some data is available.
context.currentTime starts counting the second you create the context object. That means if it takes 20 seconds for your audio to load, context.currentTime == 20.
To account for this delay, you can set a simple timer from the time that you create the context to the time that audio loading completes.
var context; //Create your context here
var audioLoadStart = new Date();
//Do audio load
var audioLoadOffset = (new Date() - audioLoadStart) / 1000;
currentTime = context.currentTime - audioLoadOffset - startTime;

Playing HTML5 Video on IPad and seeking

Very strange bug I can't seems to figure out.
I am trying to get an HTML5 video to play from a certain position when a user hits play. I am trying to have it seek right when the video starts to play.
On my play event I do this.currentTime = X
On the browser it works fine. But on the IPad, when I play the video, the video doesn't seek to the right position (it starts from zero).
Even more oddly, if I do the this.currentTime = X call in a setTimeout of let's say 1 second, it works on the IPad (sometimes).
On iOS, videos load at play time (see item #2), not at page load time. My guess is that the video is not loaded when you run this.currentTime = X, so it has no effect. This also explains why delaying the operation can sometimes fix the problem: sometimes it has loaded after a second, sometimes not.
I don't have an iOS device to test, but I'd suggest binding a loadeddata listener to the video so that your currentTime manipulation only happens after the video begins loading:
// within the play event handler...
if(!isIOSDevice) {
this.currentTime = X;
} else {
function trackTo(evt) {
evt.target.currentTime = X;
evt.target.removeEventListener("loadeddata", trackTo)
});
this.addEventListener("loadeddata", trackTo);
}
You'll need to set isIOSDevice elsewhere in your code, based on whether the current visit comes from an iOS device.
While this question is quite old the issue remains open. I discovered a solution that works on multiple tested iPads (1+2+3) for iOS 5 and 6 (iOS3+4 not tested):
Basically you first have to wait for the initial playing event, then add a one-time binder for canplaythrough and then for progress - only then can you actually change the the currentTime value. Any tries before that will fail!
The video has to start playing at first, which makes a black layer on top of the video element kinda handy. Unfortunately, sounds within the video canNOT be deactivated via JavaScript --> not a perfect UX
// https://github.com/JoernBerkefeld/iOSvideoSeekOnLoad / MIT License
// requires jQuery 1.8+
// seekToInitially (float) : video-time in seconds
function loadingSeek(seekToInitially, callback) {
if("undefined"==typeof callback) {
callback = function() {};
}
var video = $("video"),
video0 = video[0],
isiOS = navigator.userAgent.match(/(iPad|iPhone|iPod)/) !== null,
test;
if(isiOS) { // get the iOS Version
test =navigator.userAgent.match("OS ([0-9]{1})_([0-9]{1})");
// you could add a loading spinner and a black layer onPlay HERE to hide the video as it starts at 0:00 before seeking
// don't add it before or ppl will not click on the play button, thinking the player still needs to load
}
video.one("playing",function() {
if(seekToInitially > 0) {
//log("seekToInitially: "+seekToInitially);
if(isiOS) {
// iOS devices fire an error if currentTime is set before the video started playing
// this will only set the time on the first timeupdate after canplaythrough... everything else fails
video.one("canplaythrough",function() {
video.one("progress",function() {
video0.currentTime = seekToInitially;
video.one("seeked",function() {
// hide the loading spinner and the black layer HERE if you added one before
// optionally execute a callback function once seeking is done
callback();
});
});
});
} else {
// seek directly after play was executed for all other devices
video0.currentTime = seekToInitially;
// optionally execute a callback function once seeking is done
callback();
}
} else {
// seek not necessary
// optionally execute a callback function once seeking is done
callback();
}
});
}
the whole thing can be downloaded from my GitHub repo
apsillers is right. Once the video starts playing, the Quicktime player will come up and the video will not be seekable until the first 'progress' event is triggered. If you try to seek before then, you'll get an invalid state error. Here's my code:
cueVideo = function (video, pos) {
try {
video.currentTime = pos;
// Mobile Safari's quicktime player will error if this doesn't work.
} catch(error) {
if (error.code === 11) { // Invalid State Error
// once 'progress' happens, the video will be seekable.
$(video).one('progress', cueVideo.bind(this, video, pos));
}
}
}
Appreciate the attempts for answers below. Unfortunately, had to resort to just checking inside timeupdate if the currenttime was > 0 and < 1, if it was then went to that part of the video and removed the listener to timeupdate.
try to limit your X to 1 decimal
X.toFixed(1);
As you mentioned it works sometimes after a time of 1 second. Have you tried to set the position after the playing event fires? or maybe even the canplaythrough event
Take a look at the source of this page to see a whole list of events that can be used (in the javascript file)

Categories

Resources