Skip to content

Instantly share code, notes, and snippets.

@steveseguin
Last active April 21, 2020 23:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steveseguin/6c17dfb7b0c8caef3fac6557e938a6f1 to your computer and use it in GitHub Desktop.
Save steveseguin/6c17dfb7b0c8caef3fac6557e938a6f1 to your computer and use it in GitHub Desktop.
Camera getUserMedia /w Mobile Support
function grabVideo(quality=0, audioEnable=false){
if( activatedPreview == true){log("activeated preview return");return;}
activatedPreview = true;
log(quality);
log("trying with quality:");
var audioSelect = document.querySelector('select#audioSource');
var videoSelect = document.querySelector('select#videoSource');
var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
if (iOS){ // iOS will not work correctly at 1080p; likely a h264 codec issue.
if (quality==0){
quality=1;
}
}
var audio = false;
if (audioEnable==true){
if (audioSelect.value!=="ZZZ"){
audio = {deviceId: {exact: audioSelect.value}};
if (session.stereo){
audio.echoCancellation = false;
audio.autoGainControl = false;
audio.noiseSuppression = false;
}
}
}
var constraints = {
audio: audio,
video: getUserMediaVideoParams(quality, iOS)
};
constraints.video.deviceId = { exact: videoSelect.value };
if (session.width){
constraints.video.width = {exact: session.width};
}
if (session.height){
constraints.video.height = {exact: session.height};
}
if (session.framerate){
constraints.video.frameRate = {exact: session.framerate};
} else if (session.maxframerate){
constraints.video.frameRate = {max: session.maxframerate};
}
log(constraints);
setTimeout(()=>{
try {
log("Trying Constraints");
var oldstream= document.getElementById('previewWebcam').srcObject;
if (oldstream){
oldstream.getTracks().forEach(function(track) {
track.stop();
});
}
} catch(e){
errorlog(e);
}
navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
if (audioEnable == false){
stream.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now.
track.stop();
});
log("GOT IT BUT WITH NO AUDIO");
activatedPreview = false;
grabVideo(quality,true);
}else {
document.getElementById('previewWebcam').srcObject = stream; // set the preview window and run with it
log("DONE - found stream");
}
}).catch(function(e){
activatedPreview = false;
errorlog(e);
if (e.name === "OverconstrainedError"){
errorlog(e.message);
log("Resolution didn't work");
} else if (e.name === "NotReadableError"){
if (iOS){
alert("An error occured. Upgrading to at least iOS 13.4 should fix this glitch from happening again");
} else {
alert("Error Listing Media Devices.\n\nThe default Camera may already be in use with another app. Typically webcams can only be accessed by one program at a time.\n\nThe selected device may also not be supported.");
}
activatedPreview=true;
return;
} else if (e.name === "NavigatorUserMediaError"){
alert("Unknown error: 'NavigatorUserMediaError'");
return;
} else {
errorlog("An unknown camera error occured");
}
if (quality<=9){
grabVideo(quality+1);
} else {
errorlog("********Camera failed to work");
activatedPreview=true;
alert("Camera failed to load. Please make sure it is not already in use by another application.");
}
});
},0);
}
var activatedPreview = false;
function setupWebcamSelection(){
enumerateDevices().then(gotDevices).then(function(){
if (parseInt(document.getElementById("webcamquality").elements.namedItem("resolution").value)==3){
session.maxframerate = 30;
} else {
session.maxframerate = false;
}
var audioSelect = document.querySelector('select#audioSource');
var videoSelect = document.querySelector('select#videoSource');
audioSelect.onchange = function(){
log("AUDIO source CHANGED");
activatedPreview=false;
grabVideo(parseInt(document.getElementById("webcamquality").elements.namedItem("resolution").value));
};
videoSelect.onchange = function(){
log("video source changed");
activatedPreview=false;
grabVideo(parseInt(document.getElementById("webcamquality").elements.namedItem("resolution").value));
};
document.getElementById("webcamquality").onchange = function(){
log("AUDIO source CHANGED");
activatedPreview=false;
if (parseInt(document.getElementById("webcamquality").elements.namedItem("resolution").value)==3){
session.maxframerate = 30;
} else {
session.maxframerate = false;
}
grabVideo(parseInt(document.getElementById("webcamquality").elements.namedItem("resolution").value));
};
activatedPreview = false;
grabVideo(parseInt(document.getElementById("webcamquality").elements.namedItem("resolution").value));
}).catch(handleError);
}
function previewWebcam(){
if( activatedPreview == true){log("activeated preview return");return;}
activatedPreview = true;
window.setTimeout(() => {
var oldstream= document.getElementById('previewWebcam').srcObject;
if (oldstream){
oldstream.getTracks().forEach(function(track) {
track.stop();
});
}
navigator.mediaDevices.getUserMedia({audio:true, video:true }).then(function(stream){ // Apple needs thi to happen before I can access EnumerateDevices.
//document.getElementById('previewWebcam').srcObject=stream;
stream.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now.
track.stop();
});
setupWebcamSelection();
}).catch(function(e){
errorlog("trying to list webcam again");
setupWebcamSelection();
});
},0);
}
function enumerateDevices() {
if (typeof navigator.enumerateDevices === "function") {
return navigator.enumerateDevices();
}
else if (typeof navigator.mediaDevices === "object" &&
typeof navigator.mediaDevices.enumerateDevices === "function") {
return navigator.mediaDevices.enumerateDevices();
}
else {
return new Promise((resolve, reject) => {
try {
if (window.MediaStreamTrack == null || window.MediaStreamTrack.getSources == null) {
throw new Error();
}
window.MediaStreamTrack.getSources((devices) => {
resolve(devices
.filter(device => {
return device.kind.toLowerCase() === "video" || device.kind.toLowerCase() === "videoinput";
})
.map(device => {
return {
deviceId: device.deviceId != null ? device.deviceId : "",
groupId: device.groupId,
kind: "videoinput",
label: device.label,
toJSON: /* istanbul ignore next */ function () {
return this;
}
};
}));
});
}
catch (e) {
errorlog(e);
}
});
}
}
function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-pages/src/content/devices/input-output/js/main.js#L19
const audioInputSelect = document.querySelector('select#audioSource');
const videoSelect = document.querySelector('select#videoSource');
const selectors = [audioInputSelect, videoSelect];
// Handles being called several times to update labels. Preserve values.
const values = selectors.map(select => select.value);
selectors.forEach(select => {
while (select.firstChild) {
select.removeChild(select.firstChild);
}
});
log(deviceInfos);
for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
const option = document.createElement('option');
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === 'audioinput') {
option.text = deviceInfo.label || `microphone ${audioInputSelect.length + 1}`;
audioInputSelect.appendChild(option);
} else if (deviceInfo.kind === 'videoinput') {
option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`;
videoSelect.appendChild(option);
} else {
log('Some other kind of source/device: ', deviceInfo);
}
}
const option = document.createElement('option');
option.text = "Disable Audio";
option.value = "ZZZ"; // :D
audioInputSelect.appendChild(option); // NO AUDIO OPTION
selectors.forEach((select, selectorIndex) => {
if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) {
select.value = values[selectorIndex];
}
});
audioInputSelect.selectedIndex = 0;
}
function handleError(error) {
errorlog(error);
}
function getUserMediaVideoParams(resolutionFallbackLevel, isSafariBrowser) {
switch (resolutionFallbackLevel) {
case 0:
if (isSafariBrowser) {
return {
width: { min: 360, ideal: 1920, max: 1920 },
height: { min: 360, ideal: 1080, max: 1080 }
};
} else {
return {
width: { min: 720, ideal: 1920, max: 1920 },
height: { min: 720, ideal: 1080, max: 1920 }
};
}
case 1:
if (isSafariBrowser) {
return {
width: { min: 360, ideal: 1280, max: 1280 },
height: { min: 360, ideal: 720, max: 720 }
};
} else {
return {
width: { min: 720, ideal: 1280, max: 1280 },
height: { min: 720, ideal: 720, max: 1280 }
};
}
case 2:
if (isSafariBrowser) {
return {
width: { min: 360, ideal: 1280, max: 1440 },
};
}
else {
return {
width: { min: 360, ideal: 1280, max: 1440 },
};
}
case 3:
if (isSafariBrowser) {
return {
width: { min: 640 },
height: { min: 360 }
};
} else {
return {
width: { min: 240, ideal: 640, max: 1280 },
height: { min: 240, ideal: 360, max: 1280 }
};
}
case 4:
if (isSafariBrowser) {
return {
height: { min: 360, ideal: 720, max: 960 }
};
}
else {
return {
height: { min: 360, ideal: 960, max: 960 }
};
}
case 5:
if (isSafariBrowser) {
return {
width: { min: 360, ideal: 640, max: 1440 },
height: { min: 360, ideal: 360, max: 720 }
};
}
else {
return {
width: { min: 360, ideal: 640, max: 3840 },
height: { min: 360, ideal: 360, max: 2160 }
};
}
case 6:
if (isSafariBrowser) {
return {}; // iphone users probably don't need to wait any longer, so let them just get to it
}
else {
return {width: {min:360,max:1920},
height: {min:360, max:1920}}; // same as default, but I didn't want to mess with framerates until I gave it all a try first
}
case 7:
return { // If the camera is recording in low-light, it may have a low framerate. It coudl also be recording at a very high resolution.
width: { min: 360, ideal: 640 },
height: { min: 360, ideal: 360 },
frameRate: 10
};
case 8:
return {width: {min:360,max:1920}, height: {min:360, max:1920}}; // same as default, but I didn't want to mess with framerates until I gave it all a try first
case 9:
return {frameRate: 0 }; // Some Samsung Devices report they can only support a framerate of 0.
default:
return {};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment