HTML5 Video - currentTime not setting properly on iPhone

13,474

Solution 1

From my findings the issue seems to be that on iPhone only (iPad works fine) the currentTime property will not be set correctly until the "canplaythrough" event, however changing the currentTime at that point will cause a noticeable hiccup. The solution for that would be to intentionally pause the video after calling load...

myVideo.load();
myVideo.pause();        

...and then call play in the event when the time has reset.

The second problem however is when the duration of the new movie is shorter then the currentTime position. In this case not only does currentTime fail to set but "canplaythrough" is never called, and QT just sits at the end of the video doing nothing.

I discovered the solution to both problems was to force a secondary load if the currentTime was not reset in the event BEFORE "canplaythrough". Its a bit round about with the timer callback but it seems to do the trick;

var myVideo = document.getElementById("video1"); 

myVideo.addEventListener("canplay", function() {
    console.log(" canplay: before = " + myVideo.currentTime);
    myVideo.currentTime = 0.1;
    console.log(" canplay: after = " + myVideo.currentTime);

    if( myVideo.currentTime < 1 ) {
        myVideo.play();
    }
    else {
        myVideo.load();
        myVideo.pause();
        setTimeout(checkStarted, 500);
    }
}, false);

function checkStarted()
{ 
    console.log(" checkStarted called");
    myVideo.play();
}

Solution 2

TL;DR: change currentTime on the loadeddata event. This works for audio too.

It looks like Safari (and the problem is still appearing for me on Safari 11.1) is Safari will not allow currentTime to be changed when a video is first loaded IF it hasn't loaded a frame for that currentTime yet. The bigger problem: the wrong solution can break Chrome (and likely other browsers too).

Fortunately, we have a lot of events we can listen for while media is loading:

During the loading process of an audio/video, the following events occur, in this order:

  1. loadstart
  2. durationchange
  3. loadedmetadata
  4. loadeddata
  5. progress
  6. canplay
  7. canplaythrough

-W3Schools (I know it's not a preferred source, but I couldn't find the same info on MDN)

