Browser web cam stream has extremely low performance/frame rate - webrtc

I am trying to test WebRTC and want to display my own stream as well as the peer's stream. I currently have a simple shim to obtain the camera's stream and pipe that into a video element, however the frame rate is extremely low. The rare thing about this is that I can try examples from the WebRTC site and they work flawlessly.. The video is smooth and there are no problems. I go to the console and my code resembles theirs.. What could be happening? I tried to create both a fiddle and run that code within brackets but it still performs horribly.
video = document.getElementById('usr-cam');
navigator.mediaDevices.getUserMedia({video : {
width : {exact : 320},
height : {exact: 240}
}})
.then(function(stream){
if(navigator.mozGetUserMedia)
{
video.mozSrcObject = stream;
}
else
{
video.srcObject = stream;
}
})
.catch(function(e){
alert(e);
});
Pretty much everything I do. Take into account that I am using the new navigator.mediaDevices() API instead of navigator.getUserMedia() but I don't see how that would matter since 1.I am using a shim provided by the WebRTC group named adapter.js which they themselves use. 2. I don't think how you obtain hold of the video stream would affect performance.

Alright, I feel very stupid for this one... I was kind of deceived by the fact that the video element will update the displayed image without you having to do anything but pipe the output stream, which means the image will update but just at really long intervals, making it seem as if the video is lagging. What I forgot to do was actually play() the video or add autoplay as its property... it works well now.

Related

Do webm <video> elements support time scrubbing?

I am attempting to use an HTML5 video player to preview a local video before uploading it. The video is stored as a webm blob, which I convert into a data URL so that it can be sent as a JSON payload around a Chrome Extension.
The video plays great, but I can't seem to scrub the time at all. The time increments, but trying to use the controls just does nothing. The control circle is completely to the left of the control bar, and doesn't increment as the video progresses. I do not see the total time either.
Once the video completes, the control bar works as expected. The total time is present, and the video can be scrubbed. Playing it at this point causes the control circle to proceed from left->right as expected.
The weird part is that I can set currentTime on the video before it finishes and it works great.
Here's my construction. There's nothing fancy here:
const video = document.createElement('video')
video.controls = true
video.preload = 'auto'
video.currentTime = 30 // This works, somehow
video.id = 'video'
video.width = 800
const source = document.createElement('source')
source.type = 'video/webm'
source.src = resp.recordingDataUrl // This looks like data:video/webm;base64,GkXfo59Ch.....
video.appendChild(source)
document.getElementById('root').appendChild(video)
Am I missing something necessary to scrub the time? I have noticed this when browsing videos on the web (usually Reddit), which makes me wonder if there's a webm bug of some sort.

Safari not retrieving mp4 video from cache, and sometimes timeout when downloading the same resource

I'm running a VueJS application that displays a full screen story of videos. I don't create as many tag as number of media in my story : I'm just changing component video sources each time I play a new video.
But it looks like Safari (Desktop & mobile) still does not cache HTML video once loaded : when I'm playing again a previous media, Safari is downloading again the asset. Instead of getting from cache like Chrome does.
The same issue has already been reported here but sill no correct answer.
Safari even stops downloading the final bytes video (producing a sort of timeout) when we go back and forth quicky in the story, so the story looks stuck.
Here's an example link.
Does anyone know a good alternative that avoids re-downloading video data at each play on Safari ?
Partial solution
Found myself a workaround that works pretty well if video is small size - all video are less than 3Mb in my case.
The trick is to use js fetch API to download full video, then stream it into video tag.
const videoRequest = fetch("/path/to/video.mp4")
.then(response => response.blob());
videoRequest.then(blob => {
video.src = window.URL.createObjectURL(blob);
});
Contrary to video src attribute, fetch API will get video data from cache if the same video was already fetched before.
Here a codepen demo that can be tested in Safari desktop/mobile (when NOT in private mode).
Pro : Video are now pulled from cache in Safari !
Con : You can't start the video until full data has been downloaded. That's why this solution can be used only for small video (like < 5Mb), else your users may wait a while before being able to play the video.

WebRTC: View self-view while muting outgoing video in a call

