YouTube iFrame API Video Intermittently Playing on Mobile Devices - javascript

I've spent way too many hours on this with very little success. We have a client with a site that has a YouTube video popup when the page loads. It autoplays on desktop and shows the Youtube play button on mobile since autoplay is not supported on mobile. I am using the iFrame JS API to instantiate the video player (code examples below). This setup is working perfectly on desktop, but on mobile devices (Android or iOS), between myself and my coworkers, clicking the Youtube play button only plays the video about 80% of the time. The rest of the time the video loading spinner just spins and nothing happens. If I close a reload the player using the site controls it will play, but not initially.
I know that this will not be an issue for the majority of users viewing the site but I know our client is going to go ape crazy if, out of 100 reloads on their iPhones, even 10 of them result in this behavior.
I am instantiating the player as follows:
1) I am including a "hard copy" of the Youtube iframe api JS on the site first.
2) I have an object controlling the display of the youtube "modal" window that contains the player target -- when the function is called it does the following:
// Write a div element to the container
_instance.videoContainer.innerHTML = '<div id="youtube-player"></div>';
// Grab a reference to it
_instance.el = document.getElementById('youtube-player');
// Call the YT player API
_instance.player = new YT.Player('youtube-player', {
playerVars: { // trying a bunch of different params with no success
playsinline : 1 ,
origin : window.location.origin ,
autoplay : 1 ,
wmode: "opaque" ,
iv_load_policy : 3
},
videoId: videoId , // This is passed to the function
events : {
onReady : function(){
console.log('resolved player');
// another function that just changes the container visibility
_instance.play();
},
onStateChange : function( event ){
if( event.data == YT.PlayerState.ENDED ){
_instance.close();
}
}
}
});
At this point, the video is either playing automatically in desktop 100% of the time, or it has shown the Youtube player with the video thumbnail and big red play button on mobile. This is where the trouble starts -- most of the time it plays fine, the rest of the time it just spins and spins and never plays. What's interesting is, on Android, if I blur the window and reopen it, I can click the play button again and it will play.
It shouldn't matter at this point, but I am destroying the div and the player reference when the modal is closed.
I've tried pretty much everything I can think of...
The Youtube video is somehow being throttled and only showing so many times through the iframe to the origin? I added the 'origin' property to the params thinking this might be the case, but it didn't seem to make much of a difference. I seem to have a higher success rate than my coworkers loading the video.
Is there some reason to asynchronously load the youtube iframe script as in the examples, rather than loading a copy from my site?
That's all I can think of at this point... is there something I'm missing? Thanks.

There is a related thread which stated that the autoplay function is not allowed for most mobile devices.
From this documentation:
Due to this restriction, functions and parameters such as autoplay, playVideo(), loadVideoById() won't work in all mobile environments.
A simple workaround is to have a custom looks of the "play" button:
Have an overlay element with pointer-events: none;. pointer-events works on all modern mobile browsers or simply have the video container over the button with opacity: 0.
Hope this helps!

I ran into the almost the same exact problem. For us it was specific to cellular connections.
If the embed is larger than ~360px wide, the player attempts to serve up quality at "large" or higher to which AT&T and Verizon are throttling.
The result we see: player enters buffering state and cannot achieve playback.
Tests over T-Mobile work OK without issue.
Players embedded at or below 360px wide play OK on all networks at quality of "medium" or lower.

Related

player in background tab doesn't play next playlist track (autoplay policy ?)

