8
votes

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).

5

5 Answers

4
votes

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.

3
votes

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

1
votes

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));
        }
    }
}
-1
votes

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.

-2
votes

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)