Currently, the video mute functionality in webrtc is achieved by setting the enabled property of a video track to false
stream.getVideoTracks().forEach(function (track) {
track.enabled = false;
});
But the above code would not only mute the outgoing video, but the local self-view which is rendered using that local stream, also gets black frames.
Is there a way, to ONLY mute the outgoing video frames, but still be able to show a local self-view?
There's no easy way yet. Once MediaStreamTrack.clone() is supported by browsers, you could clone the video track to get a second instance of it with a separately controllable mute property, and send one track to your self-view and the other to the peerConnection. This would let you turn off video locally and remotely independently.
Today, the only workarounds I know of would be to call getUserMedia twice on Chrome (should work on https at least, where permissions will be persisted so the user won't be prompted twice) which would get you two tracks you could video-mute independently, or on Firefox you could use RTCRtpSender.replaceTrack() with a second "fake" video stream from getUserMedia using the non-standard { video: true, fake: true } constraint like this.

Safari html5 video timeupdate event gets disabled

We are playing videos from a server. We attach an 'ontimeupdate' event which fires periodically, as the video plays. For slow connections, we can compare where the video currently IS, to where it SHOULD be. Then we can do some other things we need to do, if it is lagging. Everything works fine in Chrome, FF, IE. In Safari, when the connection is slow, the event only fires twice. Why does it get removed? Is there a way to add the event again, inside of the handler for the event? Thanks
The HTML5 audio/video element is still less than perfect. The biggest issues I've noticed is that it doesn't always behave the same way in every browser. I do not know why the timeupdate event stops firing in Safari, but one option you have is to monitor whether the video is playing or not and verifying the information independently. Example,
$(video).bind('play', function() {
playing = true;
}).bind('pause', function() {
playing = false;
}).bind('ended', function() {
playing = false;
})
function yourCheck() {
if (playing) {
if (video.currentTime != timeItShouldBe) {
//do something
}
} else {
return;
}
setTimeout( yourCheck(), 100);
}
Something to that effect. Its not perfect, but neither is the current HTML5 audio/video element. Good luck.
The event will not fire if the currentTime does not change, so it may not be firing if the video has stopped playing to buffer. However, there are other events you can listen for:
1) "stalled" - browser is trying to load the video file, but it's not getting anything from the network.
2) "waiting" - playback has stopped because you ran out of buffered data, but it will probably pick up again once more data comes in from the network. This is probably the most useful one for you.
3) "playing" - playback has resumed. Not to be confused with "play" which just means it's "trying" to play. This event fires when the video is actually playing.
4) "progress" - browser got more data from the network. Sometimes just fires every so often, but it can also fire after it recovers from the "stalled" state.
See the spec for reference.
I've heard some people say that these events can be unreliable in some browsers, but they seem to be working just fine here: http://www.w3.org/2010/05/video/mediaevents.html
If you want to be extra cautious, you can also poll periodically (with a timeout as tpdietz wrote) and check the state of the video. The readyState property will tell you whether you have enough data to show the current frame ( >= 2 ), enough to keep playing at least a little bit into the future ( >= 3 ) or enough to play all the way to the end (probably). You can also use the buffered property to see how much of the video has actually been buffered ahead of where you're playing, so you can roughly estimate the data rate (if you know how big the file is).
MDN has a great reference on all these properties and events.

Specify Quality for Youtube Chromeless Player not working?

I'm developing with the Youtube Chromeless player.
My player size is 400 x 225px.
By default, Youtube sets videos quality at a "small" level with these dimensions.
Yet, as videos with "small" quality look ugly, I would like to upgrade them to the "medium" quality level.
This is my code:
ytplayer.loadVideoById(youtube_id, start, "medium");
Unfortunately, it does not seem to work... When I do some inspection on my console:
ytplayer.getPlaybackQuality();
"small"
Is someone experiencing the same issues with the Youtube API? If not, how do you specify the quality of your Youtube videos?
====== Edit =======
I've realized that once the video has started, the setPlaybackQuality function works. Therefore, I tried the hack below. It's perfectly working but would rather find another solution...
ytplayer.loadVideoById(YOUTUBE_ID, START);
setTimeout(function(){
// If medium quality available
if(ytplayer.getAvailableQualityLevels().indexOf("medium") != -1){
ytplayer.setPlaybackQuality("medium");
}
},1000)
Thanks a lot,
Dam
Ok I finally managed to solve this problem.
I did a different hack but way less ugly. Here it is:
function onYoutubePlayerReady(playerId){
[...]
// Add event listener to monitor quality
ytplayer.addEventListener("onPlaybackQualityChange","onQualityChange");
// Triggers the video
ytplayer.loadVideoById(youtube_id, start, "medium");
[...]
}
And this is the event listener in charge of forcing the videos quality to "medium"
function onQualityChange(qualityState){
if(qualityState != "medium" && ytplayer.getAvailableQualityLevels().indexOf("medium") != -1){
ytplayer.setPlaybackQuality("medium");
}
}
Now works like a charm.
If you have any suggestion, please let me know.