How do I stream my desktop to a webpage via webRTC? - stream

I've done a tutorial that does this exact thing but streams my webcam, and I'd like to stream my desktop instead - more specifically, a certain window. This is to stream a simulation via the local network, but it has to be done on WebRTC. What should I change?
(function(){
var video = document.getElementById('video'),
vendorUrl = window.URL || window.webkitURL;
navigator.getMedia = navigator.getDisplayMedia ||
navigator.webkitURLGetDisplayMedia ||
navigator.mozGetDisplayMedia ||
navigator.msGetDisplayMedia;
//capture video
navigator.getMedia({
video: true,
Audio: false
}, function(stream){
video.src = vendorUrl.createObjectURL(stream);
video.play();
}, function(error) {
//an error occured
//error.code
});
})();

Use navigator.mediaDevices.getDisplayMedia and no need vendorUrl here.
navigator.mediaDevices.getDisplayMedia({
video: true;
audio: false;
}, function(stream) {
video.srcObject = stream;
video.onloadedmetadata = function(e){
video.play();
}
});

Related

How to Render Webcam/Live video in WebGL without Library

I have been following this tutorial and it succesfully renders video on to a cube using webGL.
Is it possible for instead of using a video I would like to use a live feed from a webcam without using frameworks like Three.js?
The code below reads from the webcam into HTMLVideoElement how to convert it into texture so I can map it to my vertices in raw WebGL?
function setupVideo(url) {
const video = document.createElement('video');
var playing = false;
var timeupdate = false;
video.autoplay = true;
video.muted = true;
video.loop = true;
// Waiting for these 2 events ensures
// there is data in the video
video.addEventListener('playing', function() {
playing = true;
checkReady();
}, true);
video.addEventListener('timeupdate', function() {
timeupdate = true;
checkReady();
}, true);
navigator.getUserMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
var hasUserMedia = navigator.getUserMedia ? true : false;
console.log(hasUserMedia);
// Prefer camera resolution nearest to 1280x720.
var constraints = { audio: true, video: { width: 640, height: 360 } };
navigator.mediaDevices.getUserMedia(constraints)
.then(function(mediaStream) {
video.srcObject = mediaStream;
video.onloadedmetadata = function(e) {
};
})
.catch(function(err) { console.log(err.name + ": " + err.message); }); // always check for errors at the end.
video.play();
function checkReady() {
if (playing && timeupdate) {
copyVideo = true;
}
}
return video;
}

Play multiple Audio files on Safari at once

