javascriptvideovideo.js

How can I make a video playing with video.js keep playing in the background on iOS?


On my website I have videos that play with video.js. When using the site through Safari on iOS, if I background Safari or lock my phone, the video pauses and I need to find the media controls and press the play button for the video to keep playing in the background. I have seen other websites using video.js where videos are able to keep playing automatically in the background.

How can I make my videos keep playing in the background on iOS?

This is my <video> element (jsx). I have tried different combinations of playsinline, autoplay, and preload="auto".

      <video
        class="video-js"
        ref={(el) => void (videoRef = el)}
        playsinline
        autoplay
      />

And here is my code to setup up video.js, I have not been able to identity any parameters which change the behavior of playback when backgrounding my website:

    const player = videojs(
      videoRef,
      {
        controls: true,
        preload: 'auto',
        sources, // Array of `m3u8` URLs
        playbackRates: [1, 1.25, 1.5, 1.75, 2],
        html5: {
          hls: {
            overrideNative: false,
          },
          nativeVideoTracks: true,
          nativeAudioTracks: true,
          nativeTextTracks: true,
        },
      },
      async () => {
        try {
          await player.play();
        } catch (e) {
          // The play method is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
          console.warn('Could not automatically play video', e);
        }
      },
    );

I have had some success with the following code:

document.addEventListener('visibilitychange', function() {
  if (document.visibilityState === 'hidden') {
    video.play();
  }
});

This works, however, it also plays paused videos when the page is backgrounded. Tracking the pause event doesn't work because pause is fired whenever the page is backgrounded and not just on user pauses.


Solution

  • This appears to work:

        let userPaused = false;
    
        player.on('pause', () => {
          userPaused = Boolean(player.userActive());
        });
    
        player.on('playing', () => {
          userPaused = false;
        });
    
        document.addEventListener('visibilitychange', function () {
          if (document.visibilityState === 'hidden' && !userPaused) {
            try {
              player.play();
            } catch (e) {
              console.warn('Cannot play video while page is hidden.', e);
            }
          }
        });
    

    In my testing this accurately tracks if the user has paused the video vs some other event, and keeps the video playing in the background when the user backgrounds the page.