How can I test if a sound is playing in SoundManager2? - javascript

I'm looking for a simple way to check if SoundManager2 is currently playing a sound. I was hoping for a function like "SoundManager.isSoundPlaying" but I don't see this functionality.

In SoundManager 2 there's a dynamic propierty called "playState" wich returns these values:
Numeric value indicating the current playing state of the sound.
0 = stopped/uninitialised
1 = playing or buffering sound (play has been called, waiting for data etc.)
Note that a 1 may not always guarantee that sound is being heard, given buffering and autoPlay status.
So you can do something like:
var myAudio = soundManager.createSound({
id: 'myAudioId',
url: "/audiofile/url.mp3",
autoPlay: true
});
if (myAudio.playState === 1) {
// audio playing (or buffering)
}
More info in the documentation page of SoundManager2

The main problem of "playState" is that it returns 1 even if the sound is under "pause" status.
A solution could be to check if the duration is not null, because once the sound is loaded the duration has always a value, then also check if the sound has been paused, because the duration value is kept even after you pause the sound.
A solution can be:
// assuming s is the soundmanager object
// playing status
if (s.duration&&!s.paused) {
}
else {
}

I don't think soundmanager has such a variable..
I suggest using a global variable "isPlaying" default it to false, and adding "isPlaying = !isPlaying" in your play/pause click event.

It actually has a way of saying if it's paused. Which is essentially the same thing.
Just do
if(!_audioInstance.paused){
// is playing
}

Related

WebRTC - Restart Video Stream After Calling stop()

So, I am using webRTC to create a local stream (video and audio), and want to be able to stop and restart the video of said stream.
At the point where I want to stop the stream I am getting the local video track:
var vidTrack = this.videoEl.srcObject.getTracks().find(track => track.kind == 'video')
I then call stop() on the track, which works, and turns off the camera-light indicator on my device (which is what I want). The problem is this seems to be a one way method, there is no way to restart the stream once I call stop() on it.
I have played with just toggling the enabled boolean on the track object, which DOES disable the track from coming through, but does NOT stop displaying the camera-light indicator on my device (which I need, and stop() does).
Just wondering if anyone has come across this issue/has ideas or solutions as to get what I need.
Here is the solution I ended up with for anyone who may see this in the future, based on Dirk V's response:
if (vidTrack && toggle && vidTrack.readyState && vidTrack.readyState == "ended") {
let newVideoStreamGrab = await navigator.mediaDevices.getUserMedia({
video: true
})
this.stream.removeTrack(this.stream.getVideoTracks()[0])
this.stream.addTrack(newVideoStreamGrab.getVideoTracks()[0])
} else {
vidTrack.stop()
}
The best way is to request the stream from the camera again after stopping it as there is no way to restart the stopped track.
The enabled flag is only used to allow or disallow the track to render frames. So this means that it doesn't affect the state of the camera.
When true, enabled indicates that the track is permitted to render its actual media to the output. When enabled is set to false, the track only generates empty frames.
source

HTML5 Audio Player not moving playhead to the end [duplicate]