I want to play multiple Audio files simultaneously on iOS .
On the click of a button I create multiple instance of an Audio file and put them into an array.
let audio = new Audio('path.wav')
audio.play().then(() => {
audio.pause();
possibleAudiosToPlay.push(audio);
});
After a while I play them all:
possibleAudiosToPlay.forEach(el => {
el.currentTime = 0;
el.play();
});
While this plays all audio files: When a new one begins it stops the old one. (on iOS)
Apples developer guide says this isn't possible at all with HTML5 Audio:
Playing multiple simultaneous audio streams is also not supported.
But can this be achieved with the Web Audio API?
There isn't anything written about it in Apples developer guide.
Yes you can with Web Audio API. You have to create an AudioBufferSourceNode for each one of your audio sources, since each source can be played only once (you can't stop it and play it again).
const AudioContext = window.AudioContext || window.webkitAudioContext;
const ctx = new AudioContext();
const audioPaths = [
"path/to/audio_file1.wav",
"path/to/audio_file2.wav",
"path/to/audio_file3.wav"
];
let promises = [];
// utility function to load an audio file and resolve it as a decoded audio buffer
function getBuffer(url, audioCtx) {
return new Promise((resolve, reject) => {
if (!url) {
reject("Missing url!");
return;
}
if (!audioCtx) {
reject("Missing audio context!");
return;
}
let xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "arraybuffer";
xhr.onload = function() {
let arrayBuffer = xhr.response;
audioCtx.decodeAudioData(arrayBuffer, decodedBuffer => {
resolve(decodedBuffer);
});
};
xhr.onerror = function() {
reject("An error occurred.");
};
xhr.send();
});
}
audioPaths.forEach(p => {
promises.push(getBuffer(p, ctx));
});
// Once all your sounds are loaded, create an AudioBufferSource for each one and start sound
Promise.all(promises).then(buffers => {
buffers.forEach(b => {
let source = ctx.createBufferSource();
source.buffer = b;
source.connect(ctx.destination);
source.start();
})
});

How to get webcam video feed in a firefox addon?

I am currently developing an addon where the requirement is to capture the webcam video. I did some testing and noticed that navigator.mediaDevices.getUserMedia() is available within panel and hence have written the following content script for the panel to get webcam video feed from addon.
var mediastream;
var mediarecorder;
// Get the instance of mediaDevices object to use.
navigator.mediaDevices = navigator.mediaDevices || ((navigator.mozGetUserMedia || navigator.webkitGetUserMedia) ? {
getUserMedia: function(c) {
return new Promise(function(y, n) {
(navigator.mozGetUserMedia ||
navigator.webkitGetUserMedia).call(navigator, c, y, n);
});
}
} : null);
function startVideoCapture(width, height, framerate) {
// Check if the browser supports video recording
if (!navigator.mediaDevices) {
return;
}
// Lets initialize the video settings for use for our video recording session
var constraints = { audio: false, video: { width: 640, height: 320, framerate: 25 } };
// Make request to start video capture
navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
// Lets initialize the timestamp for this video
var date = new Date();
var milliseconds = "000" + date.getMilliseconds();
var timestamp = date.toLocaleFormat("%Y-%m-%d %H:%M:%S.") + milliseconds.substr(-3);
// Lets make the stream globally available so that we will be able to control it later.
mediastream = stream;
// Lets display the available stream in the video element available inside the panel.
var video = document.querySelector('video');
video.src = window.URL.createObjectURL(stream);
video.onloadedmetadata = function(e) {
video.play();
};
// We are not here to just show the video to screen. Lets get a media recorder to store the video into memory
mediarecorder = new MediaRecorder(stream);
// Lets decide what to do with the recorded video once we are done with the recording
mediarecorder.ondataavailable = function(evt) {
// recorded video will be available as a blob in evt.data object.
// The only way to use it properly is through FileReader Object
var reader = new FileReader();
// Lets decide what we are going to do with the data that we will read from blob
reader.onloadend = function() {
// create a video object containing the timestamp and the binary video string
var videoObject = new Object();
videoObject.timestamp = timestamp;
videoObject.video = reader.result;
// send the video to the main script for safe keeping
self.port.emit("videoAvailable", videoObject);
}
// instruct the FileReader to start reading the blob
reader.readAsBinaryString(evt.data);
}
// Lets start the video capture
mediarecorder.start();
})
.catch(function(err) {
self.port.emit("VideoError", err);
});
}
function stopVideoCapure(){
if (mediarecorder !== undefined && mediarecorder !== null) {
mediarecorder.stop();
}
if (mediastream !== undefined && mediastream !== null) {
mediastream.stop();
}
}
function updateVideoSettings(settings){
stopVideoCapture();
startVideoCapture(settings.width, settings.height, settings.framerate);
}
self.port.on("VideoPreferenceUpdated", updateVideoSettings);
// Start video capture
startVideoCapture(self.options.width, self.options.height, self.options.framerate);
Now the problem here is the code is perfectly working when from a webpage i.e. if I save the open the panel.html file directly in the browser with proper adjustment of self.options and self.port lines. But when I am using the code in the contentscript for panel in my addon, I am getting the following error
JavaScript error: resource:///modules/webrtcUI.jsm, line 186: TypeError: stringBundle is undefined
Now that is an error from the inbuilt jsm module in firefox. Is there a way I can get past that error or any other way to get webcam video feed in my addon?
Thanks

YouTube embed showing device support option

