videojs / videojs-youtube

YouTube playback technology for Video.js
1.12k stars 548 forks source link

Youtube video not playing for iframe-embedded sites (chromium browsers Only - Firefox seems to work) #593

Open JohnBernardsson opened 3 years ago

JohnBernardsson commented 3 years ago

Hello,

Seems there is a problem when playing Youtube videos in a site which is embedded in another one through an iframe.

After doing some debugging, seems for some reason, the "onPlayerStateChange" is called with state === -1, and is never called again with a different state. I don't know if it's a communication problem with the youtube or what the heck...

Here is a very basic example: https://codepen.io/johnbernardsson/pen/abwdBmR

This won't automatically play in any browser. But, at least in Firefox, if you click on the "play" button, it will start the playblack. In Chromium browsers (tested Edge and Chrome), even playback is not possible. On the other hand, it seems that if the parent site's domain is the same than the iframe-embedded one with the videojs-youtube, this issue is not happening. So I am not sure if that has anything to do with the "origin" param setted in the youtube-iframe-player. I tried to remove it by params, but for some reason seems the youtube API is anyway adding it (so not sure if Youtube guys have some part of guilt here...). This "origin" thing hasn't been tested extensivenly, but mentioning just in case is useful.

At my company we are quite stucked on this. And is a real problem, as we are managing user views, how much the user viewed from a video and other statistics, relying on the video.js API. And of course we provide iframe-embedding of our services, where this problem is coming from.

I added some "dirty hack" to the plugin, which I attach below in case it helps someone, but is not a real fix for the problem. I would be sincerelly thankful if someone with better knowledge could take a look on this...

The infamous hack:


//...
onPlayerStateChange: function(e) {
    var state = e.data;

    if (state === this.lastState || this.errorNumber) {
        return;
    }

    this.lastState = state;

    switch (state) {
        case -1:
            this.trigger('loadstart');
            this.trigger('loadedmetadata');
            this.trigger('durationchange');
            this.trigger('ratechange');

            // == Hack start == //
            if (location.search.indexOf('source=embedded') > -1) {  //This is how we know in our company the site is being embedded in an iframe
                if (this.stateTimeout) {
                    clearTimeout(this.stateTimeout);
                    this.stateTimeout = null;
                }

                this.stateTimeout = setTimeout( () => {
                    if (this.lastState === -1) {
                        // Make Video.js UI think we are playing the video, then pause it. For a strange reason, this 
                        // brings up the red "native" Youtube Play button. If you click it, everything will work fine from that point
                        this.trigger('playing');
                        this.trigger('play');
                        this.onSeeked();
                        this.trigger('pause');
                    }
                    }, 1500
                )
            }
            // == Hack end == //

            break;

        case YT.PlayerState.ENDED:
            this.trigger('ended');
            break;

        case YT.PlayerState.PLAYING:
            // == Hack start == //
            if (this.stateTimeout) {
                clearTimeout(this.stateTimeout);
                this.stateTimeout = null;
            }
            // == Hack end == //
            this.trigger('timeupdate');
            this.trigger('durationchange');
            this.trigger('playing');
            this.trigger('play');

  // ...
cadavre commented 2 years ago

I have the same problem. The thing I've observed, is that if you click fast enough (up to 2-3s after loading) – the video will play in a matter of fact.

brettgoetz commented 2 years ago

Did you ever find a long term solution here? I came across this after noticing I could embed a video directly using YouTube, play it, then bounce back to the VideoJS player and it would play (which is what your solution is replicating). We also have the same issue with Kaltura player using YouTube content. By any chance are you on a managed Chrome browser (I can only replicate it on our institution's managed instance, but have one user that states theirs is not managed)?

brettgoetz commented 2 years ago

Interesting. Try setting:

player.muted(true);

For myself, the videos now play correctly. Found lower in the comments in this stackoverflow thread: https://stackoverflow.com/questions/54944500/problems-with-youtube-iframe-api-to-start-playing-video-on-chrome

brettgoetz commented 2 years ago

Last message, and I hope it's useful to those experiencing the above. On the site you're having issues with, make sure Sound is toggled to on (the dropdown under the lock/secure icon in the url bar). If it's not an option, choose site settings and set Sound to allow (not automatic). This appears to be intended Chrome behavior, but it definitely threw me through a loop to find.

mrassili commented 2 years ago

set muted option, it works!

stavert commented 1 year ago

For any others out there having this problem, we found that with our site, we needed to add the next level down domain into the allow="autoplay" specification like:

allow="autoplay 'self' https://subdomain.childsite.com"

Everything started working without having to mess with muted any longer.

gentlemanjohn commented 4 months ago

I found another less-than-ideal workaround.

Setting the muted option to true "works" in that the video will play in Chrome but is, of course, muted. To get around this UX annoyance, I added the following to my onPlayerReady():

this.on('play', function() { this.muted(false); });

This of course hurts usability a touch in that the player will always unmute when the play button is pushed i.e. in the unlikely event the user mutes, pauses, and then resumes play, the audio will unexpectedly unmute. Maybe there's a better event to change the muted option back to false, but 'play' is the only one I found that works for my use case.

Hope this helps.

gentlemanjohn commented 4 months ago

I found another less-than-ideal workaround.

Setting the muted option to true "works" in that the video will play in Chrome but is, of course, muted. To get around this UX annoyance, I added the following to my onPlayerReady():

this.on('play', function() { this.muted(false); });

This of course hurts usability a touch in that the player will always unmute when the play button is pushed i.e. in the unlikely event the user mutes, pauses, and then resumes play, the audio will unexpectedly unmute. Maybe there's a better event to change the muted option back to false, but 'play' is the only one I found that works for my use case.

Hope this helps.

By the way, this doesn't seem to address the "all time loading spinner" issue when the player loads. I set the loadingSpinner option to false to get rid of it.