The YouTube API docs define the minimum size of an embedded player t to be 200px by 200px (link).
To allow room for critical player functionality, players must be at least 200px by 200px.
My testing has lead me to the conclusion that this is true. If I try to play a video in a player which is smaller than the minimum size, I get an error message which says "Video player is too small." and the video will not play.
However, smaller players are possible. SwitchCam, for example, uses them on pages like this one.
I've tried reducing the player size by setting it's height and width attributes, by using it's style attribute and by wrapping it in a containing element which has it's height and width set. None of these options appear to work.
What else can I try to reduce the size of the player?
EDIT
It appears that some videos will play in really small players but others will not. If you're going to test a potential solution, please use this video ID: -rMTExNTx2s
It's appears there is a restriction on some video which don't allow embeding video on size inferior to 200*200 (px). This restriction is not applied for all video (maybe older than last update youtube API, i don't know).
After some tests, this restriction is applied when youtube player readystate changed to status: PlayerState.PLAYING (evt.data === 1)
So as a basic workaround, you could change size of iframe 'on the fly' after the satus has been updated, see demo&code below:
DEMO
var player,
myWidth = "114px",
myHeight = "65px";
function onYouTubePlayerAPIReady() {
player = new YT.Player('testVideo', {
height: myWidth,
width: myHeight,
videoId: '-rMTExNTx2s',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
},
playerVars: {
controls:0,
showinfo:0
}
});
}
function onPlayerStateChange(evt) {
if (evt.data == -1) {
evt.target.a.width = "200px";
evt.target.a.height = "200px";
}
else if (evt.data == 1) {
evt.target.a.className = "";
evt.target.a.width = myWidth;
evt.target.a.height = myHeight;
done = true;
}
}
As you can ssee in this DEMO, i set an hidden class with css .hidden{opacity:0}. This is used to hide player before the video is loaded. Using display:none; doesn't work, its surely an other API restriction.
Still in this DEMO, you have to wait until the video has started to play to see the player appears.
You have now to find the best workaround which could fit your needs, using e.g a thumbnail image and moving from negative offset player to wished location when readystate has changed, hope you got the idea.
Not the most elegant solution, but have you thought about actually scaling down a larger player with the CSS3 transform: scale() property? Beware it's not supported in IE < 9.
The main reason not to do this, though, is that you'll be reducing the size of the UI controls which in turn reduces usability.
Related
Hi all I am using Signalwire's video calling functionality to make a video calling app. I am facing one issue here, as most of the times, we use video calling through phones or small screen sizes the height of the video is very small there.
Is there any way to increase the height of that div on which the video stream is getting injected?
Here What it looks like-
In mobile my video screen is quite small I want to increase the height.
I tried something like-
$scope.roomObject = new SignalWire.Video.RoomSession({
token: token,
rootElement: document.getElementById('root'), // an html element to display the video
audio: true,
video: {
width: { min: 720},
height: { min: 1280}
}
}
});
This does change the inner video into portrait mode but the issue remains, I can't increase the height.
Note- increasing the height of div not working I can increase the width though.
Thanks
It sounds like you're trying to extend the video canvas vertically to fill the entire screen. While you can change the aspect ratio of your video stream itself (which is how you're swapping your stream to portrait), you can't change the aspect ratio of the whole canvas.
I am writing code that changes video resolution depending on the current screen size. On fullscreen button clicked I check screen size and if it is bigger than 1280px, a 1080p video is used instead of 720p.
I do that by changing src of the video element. Unfortunately, this causes a delay of a second or more, because the video with higher resolution needs to load first.
How can I create a seamless transition between the 2 resolutions? Sometimes youtube or facebook videos change resolution depending on your network conditions, and it is seamless in terms of delay.
This is my basic code. I use plyr library:
html
<video id="main-video" playsinline poster="/assets/img/video.png" class="element-video">
<source id="main-video-source" src="/assets/img/video.mp4" type="video/mp4" size="1080">
</video>
js
var player = new Plyr('#main-video',{controls:['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'settings', 'fullscreen']});
player.on('enterfullscreen', event => {
var videoPlayer = document.getElementById("main-video");
if(window.devicePixelRatio * window.innerWidth > 1280){
var currentTime = videoPlayer.currentTime;
videoPlayer.src = "video.mp4";
videoPlayer.currentTime = currentTime;
videoPlayer.play();
}else{
var currentTime = videoPlayer.currentTime;
videoPlayer.src = "video-720.mp4";
videoPlayer.currentTime = currentTime;
videoPlayer.play();
}
});
As Joel says, using Adaptive Bit Rate Streaming is the easiest approach here currently to get the affect you are looking for. See here for more info on ABR and an example of how to view it in action: https://stackoverflow.com/a/42365034/334402
Most video player clients will support ABR, and will give the type of smooth(ish...) transition you see on services like YouTube or Netflix when it steps through different resolutions. Having more different resolutions or 'steps' may make it smoother so it may be worth experimenting to find what is acceptable for your use case.
Also, as you already have at least two resolution versions of the video any extra server side overhead is not too great for your case.
I have a button that allows a user to preview their video that comes through their camera. The video stream is successfully displayed but I am struggling to find out how to alter the dimensions of the displayed video. This is what I have:
HTML:
<div id="local-media"></div>
JavaScript:
previewMedia = new Twilio.Conversations.LocalMedia();
Twilio.Conversations.getUserMedia().then(
function (mediaStream) {
previewMedia = new Twilio.Conversations.LocalMedia();
previewMedia.on('trackAdded', function (track) {
if(track.kind === "video"){
track.dimensions.height = 1200;
track.on('started', function (track) { // DOES NOT FIRE
console.log("Track started");
});
track.on('dimensionsChanged', function (videoTrack) { // DOES NOT FIRE
console.log("Track dimensions changed");
});
}
previewMedia.addStream(mediaStream);
previewMedia.attach('#local-media')
}),
function (error) {
console.error('Unable to access local media', error);
};
);
The trackAdded event fires but I don't get the started or dimensionsChanged events firing and setting the track.dimensions.height does not work.
I can shrink the video by using:
div#local-media {
width:270px;
height:202px;
}
div#local-media video {
max-width:100%;
max-height:100%;
}
but I cannot increase it beyond 640x375 pixels.
Based upon some interactions with our support team it seems you should first try setting the size of a <div> using CSS before attaching the video track. This technique is used in the quickstart application.
https://www.twilio.com/docs/api/video/guide/quickstart-js
Then, try passing in the optional localStreamConstraints when calling inviteToConversation
https://media.twiliocdn.com/sdk/js/conversations/releases/0.13.5/docs/Client.html#inviteToConversation
It looks like you can specify the dimensions for video:
https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
which is then used by getUserMedia (the WebRTC function)
Keep in mind that you can adjust the capture size locally.This is the size of the Video Track being captured from the camera.
However, depending on network conditions, the WebRTC engine in your browser (and the receivers browser) may decide that the video resolution being captured is too high to send across the network at the desired frame rate (you can also set frame rate constraints on the capturer if you'd like to trade off temporal vs spatial resolution). This means that the receiving side may receive a video feed that is smaller than what you intended to send. To overcome this, you can use CSS to style the <video> element to ensure that it stays at a certain size, which will result in video upscaling/downscaling where required on the receiving side.
We plan to update our documentation with more of these specifics in the future. But you can always find additional support from help#twilio.com.
you can adjust the screensize using following css. you can find this css file in Quickstart->public->index.css
Remote Media Video Size
div#remote-media video
{
width: 50%;
height: 15%;
background-color: #272726;
background-repeat: no-repeat;
}
I have a video (let's call it composite video) composed by multiple other videos concatenated using some pattern. For example, see the screenshot of the videos below, composed by two and four other videos, respectively:
However, I need to display it differently: One main, larger, video and N-1 video thumbnails, where N is the total number of videos. Here are this other display corresponding to the videos above:
To display the main I'm using a combination of HTML and CSS to position the video I want in the larger div. It runs smoothly, no matter the number of videos in the composite videos.
To display the thumbnails, I'm using <canvas> to draw the parts I want:
video.addEventListener('play', function() {
(function loop() {
drawThumbnails();
setTimeout(loop, 1000 / 30); // drawing at 30fps
})();
}, false);
function drawThumbnails() {
for (var i = thumbs.length - 1; i >= 0; i--) {
drawThumbnail(thumbs[i]);
};
}
function drawThumbnail(thumb) {
var thumbNumber = Number(thumb.id.match(/\d+/g));
var canvasContext = thumb.getContext('2d');
var thumbCoordinates = getVideoCoordinates(thumbNumber);
var srcX = thumbCoordinates.column * videoWidth;
var srcY = thumbCoordinates.row * videoHeight;
canvasContext.drawImage(
video, srcX, srcY, videoWidth, videoHeight, // Source
0, 0, thumb.width, thumb.height); // Destination
}
It was working well for 3 (sometimes 4) videos. However, as the number of videos in the composite video increases, the videos in the thumbnails start to freeze and run not in a smooth way. This is probably happening because there's too much image processing being done at the same time.
I think the proper way to do it is, somehow, using <video> and methods specific for videos, not for images. I've also tried to use the same src in the multiple <video> tags (one for each thumbnail) and add eventListeners to play/pause the videos in the thumbnails once the main video is played/paused. That's not very efficient, particularly because videos can get out of sync sometimes, when seeking/buffering.
Is there a way of using only one video in multiple <video> tags and use only one of them (in my case, the one that contains the main video) to control all the others? In case there's no way of doing that, is there an alternative approach for my problem?
Thanks a lot,
P.S. Having multiple, separated, videos is not an option in my situation. It would take a very long time to process the input video and divide it in multiple videos.
You can certainly reference the same video across multiple video elements. Cloning the original and appending them as thumbnail videos might alleviate some of the tedium.
Iterating over the thumbnails and .play()ing them should be fine so long as you set their currentTime with that of the main video prior to playing, to minimize drift. There may be some need to wait for canplay to fire on the main video and/or the thumbnails depending on the exact experience you're looking to deliver.
If each thumbnail is given a parent container you could possibly position the video element serving as your thumbnail such that only the portion of the video you care to see is visible, clipping the rest.
FWIW, CSS masking might be of interest to you as a performance optimization if it helps the compositing performance.
You will need to manually coordinate playing/pausing all of the video elements, but that should be easy enough to do with a facade object that handle the play pause of all the "linked" video elements.
I know I'm posting late, and you may have already found an answer. However, if anyone else comes across this question, here is my answer:
You can use multiple video elements with the same source. The way to do it is with css.
.wrapper {
height: /*height of one video*/;
width: /*width of one video*/;
overflow: hidden;
}
video {
position: relative;
top: /*height offset*/;
left: /*width offset*/;
}
And HTML
<div class="wrapper">
<video src="myvideo.mp4"></video>
</div>
So, if I was doing the top right video, and each one was 250px by 250px, I would set my wrapper height and width to 250px and my video top to 0px and my video left to 250px
What's the format of the main video? Is it an on demand mp4/webm file?
If you still want to go with your approach of grabbing frames and paint them but is facing performance issues, consider using web workers for the heavy jobs. Here you can find some examples of video/canvas manipulation with web workers.
I have a video element:
var video = window.content.document.createElement("video");
video.width = width;
video.height = height;
video.style.backgroundColor = "black";
window.content.document.body.appendChild(video);
And I'm retrieving it's source via getUserMedia() on Firefox:
window.navigator.getMedia = ( window.navigator.getUserMedia || window.navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia || window.navigator.msGetUserMedia);
window.navigator.getMedia( //constraints, sucessCallback, errorCallback
{video: true, audio: false},
function(stream) {
if (window.navigator.mozGetUserMedia)
video.mozSrcObject = stream;
else
{
var vendorURL = window.URL || window.webkitURL;
video.src = vendorURL.createObjectURL(stream);
}
video.play();
},
function(err) {
console.log("Error: " + err);
}
);
The problem is I need to know the "active area" of video, and it's returning me 0:
video.onloadedmetadata = function(){
console.log(this.width + "x" +this.height);
console.log(this.videoWidth + "x" +this.videoHeight);
}
So, how can I retrieve the REAL values?:
There are two issues here:
video.videoWidth and video.videoHeight properties weren't getting set as soon as the loadedmetadata event fired. This was a bug in FireFox, which is now fixed (thanks to #Martin Ekblom for pointing out the bug).
The video itself doesn't take up the whole area of the video element, which none of the answers seem to have addressed. Instead, it scales to fit inside the element.
I don't think there's a direct way to get the dimensions of the active area, but after struggling with this myself, I wrote up a solution to calculate it from the values we do know:
function videoDimensions(video) {
// Ratio of the video's intrisic dimensions
var videoRatio = video.videoWidth / video.videoHeight;
// The width and height of the video element
var width = video.offsetWidth, height = video.offsetHeight;
// The ratio of the element's width to its height
var elementRatio = width/height;
// If the video element is short and wide
if(elementRatio > videoRatio) width = height * videoRatio;
// It must be tall and thin, or exactly equal to the original ratio
else height = width / videoRatio;
return {
width: width,
height: height
};
}
Essentially, we take the aspect ratio of the video element, the aspect ratio of the video, and the dimensions of video element, and use those to determine the area the video is occupying.
This assumes the video's fitting method hasn't been modified via CSS (not sure if that's even possible at this time, but the spec allows for it). For more details on that and a few other things, see the blog post I wrote ("Finding the true dimensions of an HTML5 video’s active area"), inspired by this question and its lack of complete answers.
It's interesting to note that while the spec specifically mentions the possible edges around a video (letterboxing and pillarboxing), I wasn't able to find any other mentions of it, apart from your question.
You should add a loadeddata event listener to the video, and try to read the size then, which is when enough information about the stream has been decoded and the dimensions can be accurately determined.
Still, it sometimes takes a bit in some cases to get the dimensions ready, so you might want to try several times (with delay) until it works.
That might be why it is not working for you but it's working for Sam.
Here's how I check the video size, with several attempts if required, in my gumhelper library:
https://github.com/sole/gumhelper/blob/master/gumhelper.js#L38
Notice how we try several times and if it doesn't work we "give up" and default to 640x480.
"loadeddata" should only fire once. It's better to use "timeupdate" to repeatedly check if the video width and height have been set, in particular with getUserMedia where you don't really pause the video, but go straight into playback.
Actually this seems to be a bug in FF:
https://bugzilla.mozilla.org/show_bug.cgi?id=926753