I embed Youtube videos in my angular app using two directives which make use of the YouTube Iframe API. The first loads the library async
angular.module('myApp')
.service('youTubeService', function($rootScope, $window) {
var self = this;
self.ready = false;
$window.onYouTubeIframeAPIReady = function () {
self.ready = true;
console.log("Youtube service ready");
$rootScope.$broadcast('youTubeServiceReady', true);
};
var tag = document.createElement('script');
tag.src = '//www.youtube.com/iframe_api';
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
});
I then embed the video using the javascript library
angular.module('myApp')
.directive('youtube', function (youTubeService) {
return {
link: function (scope, element, attrs) {
var player;
var playerReady = false;
var playerState;
var callback;
var carouselScope = element.parent().parent().scope();
function createPlayer() {
player = new YT.Player(element[0], {
height: attrs.height,
width: attrs.width,
videoId: attrs.youtube,
playerVars: { 'start' : attrs.starttime, 'end' : attrs.endtime, 'origin': 'https://', showinfo: 0, modestbranding: 1 },
events: {
onReady: function () {
playerReady = true;
// if (callback !== null) {
// callback();
// }
},
onStateChange: function (event) {
//console.log("Time:" + getCurrentTime() + ", Duration:" + getDuration() );
playerState = event.data;
if (playerState === YT.PlayerState.PAUSED) {
carouselScope.play();
}
}
}
});
}
if (youTubeService.ready) {
createPlayer();
} else {
scope.$on('youTubeServiceReady', function () {
createPlayer();
});
}
...
This was working for months up until yesterday but now I get the following video as my embed in all desktop browsers as documented here
https://support.google.com/youtube/answer/6098135?hl=en-GB
My problem is I can't figure out what I should be changing because as far as I understand the iframe api is the correct one. Does anyone know what I should be changing?
So we were having the exact same issue with our site.
It turns out that our client, which uses code very similar to yours above is functioning correctly. Our problem ended up being the way in which we were adding videos and video meta data to our database.
This might not be your issue, but we were using
http://gdata.youtube.com/feeds/api/videos/<video id>?v=2&alt=json
to add videos to our system. As this turns out to be a deprecated endpoint, we had to upgrade to the v3 system which is explained here: https://developers.google.com/youtube/v3/docs/videos/list

Distorted audio in iOS 7.1 with WebAudio API

On iOS 7.1, I keep getting a buzzing / noisy / distorted sound when playing back audio using the Web Audio API. It sounds distorted like this, in place of normal like this.
The same files are fine when using HTML5 audio. It all works fine on desktop (Firefox, Chrome, Safari.)
EDIT:
The audio is distorted in the iOS Simulator versions iOS 7.1, 8.1, 8.2. The buzzing sound often starts before I even playback anything.
The audio is distorted on a physical iPhone running iOS 7.1, in both Chrome and Safari.
The audio is fine on a physical iPhone running iOS 8.1, in both Chrome and Safari.
i.e.: the buzzing audio is on iOS 7.1. only.
Howler.js is not the issue. The problem is still there using pure JS like so:
var context;
var sound;
var extension = '.' + ( new Audio().canPlayType( 'audio/ogg' ) !== '' ? 'ogg' : 'mp3');
/** Test for WebAudio API support **/
try {
// still needed for Safari
window.AudioContext = window.AudioContext || window.webkitAudioContext;
// create an AudioContext
context = new AudioContext();
} catch(e) {
// API not supported
throw new Error( 'Web Audio API not supported.' );
}
function loadSound( url ) {
var request = new XMLHttpRequest();
request.open( 'GET', url, true );
request.responseType = 'arraybuffer';
request.onload = function() {
// request.response is encoded... so decode it now
context.decodeAudioData( request.response, function( buffer ) {
sound = buffer;
}, function( err ) {
throw new Error( err );
});
}
request.send();
}
function playSound(buffer) {
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.start(0);
}
loadSound( '/tests/Assets/Audio/En-us-hello' + extension );
$(document).ready(function(){
$( '#clickme' ).click( function( event ) {
playSound(sound);
});
}); /* END .ready() */
A live version of this code is available here: Web Audio API - Hello world
Google did not bring up any result about such a distorted sound issue on iOS 7.1.
Has anyone else run into it? Should I file a bug report to Apple?
I believe the issue is caused due to resetting the audioContext.sampleRate prop, which seem to happen after the browser/OS plays something recorded in a different sampling rate.
I've devised the following workaround, which basically silently plays a short wav file recorded in the sampling rate that the device currently does playback on:
"use strict";
var getData = function( context, filePath, callback ) {
var source = context.createBufferSource(),
request = new XMLHttpRequest();
request.open( "GET", filePath, true );
request.responseType = "arraybuffer";
request.onload = function() {
var audioData = request.response;
context.decodeAudioData(
audioData,
function( buffer ) {
source.buffer = buffer;
callback( source );
},
function( e ) {
console.log( "Error with decoding audio data" + e.err );
}
);
};
request.send();
};
module.exports = function() {
var AudioContext = window.AudioContext || window.webkitAudioContext,
context = new AudioContext();
getData(
context,
"path/to/short/file.wav",
function( bufferSource ) {
var gain = context.createGain();
gain.gain.value = 0;
bufferSource.connect( gain );
gain.connect( context.destination );
bufferSource.start( 0 );
}
);
};
Obviously, if some of the devices have different sampling rates, you would need to detect and use a specific file for every rate.
it looks like iOS6+ Safari defaults to a sample rate of 48000. If you type this into the developer console when you first open mobile safari, you'll get 48000:
var ctx = new window.webkitAudioContext();
console.log(ctx.sampleRate);
Further Reference: https://forums.developer.apple.com/thread/20677
Then if you close the initial context on load: ctx.close(), the next created context will use the sample rate most other browsers use (44100) and sound will play without distortion.
Credit to this for pointing me in the right direction (and in case the above no longer works in the future): https://github.com/Jam3/ios-safe-audio-context/blob/master/index.js
function as of post date:
function createAudioContext (desiredSampleRate) {
var AudioCtor = window.AudioContext || window.webkitAudioContext
desiredSampleRate = typeof desiredSampleRate === 'number'
? desiredSampleRate
: 44100
var context = new AudioCtor()
// Check if hack is necessary. Only occurs in iOS6+ devices
// and only when you first boot the iPhone, or play a audio/video
// with a different sample rate
if (/(iPhone|iPad)/i.test(navigator.userAgent) &&
context.sampleRate !== desiredSampleRate) {
var buffer = context.createBuffer(1, 1, desiredSampleRate)
var dummy = context.createBufferSource()
dummy.buffer = buffer
dummy.connect(context.destination)
dummy.start(0)
dummy.disconnect()
context.close() // dispose old context
context = new AudioCtor()
}
return context
}

Resources