A notable issue that's appearing as I'm building a simple audio streaming element in HTML5 is that the <audio> tag doesn't behave as one would expect in regards to playing and pausing a live audio stream.
I'm using the most basic HTML5 code for streaming the audio, an <audio> tag with controls, the source of which is a live stream.
Current outcome: When the stream is first played, it plays whatever is streaming as expected. When it's paused and played again, however, the audio resumes exactly where it left off when the stream was previously paused. The user is now listening to a delayed version of the stream. This occurrence isn't browser-specific.
Desired outcome: When the stream is paused, I want the stream to stop. When it is played again, I want it resume where the stream is currently at, not where it was when the user paused the stream.
Does anyone know of a way to make this audio stream resume properly after it's been paused?
Some failed attempts I've made to fix this issue:
Altering the currentTime of the audio element does nothing to streaming audio.
I've removed the audio element from the DOM when the user stops stream playback and added it back in when user resumes playback. The stream still continues where the user left off and worse yet downloads another copy of the stream behind the scenes.
I've added a random GET variable to the end of the stream URL every time the stream is played in an attempt to fool the browser into believing that it's playing a new stream. Playback still resumes where the user paused the stream.
Best way to stop a stream, and then start it again seems to be removing the source and then calling load:
var sourceElement = document.querySelector("source");
var originalSourceUrl = sourceElement.getAttribute("src");
var audioElement = document.querySelector("audio");
function pause() {
sourceElement.setAttribute("src", "");
audioElement.pause();
// settimeout, otherwise pause event is not raised normally
setTimeout(function () {
audioElement.load(); // This stops the stream from downloading
});
}
function play() {
if (!sourceElement.getAttribute("src")) {
sourceElement.setAttribute("src", originalSourceUrl);
audioElement.load(); // This restarts the stream download
}
audioElement.play();
}
Resetting the audio source and calling the load() method seems to be the simplest solution when you want to stop downloading from the stream.
Since it's a stream, the browser will stop downloading only when the user gets offline. Resetting is necessary to protect your users from burning through their cellular data or to avoid serving outdated content that the browser downloaded when they paused the audio.
Keep in mind though that when the source attribute is set to an empty string, like so audio.src = "", the audio source will instead be set to the page's hostname. If you use a random word, that word will be appended as a path.
So as seen below, setting audio.src ="", means that audio.src === "https://stacksnippets.net/js". Setting audio.src="meow" will make the source be audio.src === "https://stacksnippets.net/js/meow" instead. Thus the 3d paragraph is not visible.
const audio1 = document.getElementById('audio1');
const audio2 = document.getElementById('audio2');
document.getElementById('p1').innerHTML = `First audio source: ${audio1.src}`;
document.getElementById('p2').innerHTML = `Second audio source: ${audio2.src}`;
if (audio1.src === "") {
document.getElementById('p3').innerHTML = "You can see me because the audio source is set to an empty string";
}
<audio id="audio1" src=""></audio>
<audio id="audio2" src="meow"></audio>
<p id="p1"></p>
<p id="p2"></p>
<p id="p3"></p>
Be aware of that behavior if you do rely on the audio's source at a given moment. Using the about URI scheme seems to trick it into behaving in a more reliable way. So using "about:" or "about:about", "about:blank", etc. will work fine.
const resetAudioSource = "about:"
const audio = document.getElementById('audio');
audio.src = resetAudioSource;
document.getElementById('p1').innerHTML = `Audio source: -- "${audio.src}"`;
// Somewhere else in your code...
if (audio.src === resetAudioSource){
document.getElementById('p2').innerHTML = "You can see me because you reset the audio source."
}
<audio id="audio"></audio>
<p id="p1"></p>
<p id="p2"></p>
Resetting the audio.src and calling the .load() method will make the audio to try to load the new source. The above comes in handy if you want to show a spinner component while the audio is loading, but don't want to also show that component when you reset your audio source.
A working example can be found here: https://jsfiddle.net/v2xuczrq/
If the source is reset using a random word, then you might end up with the loader showing up when you also pause the audio, or until the onError event handler catches it. https://jsfiddle.net/jcwvue0s/
UPDATE: The strings "javascript:;" and "javascript:void(0)" can be used instead of the "about:" URI and this seems to work even better as it will also stop the console warnings caused by "about:".
Note: I'm leaving this answer for the sake of posterity, since it was the best solution I or anyone could come up with at the time for my issue. But I've since marked Ciantic's later idea as the best solution because it actually stops the stream downloading and playback like I originally wanted. Consider that solution instead of this one.
One solution I came up with while troubleshooting this issue was to ignore the play and pause functions on the audio element entirely and just set the volume property of the audio element to 0 when user wishes to stop playback and then set the volume property back to 1 when the user wishes to resume playback.
The JavaScript code for such a function would look much like this if you're using jQuery (also demonstrated in this fiddle):
/*
* Play/Stop Live Audio Streams
* "audioElement" should be a jQuery object
*/
function streamPlayStop(audioElement) {
if (audioElement[0].paused) {
audioElement[0].play();
} else if (!audioElement[0].volume) {
audioElement[0].volume = 1;
} else {
audioElement[0].volume = 0;
}
}
I should caution that even though this achieves the desired functionality for stopping and resuming live audio streams, it isn't ideal because the stream, when stopped, is actually still playing and being downloaded in the background, using up bandwidth in the process.
However, this solution doesn't necessarily take up more bandwidth than just using .play() and .pause() on a streaming audio element. Simply using the audio tag with streaming audio uses up a great deal of bandwidth anyway, because once streaming audio is played, it continues to download the contents of the stream in the background when it is paused.
It should be noted that this method won't work in iOS because of purposefully built-in limitations for iPhones and iPads:
On iOS devices, the audio level is always under the user’s physical control. The volume property is not settable in JavaScript. Reading the volume property always returns 1.
If you choose to use the workaround in this answer, you'll need to create a fallback for iOS devices that uses the play() and pause() functions normally, or your interface will be unable to pause the stream.
Tested #Ciantics code and it worked with some modifications, if you want to use multiple sources.
As the source is getting removed, the HTML audio player becomes inactive, so the source (URL) needs to be added directly after again to become active.
Also added an event listener at the end to connect the function when pausing:
var audioElement = document.querySelector("audio");
var sources = document.querySelector("audio").children;
var sourceList = [];
for(i=0;i<sources.length;i++){
sourceList[i] = sources[i].getAttribute("src");
}
function pause() {
for(i=0;i<sources.length;i++){
sources[i].setAttribute("src", "");
}
audioElement.pause();
// settimeout, otherwise pause event is not raised normally
setTimeout(function () {
audioElement.load(); // This stops the stream from downloading
});
for(i=0;i<sources.length;i++){
if (!sources[i].getAttribute("src")) {
sources[i].setAttribute("src", sourceList[i]);
audioElement.load(); // This restarts the stream download
}
}
}
audioElement.addEventListener("pause", pause);

