[SOLVED] Video is not playing on tablet devices without multiple interactions

Hi all,

In a game we are building we are using html videos.
On desktop the game is working as expected. However, on tablet devices the first video is playing and later in the game when the next video is called it won’t play.

These videos are shown like this:

VideoManager.prototype.playVideo = function (videoName) {
    if (VideoManager.isPlaying === true) {
        return;
    }
    VideoManager.isPlaying = true;
    // Create HTML Video Element to play the video
    var video = document.createElement('video');
    VideoManager.video = video;
    video.id = 'video';
    video.loop = false;

    // muted attribute is required for videos to autoplay
    video.muted = false;

    if (this.app.touch) {
        video.controls = true;
    }
    
    // critical for iOS or the video won't initially play, and will go fullscreen when playing
    video.playsInline = true;

    // needed because the video is being hosted on a different server url
    video.crossOrigin = "anonymous";

    // autoplay the video
    video.autoplay = false;

    // set video source
    var videoFile = this.app.assets.find(videoName).getFileUrl();
    video.src = videoFile;

    // iOS video texture playback requires that you add the video to the DOMParser
    // with at least 1x1 as the video's dimensions
    var videoStyle = video.style;
    videoStyle.width = '100vw';
    videoStyle.height = '100vh';
    videoStyle.position = 'fixed';
    videoStyle.opacity = '100%';
    videoStyle.zIndex = '1000';
    // videoStyle.pointerEvents = 'none';
    videoStyle.objectFit = 'cover';
    videoStyle.transition = 'all 1s';
    videoStyle.opacity = '0';


    document.body.appendChild(video);

    // video.addEventListener('canplaythrough', function (e) {
    video.addEventListener('canplay', function (e) {
        // alert("video is ready to play");
        this.isPlaying = true;
        document.body.appendChild(video);
        videoStyle.opacity = '1';
        VideoManager.isPlaying = true;
        document.body.querySelector('#application-canvas').style.display = "none";
        setTimeout(function () {

            video.play();
        }, 1000);
    }.bind(this));

    video.addEventListener('ended', function (e) {
        document.body.querySelector('#application-canvas').style.display = "block";
        this.isPlaying = false;
        // alert("video is geeindigd");
        videoStyle.opacity = '0';
        document.body.removeChild(video);
        this.app.fire("video:ended");
        VideoManager.isPlaying = false;
    }.bind(this));

};

To show the problem without you having to play the game I made a basic scene where I am calling to play a short video 3 times. I am calling them like this.

// initialize code called once per entity
VideoButton.prototype.initialize = function () {
    this.entity.element.on("click", function () {
        alert("clicked");


        setTimeout(function () {
            this.app.fire("video:play", "Cutscene.mp4");
        }.bind(this), 0);
        setTimeout(function () {
            this.app.fire("video:play", "Cutscene.mp4");
        }.bind(this), 4000);
        setTimeout(function () {
            this.app.fire("video:play", "Cutscene.mp4");
        }.bind(this), 8000);
    }, this);

};

What’s surprising me is that the behaviour is different again.
In this example the video won’t show until the user interacts another time.

Does anybody know why this is happening on tablets?

Here is the example scene:
https://playcanv.as/b/tMyLnnSV/

Which devices/tablets are you testing on? iOS and Android behave differently with Video

I am testing on several iPads.
As for browsers, I am using Safari and Google Chrome.

Can you share the test project you are using publicly please?

As the videos are set to play on setTimeout, they may not play properly as iOS and mobile devices are quite specific on video playback to require user input if they aren’t muted

Here is the sample project.
playcanvas.com/project/921842/

There are timeouts in the sample project just to fire the event 3 times.

In the original game the videos are called solely by the fire event.

Looks like the issue is due to iOS not firing events on the video element such as canplaythrough or canplay unless there is a user input event AFTER The video element is added to the page or that autoplay and muted are true.

I’ve modified a fork of the project that ‘works’ on iOS here: https://playcanvas.com/editor/scene/1406553

Depending on your needs, you would either need autoplay and muted to be enabled OR add the video element to the document first before trying to play it via the button

1 Like

Thanks yaustar,

Your fork works. However it is muted and I need it to be unmuted.

I managed to fix it by going another route.
In the game I am making the first video did work, also on tablet. The videos after that one did not. So I thought it might be because there are multiple video elements created and deleted and I guess the mobile browsers don’t like that.

So now I am making one video element on launch, hide it and replace the video source when calling the function.

This is my updated code. Note that autoplay = true and muted = false.

var VideoManager = pc.createScript('videoManager');

// initialize code called once per entity
VideoManager.prototype.initialize = function () {

    // listen for the video:play event
    this.app.on('video:play', this.playVideo, this);
    this.app.on('video:skip', function () {
        // VideoManager.video.pause();
        VideoManager.video.currentTime = VideoManager.video.duration - 1;
    }, this);

    // remove player:move event listeners when script destroyed
    this.on('destroy', function () {
        this.app.off('video:play', this.playVideo, this);
    });


    // Create HTML Video Element to play the video
    var video = document.createElement('video');
    video.id = 'video';
    video.loop = false;

    // muted attribute is required for videos to autoplay
    video.muted = false;

    // critical for iOS or the video won't initially play, and will go fullscreen when playing
    video.playsInline = true;

    // needed because the video is being hosted on a different server url
    video.crossOrigin = "anonymous";

    // autoplay the video
    video.autoplay = true;

    // iOS video texture playback requires that you add the video to the DOMParser
    // with at least 1x1 as the video's dimensions
    var videoStyle = video.style;
    videoStyle.width = '100vw';
    videoStyle.height = '100vh';
    videoStyle.position = 'fixed';
    videoStyle.opacity = '100%';
    videoStyle.zIndex = '1000';
    videoStyle.objectFit = 'cover';
    videoStyle.transition = 'all 1s';
    videoStyle.display = 'none';

    document.body.appendChild(video);
    VideoManager.video = video;

    // Keyboard event to test certain scenes
    this.app.keyboard.on(pc.EVENT_KEYDOWN, this.onKeyDown, this);
};

// Event handler called when key is pressed
VideoManager.prototype.onKeyDown = function (event) {
    // Check event.key to detect which key has been pressed
    if (event.key === pc.KEY_SPACE) {
        console.log("Space key pressed");
        this.app.fire("video:skip");
    }
};

VideoManager.prototype.playVideo = function (videoName) {
    if (VideoManager.isPlaying === true) {
        return;
    }
    VideoManager.isPlaying = true;

    // set video source
    var videoFile = this.app.assets.find(videoName).getFileUrl();
    VideoManager.video.src = videoFile;

    VideoManager.video.style.display = 'block';
    document.body.querySelector('#application-canvas').style.display = "none";

    video.addEventListener('ended', function (e) {
        document.body.querySelector('#application-canvas').style.display = "block";
        // alert("video is geeindigd");
        VideoManager.video.style.display = 'none';
        this.app.fire("video:ended");
        VideoManager.isPlaying = false;
    }.bind(this));
};

1 Like