I tried adjusting currentTime on different events, but on the first 3 events, Safari would move the time back to 0; and on 5 and 6 it seemed to prevent the video from playing in Chrome, because it would get stuck at currentTime (which I could've worked around, but I thought there was a better solution).

(I didn't want to have to load the whole file to go to the right spot, because I want to support hefty videos. So canplaythrough wasn't an option for me.)

The description for the loadeddata event reads:

The loadeddata event is fired when the first frame of the media has finished loading.

-MDN

When I changed currentTime on loadeddata, Safari could tell that the frame was loaded and available, and would update correctly. It also wouldn't cause Chrome to freeze in a single spot while playing.

The problem and solution are identical for audio.

Solution 3

I had to set the preload attribute to metadata (on the HTML of the main video element) and set the currentTime of the video element within the loadedmetadata event listener.

 <video id="myVideo" preload="metadata">
    <source src="/path/to/video" type="video/mp4">
 </video>

JS

VideoEl.addEventListener('loadedmetadata', VideoMetaDataLoaded);

function VideoMetaDataLoaded() {
  VideoEl.currentTime = newTime;
}

Solution 4

Below is my Angular/Ionic solution to restore the video position. It works on IOS, Android, Chrome and Safari.

HTML:

<video         preload="metadata"
               poster="{{ resource.thumbnail_file }}"
               playsinline webkit-playsinline
               #videos>
...
</video>

Typescript:

@ViewChildren('videos') videos: QueryList<any>;
videoCache: Map<number, number> = new Map<number, number>();

private restoreVideoPositions() {
    var context = this;
    setTimeout(() => {
      this.videos.forEach(function (item, idx) {
        var video = item.nativeElement;
        var currentTime = context.videoCache.get(video.id);

        if (currentTime != null && currentTime > 0) {
          console.log('add listener', currentTime);

          video.addEventListener("loadeddata", function () {
            if (video.readyState >= 3) {
              video.currentTime = currentTime;
              // video.play();
            }
          });
          video.load();
         }
      });
    }, 0);
}

private storeVideoPositions() {
    var context = this;
    this.videoCache.clear()
    this.videos.forEach(function (item, idx) {
      var video = item.nativeElement;
      video.pause();
      context.videoCache.set(video.id, video.currentTime)
    });
  }

Solution 5

I used a combination of the last two answers here, but had to reduce the ready state limit to 2 (HTMLVideoElement.prototype.HAVE_CURRENT_DATA) as the loadeddata event on iOS would often never come in higher than 2.

Share:
13,474

Related videos on Youtube

Quinnland23
Author by

Quinnland23

Updated on September 15, 2022

Comments

  • Quinnland23
    Quinnland23 over 1 year

    I have a basic HTML5 video set up from which I load one of four videos. The problem I'm having is that when I load the next video, it continues playing from the previous time position. Efforts to set the currentTime property seem to be either short lived or ignored entirely.

    I have added listeners to a collection of events and have something like this in each one;

    myPlayer.addEventListener("loadeddata", function() {
            console.log(" loadeddata: before = " + myPlayer.currentTime);
            myPlayer.currentTime = 0.1;
            console.log(" loadeddata: after = " + myPlayer.currentTime);
        }, false);
    

    Sometimes I see the time change for one event but not persist correctly;

    durationchange: before = 19.773332595825195
    durationchange: after = 0.10000000149011612
    
    loadedmetadata: before = 0.10000000149011612
    loadedmetadata: after = 19.773332595825195
    
    loadeddata: before = 19.773332595825195
    loadeddata: after = 0.10000000149011612
    
    canplay: before = 0.10000000149011612
    canplay: after = 19.773332595825195
    

    And sometimes it never even seems to set at all;

    durationchange: before = 50.66666793823242
    durationchange: after = 50.66666793823242
    
    loadedmetadata: before = 50.66666793823242
    loadedmetadata: after = 50.66666793823242
    
    loadeddata: before = 50.66666793823242
    loadeddata: after = 50.66666793823242
    
    canplay: before = 50.66666793823242
    canplay: after = 50.66666793823242
    

    This seems similar to the issue here but there didn't seem to be any resolution. Has anyone encountered this issue on iPhone before?

  • Captain Hat
    Captain Hat over 3 years
    Hello ccates. I don't know anything about iOS development, but I'm not sure that this answer gives any information additional to that which is provided by existing answers to this question. It's also not a very detailed or reproducible answer. Please could you review the 7 existing answers and check that this answer gives additional help? If you feel that it does, please add some detail and make the new information clear. If not, please delete this answer.
  • ccates
    ccates about 3 years
    I had the same issue as the one described above. None of the answers here fixed my issue so I had to come up with my own unique solution. What I commented worked for me and fixed the video not loading on mobile safari. When I realized that, I took the time to come back here and post what I found. I definitely don’t feel it should be down voted though. I’m not deleting my answer because it will help someone in the future since none of these answers fixed my problem.
  • ccates
    ccates about 3 years
    I am unclear about your position on this thread. Are you currently having this issue or are you just going around on this site and down voting comments whenever you feel like it?
  • Captain Hat
    Captain Hat about 3 years
    Sorry, it sounds like I misunderstood your answer - I've removed the downvote. I got to this question via the 'first posts' review queue, where S.O users review first posts from across the site and edit/vote/comment on them to help improve site quality.
  • Captain Hat
    Captain Hat about 3 years
    Does this answer stand alone, or is it an addition to another answer for this question? If the latter, you might consider adding a comment or even suggesting an edit to that answer.
  • ccates
    ccates about 3 years
    I would say it can be used as a single solution to the problem. I added some example code for context to the answer.. hope it helps :)
  • Captain Hat
    Captain Hat about 3 years
    Thanks ccates - code examples are always helpful
  • Wilter Monteiro
    Wilter Monteiro over 2 years
    This answer does not address OP's question properly. The problem is not related to where the video is served from and OP is/was having trouble with iPhone(Safari) not chrome.
  • TotomInc
    TotomInc about 2 years
    Thanks for your answer. For anyone coming here, this fixed an issue on Safari (iPad OS + iPhone iOS). You may also want to read video.duration after "loadedmetadata" have been called, as it is no available before.