HTML 5 audio .play() delay on mobile

I just built a real-time app using socket.io where a "master" user can trigger sounds on receiving devices (desktop browsers, mobile browsers). That master user sees a list of sound files, and can click "Play" on a sound file.
The audio playback is instant on browsers. On mobiles however, there is a 0.5-2 seconds delay (my Nexus 4 and iPhone 5 about 1 second and iPhone 3GS 1-2 seconds).
I've tried several things to optimize the audio playback to make it faster on mobiles. Right now (at the best "phase" of its optimization I'd say), I combine all the mp3's together in one audio file (it creates .mp3, .ogg, and .mp4 files). I need ideas on how I can further fix / improve this issue. The bottleneck really seems to be in the hmtl 5 audio methods such as .play().
On the receivers I use as such:
<audio id="audioFile" preload="auto">
<source src="/output.m4a" type="audio/mp4"/>
<source src="/output.mp3" type="audio/mpeg"/>
<source src="/output.ogg" type="audio/ogg"/>
<p>Your browser does not support HTML5 audio.</p>
</audio>
In my JS:
var audioFile = document.getElementById('audioFile');
// Little hack for mobile, as only a user generated click will enable us to play the sounds
$('#prepareAudioBtn').on('click', function () {
$(this).hide();
audioFile.play();
audioFile.pause();
audioFile.currentTime = 0;
});
// Master user triggered a sound sprite to play
socket.on('playAudio', function (audioClip) {
if (audioFile.paused)
audioFile.play();
audioFile.currentTime = audioClip.startTime;
// checks every 750ms to pause the clip if the endTime has been reached.
// There is a second of "silence" between each sound sprite so the pause is sure to happen at a correct time.
timeListener(audioClip.endTime);
});
function timeListener(clipEndTime) {
this.clear = function () {
clearInterval(interval);
interval = null;
};
if (interval !== null) {
this.clear();
}
interval = setInterval(function () {
if (audioFile.currentTime >= clipEndTime) {
audioFile.pause();
this.clear();
}
}, 750);
}
Also considered blob for each sound but some sounds can go for minutes so that's why I resorted to combining all sounds together for 1 big audio file (better than several audio tags on the page for each clip)
Instead of pausing / playing, I simply set the volume to 0 when it shouldn't be playing, and back to 1 when it should be playing. The Audio methods currentTime and volume don't slow the audio playback at all even on an iPhone 3GS.
I also added the 'loop' attribute to the audio element so it never has to be .play()'ed again.
It was fruitful to combine all mp3 sounds together because this solutions can work because of that.
Edit: audioElement.muted = true or audioElement.muted = false makes more sense.
Edit2: Can't control volume on user's behalf on iOS so I must pause() and play() the audio element as opposed to just muting and unmuting it.
Your setup is working well on desktop because of the preload attribute.
Unfortunately, here's Apple on the subject of preload:
Safari on iOS never preloads.
And here's MDN:
Note: This value is often ignored on mobile platforms.
The mobile platforms are making a tradeoff to save battery and data usage to only load media when it's actually interacted with by the user or programmatically played (autoplay generally doesn't work for similar reasons).
I think the best you're going to do is combining your tracks together, as you said you've done, so you don't have to pay the initial load-up "cost" as much.
I was having the same delay issue when testing in mobile. I found out what some HTML 5 games are using for audio since games demand very low latencies. Some are using SoundJS. I recommend you try that library out.
You can find a speed comparison between using the HTML Audio tag vs using SoundJS here:
http://www.nickfrazier.com/javascript/audio/ui/2016/08/14/js-sound-libraries.html
(test in mobile to hear the difference)
From my tests SoundJS is much faster.
In fact, it's Good enough to be used in a game, or for sound feedback in a user interface.
Old question but here is my solution using one of the answer above:
const el = document.createElement("audio");
el.muted = true;
el.loop = true;
const source = document.createElement("source");
source.src = lineSe;
source.type = "audio/mpeg";
el.appendChild(source);
// need to call this function after user first interaction, or safari won't do it.
function firstPlay() {
el.play();
}
let timeout = null;
function play() {
// In case user press the button too fast, cancel last timeout
if (lineSeTimeout) {
clearTimeout(timeout);
}
// Back to beginning
el.currentTime = 0;
// unmute
el.muted = false;
// set to mute after the audio finish. In my case 500ms later
// onended event won't work because loop=tue
timeout = setTimeout(() => {
// mute audio again
el.muted = true;
}, 500);
}