I use the react-player module in a React music app i'm working on.
My app has a music playlist. When a track has finished playing, it goes to the next one automatically.
Problem is, when the browser tab do not have the focus (background tab), it doesn't play the next track since it seems to be blocked by the browser's autoplay policy (have tested with Chrome & Firefox).
See documentation here: https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide
I was suggested to turn on autoplay for particular websites (for Chrome, go to chrome://settings/content/sound); but it doesn't work either. Anyway, this is not a solution for my visitors.
I've been struggling with this problems for days; any ideas on how achieve this ?
I have multiple providers (Youtube, Soundcloud, ...) so player does not always play things from the same service.
I'm able to detect the visibility of the tab using the Page Visibility API.
In my console, I can see that the onready event of react-player is fired; but the player is "stuck" until I go back to the tab again - then it plays.
Here's a pastebin of the current code of my player. The function responsible for getting the source to play is
const resolveTracks = async(track,source,backwards) => {
...
}
Thanks!

Are there known issues (in Feb 2018) with HTML5 Audio / Video in Chrome Mobile? [duplicate]

With the release of OSX High-Sierra*, one of the new features in Safari is that videos on websites will not auto play anymore and scripts can't start it either, just like on iOS. As a user, I like the feature, but as a developer it puts a problem before me: I have an in-browser HTML5 game that contains video. The videos do not get automatically played anymore unless the user changes their settings. This messes up the game flow.
My question is, can I somehow use the players' interaction with the game as a trigger for the video to start playing automatically, even if said activity is not directly linked to the video element?
I cannot use jQuery or other frameworks, because of a restraint that my employer has put on our development. The one exception is pixi.js which - among all other animations - we are also using to play our videos inside a pixi container.
*The same restriction also applies on Mobile Chrome.
Yes, you can bind on event that are not directly ones triggered on the video element:
btn.onclick = e => vid.play();
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>
So you can replace this button with any other splash screen requesting an user click, and you'll be granted access to play the video.
But to keep this ability, you must call at least once the video's play method inside the event handler itself.
Not working:
btn.onclick = e => {
// won't work, we're not in the event handler anymore
setTimeout(()=> vid.play().catch(console.error), 5000);
}
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>
Proper fix:
btn.onclick = e => {
vid.play().then(()=>vid.pause()); // grants full access to the video
setTimeout(()=> vid.play().catch(console.error), 5000);
}
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>
Ps: here is the list of trusted events as defined by the specs, I'm not sure if Safari limits itself to these, nor if it includes all of these.
Important note regarding Chrome and preparing multiple MediaElements
Chrome has a long-standing bug caused by the maximum simultaneous requests per host which does affect MediaElement playing in the page, limiting their number to 6.
This means that you can not use the method above to prepare more than 6 different MediaElements in your page.
At least two workarounds exist though:
It seems that once a MediaElement has been marked as user-approved, it will keep this state, even though you change its src. So you could prepare a maximum of MediaElements and then change their src when needed.
The Web Audio API, while also concerned by this user-gesture requirement can play any number of audio sources once allowed. So, thanks to the decodeAudioData() method, one could load all their audio resources as AudioBuffers, and even audio resources from videos medias, which images stream could just be displayed in a muted <video> element in parallel of the AudioBuffer.
In my case i was combining transparent video (with audio) with GSAP animation. The solution from Kaiido works perfectly!
First, on user interaction, start and pause the video:
videoPlayer.play().then(() => videoPlayer.pause());
After that you can play it whenever you want. Like this:
const tl = gsap.timeline();
tl.from('.element', {scale: 0, duration: 5);
tl.add(() => videoPlayer.play());
Video will play after the scale animation :).
Tested in Chrome, Safari on iPhone

How to interact with Netflix Cadmium video player on the client?

I have a Netflix account and I have peeked under the hood at its video player running inside Google Chrome. Netflix calls its video player "Cadmium" and the javascript exposes all the functions and event handlers you might expect, such as play, stop, pause, mute, etc. I'm building a little Chrome extension that would enable me to call these Cadmium player function, but the hard part for me is figuring out how to create an instance of the player so I can start calling. The javascript is large, complex, and somewhat obscure. Once I can create an instance of that player, I'm thinking that making calls into the functions will be easy.
Here is a relevant chunk of js:
muteOn: function() {
this.savedVolume = this.getVolume(),
this.updateVolumeDisplay(0),
this.scrubber.updatePercent(0),
this.muted = !0,
this.videoPlayer.setMuted(this.muted)
}
In Chrome dev tools I can set a breakpoint inside that block, and execution hits the breakpoint when I click the Mute button on the netflix video player. The Netflix js is (unsurprisingly) heavily obfuscated via method renaming. I tried stepping through the code in the debugger and ended down a hundred rabbit holes, never able to find my way to the top of the stack, so that I could make that same call (at top of stack) to simulate the user clicking the mute button. I also tried the approach of programmatically clicking the mute button on the UI player, which would meet my needs equally well, but they have serious defensive mechanisms in there, spinning me like a top.
Since there are over 100K lines of javascript, and I'm uncertain which chunks exactly would be relevant for this post, I would like to suggest that you load Netflix in Chrome, open dev tools, play a movie, and inspect the pause or mute button. Interacting with those video player controls takes you into the maze of javascript which I'm trying to see how I can tap into to control aspects of the player programmatically (just from dev tools is fine for now). Another important thing I need to figure out is how to query the video player to determine the current elapsed time of the playing video.
Any ideas how I can crack this nut? (Thanks in advance!)
Using Chrome, I get playback using HTML 5 video.
Once you get a hold on the <video> tag element, you can use the HTML 5 video API:
Get the <video> element
var video = document.evaluate('//*[#id="70143639"]/video',document).iterateNext()
70143639 is the id of the video, as in https://www.netflix.com/watch/70143639
Remaining time (HH:mm)
document.evaluate('//*[#id="netflix-player"]/div[4]/section[1]/label', document).iterateNext().innerHTML
Elapsed time (seconds)
video.currentTime
Elapsed time updates
video.addEventListener("timeupdate",
function(e) {
console.debug("Seconds elapsed: ", e.timeStamp/1000/60);
}
);
Note that I don't get the same results as with video.currentTime, you may need to use an offset based on the difference. Also it may be something explained in the spec: https://www.w3.org/TR/html5/embedded-content-0.html
Play
video.play();
Pause
video.pause();
Go back and forth in time
Courtesy of rebelliard:
netflix.cadmium.UiEvents.events.resize[1].scope.events.drage‌​nd[1].handler(null, {value: 600, pointerEventData: {playing: false}}); where 600 is the number of seconds to seek.
Note that I ran into "Whoops, something went wrong..." using this:
video.currentTime += 60;
Even with pause and play calls. This is what this demo page does, you nay need to read the full spec on seeking.
Mute and get muted status
video.muted = true
Like video.currentTime, this is a writeable property.

Html5 video loading improperly

I've got a project I'm working on implementing auto playing html5 video in a pop up window on hover. Everything is working close to fine however, in Chrome, when you hover over a video there is a small black box in the popup for a split second.
Now, I'm sure this has something to do with the time it takes to pull the video and load it. I'm just not sure on how to combat it efficiently. Maybe delay the playing somehow or make sure it's loaded before onMouseover()?
The project can be found here
You can just show a loading animation before the media is loaded. To know how, read this Tell whether video is loaded or not in Javascript
To load all the videos before will be troublesome for the user if there are a lot of videos.
Checking whether video is ready
if ( yourVideoElement.readyState === 4 ) {
// it's loaded
}

Exiting HTML5 Fullscreen Video Breaks Poster Images on Android

I have a scenario where I have search results that contain video content. Each video item in the results has a thumbnail sized video player, so up to 10 html 5 video players can exist per result set. When the user clicks the thumbnail, the video goes fullscreen and automatically plays the video. When the user exits fullscreen, the video pauses.
This all works great on iOS devices, but on android I have significantly more even handling to worry about. Here's the logic as I have it now:
goFullScreen: function (ev) {
var el = ev.target,
isVideoFullscreen = el.webkitDisplayingFullscreen;
el.webkitEnterFullScreen();
// the approach below is the only way I could get reliable fullscreen detection on android
$(window).bind("resize", function (e) {
if (isVideoFullscreen != el.webkitDisplayingFullscreen) {
isVideoFullscreen = el.webkitDisplayingFullscreen;
if (isVideoFullscreen) {
el.load();
el.play();
} else {
el.pause();
}
}
});
}
Even though iOS does not need all of this even handling, it still works fine. The problem with android is that when I exit fullscreen, the video pauses, but the poster image is replaced for the video I just paused with a still from the video (to be expected), but all subsequent videos in the result set have their poster image replaced with an ugly video icon. As a result, the thumbnails just look like broken videos. But if you tap them they still go fullscreen and play just fine.
I'm testing on a Galaxy Nexus and a Galaxy SII. I can say that the el.pause() is not responsible, if removed the video will continue playing in the thumbnail and all video tags below it will still have the broken poster icon.
This works as expected on desktop webkit browser and on iOS devices. Only experiencing this issue on android 4+ devices. Also remember that the EnterFullscreen request has to happen in the scope it's in. Calling out of this scope will prevent it from working due to security restrictions on mobile devices. I've pretty much exhausted all ideas so I'm looking here to hopefully get a few more.
Any suggestions would be greatly appreciated.
I've given up on trying to solve all of the bugs and quirks in Android. Instead, I'm just linking directly to the mpeg4 videos from the thumbnails. So no more video tags, no more event handling.
The only side effect is that some versions of android display a dialog on how you want to play the video, which is not ideal but better than any alternative I could find. Fortunately the iOS experience is consistent no matter what approach I take.

Categories