admin管理员组

文章数量:1394159

Mobile Safari only lets you play one audio or video stream at a time.

Multiple Simultaneous Audio or Video Streams

Currently, all devices running iOS are limited to playback of a single audio or video stream at any time. Playing more than one video—side by side, partly overlapping, or pletely overlaid—is not currently supported on iOS devices.

If you play a second video the first stops. I need to detect when this occurs so that I can fade back in a play button that appears next to the video, and I'd also like to reset it to the first frame too because leaving it mid animation looks terrible.

Unfortunately the <video> element doesn't seem to trigger either the 'ended' or 'paused' event when the video is forced to stop like this.

 $('video').off('ended pause').on('ended paused', function ()
 {
     alert('ended');   // don't get here except when the video naturally ends
 }

I can't seem to find any other events that correspond to 'forced stop'. (See Apple events for HTMLMediaElement)

Do I have to do this by myself and add a .playing class to my video and check for it when playing a second video? This is workable if I control every video on the page, but as soon as I stick a YouTube video in there too it bees a lot more plicated. Any better solutions

Mobile Safari only lets you play one audio or video stream at a time.

Multiple Simultaneous Audio or Video Streams

Currently, all devices running iOS are limited to playback of a single audio or video stream at any time. Playing more than one video—side by side, partly overlapping, or pletely overlaid—is not currently supported on iOS devices.

If you play a second video the first stops. I need to detect when this occurs so that I can fade back in a play button that appears next to the video, and I'd also like to reset it to the first frame too because leaving it mid animation looks terrible.

Unfortunately the <video> element doesn't seem to trigger either the 'ended' or 'paused' event when the video is forced to stop like this.

 $('video').off('ended pause').on('ended paused', function ()
 {
     alert('ended');   // don't get here except when the video naturally ends
 }

I can't seem to find any other events that correspond to 'forced stop'. (See Apple events for HTMLMediaElement)

Do I have to do this by myself and add a .playing class to my video and check for it when playing a second video? This is workable if I control every video on the page, but as soon as I stick a YouTube video in there too it bees a lot more plicated. Any better solutions

Share Improve this question edited May 12, 2014 at 8:06 Charles 51.5k13 gold badges106 silver badges144 bronze badges asked May 11, 2014 at 11:16 Simon_WeaverSimon_Weaver 146k92 gold badges680 silver badges717 bronze badges 3
  • Have you tried listening to the webkitbeginfullscreen and webkitendfullscreen events? – nils Commented May 13, 2014 at 22:09
  • webkitendfullscreen is only called if the video has actually played in full screen, which for me applies for the iPhone. it's on an iPad where the video is playing in-page that I have issues when another video starts. webkitendfullscreen isn't raised in this case. there just don't seem to be any events on the list that apply – Simon_Weaver Commented May 14, 2014 at 0:53
  • to be fair though this seems like a problem even YouTube hasn't fixed. in fact for YouTube it's even worse. the second video never even starts playing and just buffers forever while the first video continues to play (you can see this behavior on any website that hosts two youtube videos - such as failblog.). i just got really frustrated because I couldn't even seem to find discussion of this issue anywhere else – Simon_Weaver Commented May 14, 2014 at 0:55
Add a ment  | 

2 Answers 2

Reset to default 2 +25

This solution feels a bit hackish but if the Safari mobile browser does not throw an event when the video stopped then there are not many options left -

To detect if a video has stopped we can first make sure the script knows when a video is playing, so:

var isPlaying1 = false,
    isPlaying2 = false, // if more videos an array is better to use, or reuse
    lastTime1 = 0,
    lastTime2 = 0;      // etc.

$('video1').on('playing', function() {
    isPlaying1 = true;
});

$('video1').on('ended', function() {
    isPlaying1 = false;
});

This sets a playing flag when video started. Use the ended event too, to set it to false in case it played to the end or was manually stopped.

Next step is to poll the current time of the video playing to see if it changes. If no change it will indicate the video is not playing (and not caught by the ended event):

function check() {

    if (isPlaying1) {
        var video = $('video1')[0];
        if (video.currentTime === lastTime1) {
            // video has stopped
            isPlaying1 = false;
            video.currentTime = lastTime1 = 0; //reset time
        }
        else {
            lastTime1 = video.currentTime;
        }
    }
}

var timerID = setInterval(check, 1000/15);  // start check

The check rate should not be higher than 30 fps. This is because the time-stamp would not be changed from what would correspond to the time from one frame to another. To be safe use half the rate as in the example for the highest known frame rate (videos are very rarely above 30 fps in US or 25 fps in Europe). If you know the videos will play at a lower rate the lowest rate should be the value here. It can also be lowered to reduce load in general.

To stop the checking simply call:

clearInterval(timerID); // timerID is in global scope

You may run into a race condition between ended event and the checking (as both creates asynchronous behavior). If it matters depends on how you handle the two situations.

As said, it feels "hackish", it's untested on my part (only theorized as I am unable to set up an entire test environment for it at the moment) but hopefully it will help you get around this limitation of the Safari mobile browser.

If the event doesn't fire only when another video plays, perhaps when we play a video we can simply trigger the ended event of all the other videos that was playing. To keep track of playing videos we could use a javascript property attached to the video object. Something like

$("video").on("ended",function(){
    this.playing=false
    this.currentTime=0
    console.log(this.src+" has ended")
})


$("video").on("play",function(){
    var that=this
    this.playing=true //video that is playing is "playing"
    $("video").each(function(i,o){
        if(o.playing && !$(o).is($(that)) ){ //for all the other "playing" video
          o.pause()
          $(o).trigger("ended")
        }
    })

})

Example (Tested on iPad mini)

The flip side to this is that, this will also apply to desktop browsers which may not be desirable. As for youtube videos embedded in the same page, we might be able to use Youtube's APIs. The APIs use postMessage to municate with each other, so I used the code below to stop the video tags, when a youtube video is playing.

window.addEventListener('message',function(event) {
    //probably should check origin
    var data=JSON.parse(event.data)
    if(data.info && 
     ( data.info.playerState == 1   //buffering
       || data.info.playerState == 3 )){ //or playing
        $("video").each(function (i, o) {
            if(o.playing){
              o.pause()
              $(o).trigger("ended")
            }
        })
    }
},false);

Example

本文标签: javascriptDetect when mobile Safari stops playing a video because a second video startsStack Overflow