<video> element with looping does not loop videos seamlessly in Chrome or Firefox

<video width="640" height="360" src="http://jakelauer.com/fireplace.mp4" autoplay loop muted/>
Fiddle here: http://jsfiddle.net/bWqVf/
IE9 does a decent job of it. Is there any recommendation for ways to overcome this? It is very obvious in videos like this one that SHOULD seamlessly loop, but have an annoying skip/pause.
EDIT:
As you can see, if I use javascript to simulate the loop, there's a measurable lag: http://jsfiddle.net/bWqVf/13/
The problems seem to be related to how both Chrome and FF fills the pre-load buffers. In both cases they seem to ignore the loop flag and "reset" the buffers from start meaning in that case that at the end the buffers are emptied and pre-loaded again when video starts causing a slight delay/jump.
IE seem to consider the loop flag and continue to fill also towards the end.
This means it's gonna be very hard to make this look seamless. I tried several techniques over a few hours including pre-caching the first frames to 15 frames off-screen canvases. The closest I could get to seamless was modifying the video to have two segments in it (I do not (no longer) have capable hardware so I needed to reduce the dimension as well to test - see fiddle).
However, there are drawbacks here as well:
The video is double length
You need to play two instances at the same time
Two downloads of the same video happens
Lag compensation will vary from computer to computer
Browser updates in the future can influence good/bad how the result will end up to be.
In other words - there is no stable solution to get around the problem with these browsers.
I would recommend an extension to what I mention above, to pre-loop some segments. This way you can reduce the glitch.
However, to share what I did here goes.
First I extended the video with an extra segment (and reduced the dimension to run it on my computer):
Then I used the following code to do an overlapping loop. That is:
I start the videos at the same time, but one video from the middle.
The video that is currently => middle is shown
I use a canvas element to draw the video onto
When at end the current video is switched so that the new video is still the one being played from the middle
The theory here is that this will mask the glitch you get at the start as the video playing is always in the middle (starting on the second segment).
The code looks like this:
As the videos are loaded async we need to count the loads as this technique uses two video instances and the browser seem to be unable to share the download.
We also set a new position for video 1 to be at the middle. An event is raised for this when video is moved and ready, so we start everything from that point:
v1.addEventListener('canplay', init, false);
v2.addEventListener('canplay', init, false);
v1.addEventListener('timeupdate', go, false);
Handlers:
function init() {
count--; /// = 2
/// both videos are loaded, prep:
if (count === 0) {
length = v1.duration;
mid = length * 0.5;
current = mid;
/// set first video's start to middle
v1.currentTime = mid + lag;
}
}
function go() {
/// remove listener or this will be called for each "frame"
v1.removeEventListener('timeupdate', go, false);
v1.play();
v2.play();
draw();
}
The lag value is an attempt to compensate for the difference between the two videos starting as they don't start at the exact same time.
The main code draw simply switches between the videos depending on the position of the main video (v1) - the frame rate is also reduce to 30 fps to reduce overhead of drawImage as requestAnimationFrame runs optimally at 60 fps (the video here is 30 fps so we only need to draw a frame every other time):
function draw() {
/// reduce frame-rate from 60 to 30
if (reduce === true) {
reduce = false;
requestAnimationFrame(draw);
return;
} else {
reduce = true;
}
/// use video that is >= middle time
var v = v1.currentTime >= mid ? v1 : v2;
/// draw video frame onto canvas
ctx.drawImage(v, 0, 0);
requestAnimationFrame(draw);
}
Now, using canvas opens up other possibilities as well such as making for example a cross-fade between the two videos to smooth the transition further. I didn't implement this as it is outside the scope (in size/broadness), but worth to mention as that could be a solution in itself.
In any case - as mentioned, this is a solution with many drawbacks but it is the closest I could get to reduce the glitch (using Chrome).
The only solution that can work properly is an internal browser driven one as you would need access to the buffers to be able to do this fully seamlessly.
My "solution" is in essence saying: forget it! It won't work in these browsers, use an repeated looped video instead. :-)
I think the problem is related to browser-specific-video-handling.
As a quirk, you can achieve less latency converting the video to webm, but you should place it before mp4 source, ie:
<video width="640" height="360" autoplay loop muted>
<source src="http://jakelauer.com/fireplace.webm" type="video/webm" />
<source src="http://jakelauer.com/fireplace.mp4" type="video/mp4" />
</video>
Heureka!
We've found the actual, real, work-around-free solution to this problem over at where I work. It explains the inconsistent behavior through multiple developers as well.
The tl;dr version is: Bitrates. Who would've guessed? What I suppose is that many people use standard values for this that usually are around 10 Mbit/s for HD videos if you use the Adobe Media Encoder. This is not sufficient. The correct value would be 18 Mbit/s or maybe even higher. 16 is still a bit janky. I cannot express how well this works. I've, by now, tried the messiest workarounds for about five hours until I found this together with our video editor.
I hope this helps everyone and saves you tons of time!
I also hope it's okay that I posted this in another thread as well, but there are a bunch of questions of the same type about this and I wanted to reach a lot of people.
I don't think your problem is "code-related". It has more to do with the actual video itself. It would be much better if you edit your video for a seamless looping.
Have a look HERE as it will give you some guidance on how to do so.
Hope this helps you.
EDIT: You can try breaking the video up into two sections: the intro and the looping part. Make a <video> element for each one and position them in the same place, with the second video hidden. Set an "ended" event on the intro to swap out the display and start the second video. Then, you can set the loop attribute on the second video element.
You shouldn't have a problem getting the two videos to play seamlessly together as long as you have the preload attribute on at least the looping video.
If that doesn't work, try making two video elements with the same looping video. While one is playing, you can hide the other and set its currentTime back to zero, so any seeking delay will happen when nobody is looking.
If none of the above works for you, then you can try an other way with javascript. Note that i haven't tested the below code. What it does is starting the video from the 2nd second and when the video reaches the 4th second it will start it again (from the 2nd second).
function playVideo() {
var starttime = 2; // start at 2 seconds
var endtime = 4; // stop at 4 seconds
var video = document.getElementById('player1');
//handler should be bound first
video.addEventListener("timeupdate", function() {
if (this.currentTime >= endtime) {
this.play();
}
}, false);
//suppose that video src has been already set properly
video.load();
video.play(); //must call this otherwise can't seek on some browsers, e.g. Firefox 4
try {
video.currentTime = starttime;
} catch (ex) {
//handle exceptions here
}
}
The solution that worked for me (and doesn't require a huge amount of JavaScript) is something like:
var video = document.getElementById('background-video');
var loopPoint = 15; // s
function resetVideo() {
if (video.currentTime >= loopPoint) {
video.currentTime = 0;
}
}
video.addEventListener('timeupdate', resetVideo);
Unfortunately I guess this is quite expensive because it will use a callback every time the time of the video/audio updates.
This issue happens to me using the Chromium wrapper with Electron. Regardless of that, I got closer to solving the issue ( not close enough ). Here's a list of things that improved the looping to near seamless jumping back from cuepoint A to B:
A mp4 video with keyframes only was key (increases video size a bit)
Get a framerate-sensitive loop. This little tool helps a lot when using keyframes and timecodes: http://x3technologygroup.github.io/VideoFrameDocs/#!/documentation/FrameRates
( 3. The last thing is only needed if things in 1 & 2 do not help. I've loaded the whole video with an XmlHTTPrequest to fill the buffer completely. )
var xhr = new XMLHttpRequest();
xhr.open('GET', '../assets/video/Comp1.mp4', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 0) { // I used chromium and electron, usually status == 200 !
var myBlob = this.response;
var vid = URL.createObjectURL(myBlob);
// myBlob is now the blob that the object URL pointed to.
var v = document.getElementById("video");
v.src = vid;
// not needed if autoplay is set for the video element
v.play();
// This requires the VideoFrame-tool (see Nr. 2.)
var videoFrame = new VideoFrame({
id: 'v',
frameRate: 25, // ! must match your video frame rate
callback: function(response) {
// I jump from fram 146 to 72
if (videoFrame.get() === 146) {
// now, jump! Dealbreaker is that the seek is stopping the video
// and the few ms to play it again bugger up the experience.
// Any improvements welcome!
videoFrame.seekBackward(71, function() {
v.play();
});
}
}
});
videoFrame.listen('frame', 25);
v1.play();
}
}
xhr.send(null);
The only issue I encounter with this code is that the seeking stops the video and play() needs to be triggered again. This causes a glitch which I solved by going 3 frames back before the actual cuepoint I want to jump to.
This is still inaccurate if used on different hardware with different videos, but maybe it gets you closer to a solution -- an me too! :)
The problem is nothing.
The starting slide and ending slide is different. If both the slides are same, the looping will looks fine. Because of mismatch in these slides only, it looks like pausing at some seconds. Avoid those things and try out.
check below jsFiddle URL carefully i add console.log and trace video tag event like play, pause, ended etc, i check in window chrome version 28 (working loop for me without fire pause event )
http://jsfiddle.net/bWqVf/6/
Ok... after much trial and error, this is what finally worked for me. It seemed to me that the video is not updating after it's ended, so I just remind it all of its properties again when it finishes playing.
myVid.setAttribute('src', "videos/clip1.mp4");
myVid.autoplay = true;
myVid.addEventListener('ended', vidEnded);
function vidEnded()
{
myVid.setAttribute('src', "videos/clip1.mp4");
myVid.autoplay = true;
}

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