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;
}
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.
When I call getUserMedia() I can get my current video resolution by using onloadedmetadata on the srcObject.
However, when the video is changing its resolution for some reason; in my example the user rotates the camera to portrait, then my the old video resolution becomes obsolete and I would like to get the new one.
This is how my initial fetch of resolution looks like:
this._localVideo.onloadedmetadata = (e) => {
e.srcElement.videoWidth;
e.srcElement.videoHeight;
};
but how do I know if these values are changing?
EDIT: Turns out re-setting srcObject on the video element triggers onloadedmetadata again with updated values.
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.
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.
I have a bit of Javascript that runs on an Android tablet, running Opera Mobile 12. The tablet is attached to the wall in an office, so it's used by everyone who works in this office, as a timekeeping system (ie. they use it to clock in/clock out of work). This javascript takes a photo of the user when a certain event is raised, then converts this photo to a data URL so it can be sent to a server. This all runs in the background, though - the video and canvas elements are both set to display:none;.
Here's the code that handles the webcam interaction:
var Webcam = function() {
var video = document.getElementById('video');
var stream_webcam = function(stream) {
video.addEventListener('loadedmetadata', function(){
video.play();
}, false);
video.src = window.URL.createObjectURL(stream);
}
// start recording
if (navigator.getUserMedia)
navigator.getUserMedia({video: true}, stream_webcam);
else
_error("No navigator.getUserMedia support detected!");
var w = {};
w.snap = function(callback) {
var canvas = document.getElementById('canvas');
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
// delay is incurred in writing to canvas on opera mobile
setTimeout(function() {
callback(canvas.toDataURL());
}, 1);
};
return w;
}
w.snap(callback) gets called when the user presses a certain button. The problem is that there is a significant lag between the call to drawImage, and an image actually being drawn to the canvas. Here's the problem that occurs:
User 1 users the tablet, presses the button, has their photo "snapped" and sent to the server. The server receives a blank, transparent, image. (It's not the same issue as html5 canvas toDataURL returns blank image - the data being sent to the server is substantially less for the first image, because it's blank)
A few minutes later, user 2 uses the tablet and presses the button, and has their photo "snapped". The server receives the photo of user 1.
Later, user 3 does the same thing, and the server gets a photo of user 2.
So I'm guessing the call to toDataURL() is returning old data because drawImage() doesn't . (I read somewhere that it's async, but I can't confirm and can't find any way of attaching an event to when it finishes drawing.)
I've tried:
changing the timeout in snap() to a variety of values. The highest I tried was 2000, but the same issue occurred. (If anyone suggests higher numbers, I'm happy to try them, but I don't want to go much higher because if I go too high, there's potential for user 3 to have their photo taken before I've even processed user 2's photo, which means I might lose it entirely!)
having the canvas draw something when the page first loads, but that didn't fix it - when user 1 was photographed, the server received whatever photo was taken when the page loaded, instead of the photo of user 1.
"getting" the canvas element again, using getElementById, before calling toDataURL.
A few more notes:
This works fine on my computer (macbook air) in Chrome and Opera.
I can't reliably replicate it using a debugger (linking to the tablet using Opera Dragonfly).
AFAIK Opera Mobile is the only Android browser that supports getUserMedia and can thus take photos.
If I remove the display:none; from the video and canvas, it works correctly (on the tablet). The issue only occurs when the video and canvas have display:none; set.
Because the page is a single page app with no scrolling or zooming required, a possible workaround is to move the video and canvas below the page fold, then disable scrolling with javascript (ie. scroll to the top whenever the user scrolls).
But I would rather a proper fix than a workaround, if possible!
I don't have a very good solution but I would suggest not handling an invisible canvas through the dom by setting it invisible.
Instead of
var canvas = document.getElementById('canvas');
Try
var canvas = document.createElement('canvas');
canvas.width = video.width;
canvas.height = video.height;
canvas.getContext('2d').drawImage(video, 0, 0)
This way it's separate from the DOM.
Also Why not use
canvas.toDataURL("image/jpg")?
EDIT: Also, if you're designing it, have you tried the other browsers out there? Whats restricting you to Opera over the other browsers available or using phonegap?
EDIT2: Thinking about it, Canvas also has two other options for getting that photo into place that you would want to look in two. Those two being:
var imgData = canvas.getContext('2d').getImageData(0,0,canvas.width,canvas.height);
-or-
canvas.getContext('2d').putImageData(imgData,0,0);
These two ignore any scaling you've done to the context but I've found that they are much more direct and often faster. They are a solid alternative to toDataURL and drawImage but the image data you put and get from these are encoded as an array in the form:
[r1, b1, g1, a1, r2, b2, g2, a2....]
you can find documentation for them here:
http://www.w3schools.com/tags/canvas_putimagedata.asp
http://www.w3schools.com/tags/canvas_getimagedata.asp
Some version of Android have problems with toDataUrl(); Maybe this is the solution?
http://code.google.com/p/todataurl-png-js/wiki/FirstSteps
The script: http://todataurl-png-js.googlecode.com/svn/trunk/todataurl.js
Paste this in your head:
<script src="todataurl.js"></script>