I am scratching my head over inconsistencies on youtube embeds playback quality on mobile devices.
I am using the iframe api and I have created a demo to showcase the code:
https://ancientoffbeatrotation--five-nine.repl.co/
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>repl.it</title>
<link href="index.css" rel="stylesheet" type="text/css" />
</head>
<body>
<script src="index.js"></script>
<iframe id="ytplayer" type="text/html" style="position:absolute; top:0px; left:0px; bottom:0px; right:10px; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;" src="https://www.youtube-nocookie.com/embed/_r3LynEZuko?playlist=_r3LynEZuko&rel=0&autoplay=1&playsinline=1&controls=0&showinfo=0&enablejsapi=1" frameborder="0" allowfullscreen></iframe>
</body>
</html>
JS
var tag = document.createElement('script');
tag.src = "//www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('ytplayer', {
events: {
'onReady': onPlayerReady,
'onPlaybackQualityChange': onPlayerPlaybackQualityChange,
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerReady() {
player.playVideo();
player.mute();
}
function onPlayerPlaybackQualityChange(event) {
var available_qualities = player.getAvailableQualityLevels();
alert("Available qualities are" + available_qualities);
var displayed_quality= player.getPlaybackQuality();
alert("Used quality is" + displayed_quality);
}
function onPlayerStateChange(event) {
if(event.data === YT.PlayerState.PLAYING){
event.target.setSize( width=7000, height=10000 ); //huge dimensions
}
}
Note: I used repl.it instead of jsfiddle or codenpen because the two latter use iframes so it tampares/modifies the pixels available for the youtube iframe which can make our observations wrong. Instead, repl.it does not use any iframe and deploys a real html page so we can trust it more for this experiments.
In theory
Youtube says it will use the height of the video to set the resolution (even if it can change it based on other factors it can alone decide like speed of connection...):
The list below shows the playback quality levels that correspond to
different standard player sizes. We recommend that you set the height
of your video player to one of the values listed below and that you
size your player to use 16:9 aspect ratio.
As stated above, even if
you choose a standard player size, we also recommend that you set the
suggestedQuality parameter value to default to enable YouTube to
select the most appropriate playback quality.
small: Player height is
240px, and player dimensions are at least 320px by 240px for 4:3
aspect ratio. medium: Player height is 360px, and player dimensions
are 640px by 360px (for 16:9 aspect ratio) or 480px by 360px (for 4:3
aspect ratio).
large: Player height is 480px, and player dimensions
are 853px by 480px (for 16:9 aspect ratio) or 640px by 480px (for 4:3
aspect ratio).
hd720: Player height is 720px, and player dimensions
are 1280px by 720px (for 16:9 aspect ratio) or 960px by 720px (for 4:3
aspect ratio).
hd1080: Player height is 1080px, and player dimensions
are 1920px by 1080px (for 16:9 aspect ratio) or 1440px by 1080px (for
4:3 aspect ratio).
highres: Player height is greater than 1080px,
which means that the player's aspect ratio is greater than 1920px by
1080px. default: YouTube selects the appropriate playback quality.
This setting effectively reverts the quality level to the default
state and nullifies any previous efforts to set playback quality using
the cueVideoById, loadVideoById or setPlaybackQuality functions.
I insist on the fact I am not using in the demo code the method setPlaybackquality which has not been working at all for the past few months, even if it is still undocumented ( see here, or here), so this is not about I think the issue of the method setPlaybackQuality.
In practice: The gist of the bug
I am just setting the size with setSize() and youtube is accordingly changing the video resolution. And I observe that changing it does on a desktop perfectly works according to this table given by google. but on mobile it's like it works...until a certain point, it's like there is a "ceiling", a resolution which is the maximum allowed by Youtube, based on unknown and very odd (see my experiments further below) criteria to me, which is capping the resolution that can be set. On my iphone 7 it's always capped at "HD720". On my Huawei Android P SMart it's always capped at "Large". So I don't get it: I mean on a super fast wifi at home even I can't get for huge dimensions of the demo video (height >5000px) hd 1080 and I am always maxed at hd720.
Here is a demo to share my findings so far: https://ancientoffbeatrotation--five-nine.repl.co/
Note that all tests are performed on a very recent chrome (>=64); safari on ios behaves the same way in my experiments.
on a chrome large desktop display window of 1800 x 945, I get a youtube video with a resolution of hd 1080 (highest available cool): perfect, as expected
chrome dev tools > emulate iphone7 so it takes dimensions of 375px x 667px => 1080hd is used, perfect, as expected
then on a real iphone 7 or browserstack (service to use real devices on the cloud): here the resolution used is hd720 despite the huge vidoe dimensions that should ensure it loads in hd1080. Right here is the issue.
Context: very good wifi connection, full batteries (no low battery mode that could add constraint on rich media download)
Same issue with my android phone where it does not even make it to hd720 and is set to "large".
Another "fascinating" observation is : I decided to add in the code an alert to know which so that which quality is available, the mystery deepens
function onPlayerPlaybackQualityChange(event) {
var available_qualities = player.getAvailableQualityLevels();
alert("Available qualities are" + available_qualities);
}
And the mystery clarifies but re-depeens again :( as shown below:
on desktop: I read for this alert "available qualities are: hd1080, hd720,large, medium, tiny, auto"
=> so in the observations above, I got indeed the highest possible quality, which is fine given the video dimensions
on the real iphone 7: I read for this alert "available qualities are: hd720, medium, small, auto"
So I thought, haha that's why I am getting hd720 maybe as it's the maximum availmable. But why? my connection speed i super strong and my phone pretty modern (iphone 7).
but the mystery deepens because with the above observation on iphone 7, I thought "ok at least I understand a little bit more now, I got the highest resolution available, it's just that hd720 was the maximum available for some reason", but the mystery deepens when I test on my huawei P Smart : here I read on the alert "available qualities are: hd1080, hd720, large, mediom, small, tiny, auto" so I would expect to get the highest hd1080 resolution...but no I am getting ..."large" (not even hd720!). So my theory goes down. Plus why on earth would iphone 7 get in available qualities hd720 as highest resolution and huawei P Smart hd1080 (note: when I checked the screen resolution of the phone it said: 2160 x 1080)
I thought maybe like some people mentioned in the press, my internet operator (free mobile in France) is doing the same throttling a verizon or t-mobile in the US for youtube, but I tried with various proxies in the world and observations are always the same. so the issue is likely not related to this.
I'm a super lost here, anybody has an idea what's going on ?
Related
I have a dynamically generated iFrame on my page that loads a website using a variable object.
All that is well understood. My challenge now is that in some cases, say if I am viewing on mobile, the frame width exceeds my mobile device width.
// STATIC VALUE
let screenSize = {
"height": window.innerHeight,
"width" window.innerWidth:
}
// DYNAMICALLY GENERATED VARIABLE
let frameValue = {
"url": "https://example.com",
"height": 913,
"width": 1600
}
//Using this variable, the iframe property is set as follows using javascript
$('#dynamicFrame').attr('src', frameValue.url);
$('#dynamicFrame').width(frameValue.width);
$('#dynamicFrame').height(frameValue.height);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- HTML DYNAMIC iFRAME -->
<iframe src="" id="dynamicFrame" frameBorder="0" width="100%" height="100%" scrolling="auto"> </iframe>
Need:
I would like an algorithm (or perhaps some code) to perhaps scale or zoom the iframe whilst keeping its aspect ratio.
Meaning I want the content to of frameValue.url (example.com) to load in the iframe as it would while considering frameValue.width & frameValue.height.
Notes:
I don't mind having the iframe look smaller or have dark bands around the edge just like when you watch videos on a mobile device or use zoom or Microsoft teams on a mobile device whilst the person sharing the screen is on a desktop device.
Please feel free to comment if you need further explanation. Thank you.
Is there any reason you aren't using a CSS solution? It'd be a much cleaner solution than setting the height/width attributes.
#dynamicFrame {
/* Swap for your desired aspect ratio */
aspect-ratio 16/9;
width: 100%;
height: auto;
}
Depending on the screen size and orientation, one of the following formulas may be useful.
NB:The orientation can be determined using the dimensions provided. i.e landcape = width > height
let scale = 0;
// If the window is potrait and the frame is landscape orientation
scale = screenSize.height * (1 / frameValue.height);
// If the window is landscape and the frame is potrait, one of the following applies.
scale = dimensions.height/dimensions.width).toFixed(2);
// OR
scale = dimensions.width/dimensions.height).toFixed(2);
// Set the iFrame Dynamic Scale Value
iframe.style.transform = `scale(${scale})`;
When in google chrome's device mode, what does window.innerWidth return? Is it the viewport of the device (plus any scroll bars)?
I'm getting different values for the device's width x height (the dimensions on top of the page - the device's viewport?) and window.innerWidth x window.innerHeight (browser's viewport?). Is this supposed to happen?
Here's a picture of what I'm getting, and the code I used.
<!doctype html>
<html>
<head></head>
<body>
<script>
var image;
window.onload = function() {
image = document.getElementById("img");
checkWindowSize();
window.addEventListener('resize', function(event){
checkWindowSize();
});
}
function checkWindowSize() {
var width = window.innerWidth,
height = window.innerHeight;
console.log("window.innerHeight: ", window.innerHeight, " window.innerWidth: ", window.innerWidth);
}
</script>
<img id="img" class="vid-img-filter" src="http://i.imgur.com/jkhFJMn.jpg" alt="">
</body>
</html>
window.innerWidth and innerHeight return the dimensions of the visual viewport. In desktop browsers, this is generally the browser's window dimensions. On mobile the situation is a bit more complicated because of pinch zoom.
When you load a page without a <meta name="viewport"> tag, a default layout width is used (e.g. Chrome uses 980px). When the browser loads the page it does so maximally zoomed out. It looks like your device size above has a width of 425px so the browser zooms out when the page is loaded to see the whole 980px. If you have content that's wider than this (e.g. your image) it'll zoom out even further. Seeing as how your window.innerWidth is 1248, that implies a scale factor of about 30%.
tl;dr: innerWidth/innerHeight reflect viewport with the pinch-zoom factor applied and the page is loaded fully zoomed out.
EDIT: This has since changed in Chrome. window.innerWidth now returns the layout viewport width. To get the visual viewport width, use window.visualViewport.width. See this article for more details.
I'm not sure if this is a recent update (since the last responses), but I was able to find the viewport height/width by using:
window.screen.width
and
window.screen.height
This was particularly useful when I was trying to test whether the screen was phone-sized or not.
We're currently having success with something like:
const widths = [window.innerWidth];
if (window.screen?.width) {
widths.push(window.screen?.width);
}
const width = Math.min(...widths);
The conditional check is there because I'm not sure how widespread the screen width API is. You may need to adjust this not to use certain newer JS features depending on what devices you are targeting/your build process.
This could potentially go a bit weird if you have a window that is wider than the screen, but for us that isn't a problem.
This gives us a width that matches the one at the top of the Responsive screen tool, even when contents overflow horizontally. This is important for us because we needed the UI to change in order to prevent that overflow, but the overflow was interfering with the width number we used to trigger the adjustment.
I'm not sure if this is important, but we are also using:
<meta name="viewport" content="width=device-width, initial-scale=1" />
I was reading many questions on SO regarding best ways to detect screen size in pixels which is ultimately dependant on resolution to find the actual screen size.
I know some mobile devices have small screen (say 4inch) yet the resolution is high. While some devices r having large screen (say 7inch tab) yet the resolution is low.
So when you want to display your page to the user, you really need the screen size in inches or centimetres to give the best view comforting the eyes.
So I ask:
Is there away to find out screen size of the device in inches not pixels ?
I know it can be, provided that we know the resolution! !Bec we can calculate the size from screen width and height
Appreciating any idea!
once you have got the screen resolution in pixels, you can work out the dpi with a hidden div:
<div id="dpi" style="height: 1in; width: 1in; left: 100%; position: fixed; top: 100%;"></div>
js
var dpi_x = document.getElementById('dpi').offsetWidth;
var dpi_y = document.getElementById('dpi').offsetHeight;
then work out the resolution in inches:
var width = screen.width / dpi_x;
var height = screen.height / dpi_y;
Example
Now, as far as I can tell there is no sure-fire way to accurately get the screen width of a device. Everything (including inches, 1in == 96px) is based in one way or another on screen resolution.
Now, I know this may not be the fix-all for everyone, but hopefully it helps a few people out there. It helped me, since I wanted my page to behave differently for mobile users, and my initial plan of attack was going to be based on screen size.
What I ended up doing was replacing the line
if (screen.height <= 768)
with
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent))
As a disclaimer, I should also mention that this was from an answer that is a few years old now, so some devices may not be covered.
Source: https://stackoverflow.com/a/26577897/8082614
I found this html5 video player which supports youtube source. But it didn't have a fullscreen button, so I started to implement the full screen function.
My problem now is that on chrome or safari the video doesn't take 100% width (not really fullsize, i have a black gap on all sides).
I used this script:
$('.video-fullscreen-btn', player).bind('click', function(e){
if (player.requestFullscreen) {
player.requestFullscreen();
} else if (player.msRequestFullscreen) {
player.msRequestFullscreen();
} else if (player.mozRequestFullScreen) {
player.mozRequestFullScreen();
} else if (player.webkitRequestFullscreen) {
player.webkitRequestFullscreen();
}
});
I also tried it with the css webkit
:-webkit-full-screen video {width: 100%; height: 100%;}
still not working. does someone have an idea why this is not working on chrome/safari?
The whole example can be found here: http://jsfiddle.net/Z5PTv/
I think it is just due to the aspect ratio of the actual video content. If you look at the original youtube video it also has black bars either side of the video.
That is why the preload image looks pixelated because the image, which has the same aspect ratio as the video I assume, has been forced to take up the space.
If the video was to be the full width of the screen the top and bottom of the video would be clipped off. I assume you would rather have black bars on the side rather than clipping.
If the problem is aspect ratio you can control is with CSS object-fit: fill;
But this is only supported in Opera right now.
If the video element is using 100% of the screen this doesn't mean that the video-image it self will be stretched to fill the whole screen. You can opserve this by using the controls attribute on the video element and notice how the controls can be wider then the video-image.
Demo : http://netkoder.dk/netkoder/eksempler/eksempel0017.html
Is there a way to force a high quality THUMBNAIL for YouTube?
My videos are of very high quality and once they start streaming they run fine in 720p, however the thumbnail for the video is of variable quality - sometimes it's high, other times it's really blurry.
Is there a way of forcing a high quality thumbnail? I've found this in the API docs - http://code.google.com/apis/youtube/2.0/reference.html#youtube%5Fdata%5Fapi%5Ftag%5Fmedia:thumbnail but it doesn't detail how to use the media tag.
There is a "maxres" version as well, which is a "full hd" picture in case that the video resolution is high enough.
http://i.ytimg.com/vi/VIDEO_ID/maxresdefault.jpg
However, if the video resolution isn't high enough, this image doesn't seem to be created. So you might want to have a function that shows a lower quality version in case the "maxres" version doesn't exist.
Check out How do I get a YouTube video thumbnail from the YouTube API? for more info.
Thanks, Johan for answer.
http://i.ytimg.com/vi/VIDEO_ID/maxresdefault.jpg
If you also want set playback to HQ use Javascript API
https://developers.google.com/youtube/js_api_reference?hl=fi-FI#Playback_quality
var qualityLevels = self.players[iframeID].getAvailableQualityLevels();
/* Set highest quality */
self.players[iframeID].setPlaybackQuality(qualityLevels[0]);
In order for this to work we first need to call:
self.players[iframeID].playVideo();
Then it will triggrer
self.players[iframeID] = new YT.Player(iframeID, {
events: {
'onReady': function(event) {
var iframe = $(event.target.getIframe());
self.players[iframe.attr('id')].loaded = true;
},
'onStateChange': function(event) {
var iframe = $(event.target.getIframe());
var iframeID = iframe.attr('id');
if(event.data == 1) /* playing */
{
var qualityLevels = self.players[iframeID].getAvailableQualityLevels();
if(self.players[iframeID].getPlaybackQuality() != qualityLevels[0])
{
/* Set highest quality */
self.players[iframeID].setPlaybackQuality(qualityLevels[0]);
}
}
}
}
});
Example:
http://img.youtube.com/vi/xjFouf6j81g/hqdefault.jpg
where xjFouf6j81g, videoid
As far as I know, YouTube only offers 4 thumbnails:
http://i.ytimg.com/vi/VIDEO_ID/default.jpg
http://i.ytimg.com/vi/VIDEO_ID/1.jpg
http://i.ytimg.com/vi/VIDEO_ID/2.jpg
http://i.ytimg.com/vi/VIDEO_ID/3.jpg
The thumbnails are generated once after the upload, so you can't alter the size of them in any way.
Edit:
YouTube JS API lets you force the quality of the video, but I don't know if it will affect the video preview. See the docs for more info.
Issue has been reported here:
https://code.google.com/p/gdata-issues/issues/detail?can=2&start=0&num=100&q=&colspec=API%20ID%20Type%20Status%20Priority%20Stars%20Summary&groupby=&sort=&id=4590
Not a fix or a workaround, but if you want to avoid getting the low-res thumbnail your player should be larger than 430px wide.
So a suggested minimum size for perfect 16:9 aspect would be 432 x 243 pixels and this should get a nice-looking thumbnail.