I'm attempting to access the microphone on iOS Safari with the help of the getUserMedia. Below you can find a snippet of my code.
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function(constraints) {
// First get ahold of the legacy getUserMedia, if present
let getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
// Some browsers just don't implement it - return a rejected promise with an error
// to keep a consistent interface
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
}
// Otherwise, wrap the call to the old navigator.getUserMedia with a Promise
return new Promise(function(resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
}
}
navigator.mediaDevices.getUserMedia({
audio: true
}).then(function(stream) {
successCallBack(.......);
}).catch(function(error) {
debug.log(error);
..........
});
Yet the promise always catches an error, to be more specific an OverConstraintError.
{message: "Invalid constraint", constraint: ""}
This behaviour is unique for iOS Safari, on all other browsers (Chrome, Firefox, Safari osX) it works without any problem. Actually my issue ressembles a lot like this one => How to resolve iOS 11 Safari getUserMedia "Invalid constraint" issue, yet I'm not trying to use the camera. The only thing that interests me is the microphone.
I'm testing with a real iPhone (a 5 and X, both updated to the latest version), so it is not linked to the iPhone Simulator.
The access to the microphone is granted and the popup requesting permissions is also showing, so it is not an permissions issue.
This issue may be related to this bug report titled getUserMedia fails with an OverConstrainedError when no devices are found:
https://bugs.webkit.org/show_bug.cgi?id=177126
Here is your code running in Codepen. As you stated, audio is enabled and works. Note the enumerateDevices() call returns an empty array. As the bug states, this causes the error in your question:
.catch(function(error) {
navigator.mediaDevices.enumerateDevices().then(devices=>{console.log(devices)});
console.log('error: ',error);
});
https://codepen.io/anon/pen/rQWRyZ?editors=0011
web rtc or getusermedia has some issues and it is not working on all platforms as u expect - have same problems with camera detection like in samsung s5 same code is working fine but under newer device it was failing.
My advice is to use webrtc adapter js.
Try simply include this script:
<script src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/6.4.0/adapter.js" type="text/javascript"></script>
before u use getusermedia api. I think that 99% of issues just disappear.
Related
I am using getusermedia to record audio in my website. I've included the javascript of getusermedia. The record function works fine on website but doesn't even popup for permission in when opened on mobile.
var constraints = {
audio: true,
video: false
}
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
permissiongiven = 1;
});
this is the sample code which prompt me for permission but doesn't work on mobile.
How can I make it work on both device. Any help is appreciated.
Thanks.
Edit :- The code is now working fine on mobile as well. Maybe it was solved from chrome updates or security certificates of website. Thanks all for help.
Note that on mobile devices, you have to use SSL to use getUserMedia(). Check out if you're accessing your website with a http:// prefix. Try change it to https:// and it should work fine.
I found this website useful in answering this question
https://caniuse.com/#search=getusermedia
It says getUserMedia only supported from Chrome for Android version 70 (released Oct 16 2018)
Check MDN, then make sure your browser supports it. You need Chrome for Android 52+ it says. FWIW 68 works for me.
You should also feature-check and catch errors:
if (!navigator.mediaDevices) {
console.log("Sorry, getUserMedia is not supported");
return;
}
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => permissiongiven = 1)
.catch(error => console.log(error));
Depending on the error you get, we might learn more.
NotFoundError means no camera or mic detected on the device.
NotAllowedError means user permission not granted, or you're in an iframe or not https.
navigator.mediaDevices.getUserMedia Browser compatibility
i had no idea that navigator.getUserMedia was deprecated (at least for Samsung, which is what i was testing on), and i found this article on web audio with a code sample that i barely modified:
function initGetUserMedia() {
navigator.mediaDevices = navigator.mediaDevices || {}
navigator.mediaDevices.getUserMedia = navigator.mediaDevices.getUserMedia || function(constraints) {
let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia not supported by this browser'));
} else {
return new Promise((resolve, reject) => {
getUserMedia.call(navigator, constraints, resolve, reject);
});
}
}
}
Try this code with ssl site:
<script>var constraints = {audio: true, video: false}
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {{permissiongiven = 1;});</script>
is there a way to detect if calling play() on a video element is allowed without a user gesture?
On Android Chrome this warning is given:
Failed to execute 'play' on 'HTMLMediaElement': API can only be initiated by a user gesture.
So on Chrome Android a user gesture is required to start the playback of a video, while it isn't on desktop Chrome.
Is there a way to detect which behavior I will get?
I want to have slightly different behavior in my app depending on if calling play programatically is allowed or not.
I have tried to use Modernizr.videoautoplay, but that checks if the autoplay property on the element, which is not the same thing. This gives false negatives for IE11 and Edge.
Edit: added an example. The video will start playing automatically in Chrome desktop and IE11 or Edge (with 3s delay) on windows 8 or 10. For Chrome#Android a user interaction is needed (clicking the button) and the error message can be seen in the console.
The play method returns a promise which can be used to catch the error.
Not all browsers follow the specification so you will have to check if what is returned is a promise first.
var autoPlayAllowed = true;
var promise = document.createElement("video").play();
if(promise instanceof Promise) {
promise.catch(function(error) {
// Check if it is the right error
if(error.name == "NotAllowedError") {
autoPlayAllowed = false;
} else {
throw error;
}
}).then(function() {
if(autoPlayAllowed) {
// Allowed
} else {
// Not allowed
}
});
} else {
// Unknown if allowed
}
I'm using the HTML5 notification API to notify the user in Chrome or Firefox. On desktop browsers, it works. However in Chrome 42 for Android, the permission is requested but the notification itself is not displayed.
The request code, works on all devices:
if ('Notification' in window) {
Notification.requestPermission();
}
The sending code, works on desktop browser but not on mobile:
if ('Notification' in window) {
new Notification('Notify you');
}
Try the following:
navigator.serviceWorker.register('sw.js');
Notification.requestPermission(function(result) {
if (result === 'granted') {
navigator.serviceWorker.ready.then(function(registration) {
registration.showNotification('Notification with ServiceWorker');
});
}
});
That is, use ServiceWorkerRegistration»showNotification() not new Notification().
That should work on Android both in Chrome and in Firefox — and on iOS in Safari, too.
(The sw.js file can just be a zero-byte file.)
One caveat is that you must run it from a secure origin (an https URL, not an http URL).
See https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/showNotification.
If you already have a service worker registered, use this:
navigator.serviceWorker.getRegistrations().then(function(registrations) {
registrations[0].showNotification(title, options);
});
Running this code:
if ('Notification' in window) {
Notification.requestPermission();
}
Console in Chrome DevTools shows this error:
Uncaught TypeError: Failed to construct ‘Notification’: Illegal
constructor. Use ServiceWorkerRegistration.showNotification() instead
A better approach might be:
function isNewNotificationSupported() {
if (!window.Notification || !Notification.requestPermission)
return false;
if (Notification.permission == 'granted')
throw new Error('You must only call this \*before\* calling
Notification.requestPermission(), otherwise this feature detect would bug the
user with an actual notification!');
try {
new Notification('');
} catch (e) {
if (e.name == 'TypeError')
return false;
}
return true;
}
Function Source: HTML5Rocks
I had no trouble with the Notification API on Windows Desktop. It even worked without issues on Mobile FF. I found documentation that seemed to indicate Chrome for Android was supported too, but it didn't work for me. I really wanted to prove the API could work for me on my current (2019) version of Chrome (70) for Android. After much investigation, I can easily see why many people have had mixed results. The answer above simply didn't work for me when I pasted it into a barebones page, but I discovered why. According to the Chrome debugger, the Notification API is only allowed in response to a user gesture. That means that you can't simply invoke the notification when the document loads. Rather, you have to invoke the code in response to user interactivity like a click.
So, here is a barebones and complete solution proving that you can get notifications to work on current (2019) Chrome for Android (Note: I used jQuery simply for brevity):
<html>
<head>
<script type="text/javascript" src="libs/jquery/jquery-1.12.4.min.js"></script>
<script>
$( function()
{
navigator.serviceWorker.register('sw.js');
$( "#mynotify" ).click( function()
{
Notification.requestPermission().then( function( permission )
{
if ( permission != "granted" )
{
alert( "Notification failed!" );
return;
}
navigator.serviceWorker.ready.then( function( registration )
{
registration.showNotification( "Hello world", { body:"Here is the body!" } );
} );
} );
} );
} );
</script>
</head>
<body>
<input id="mynotify" type="button" value="Trigger Notification" />
</body>
</html>
In summary, the important things to know about notifications on current (2019) Chrome for Android:
Must be using HTTPS
Must use Notification API in response to user interactivity
Must use Notification API to request permission for notifications
Must use ServiceWorker API to trigger the actual notification
new Notification('your arguments'); This way of creating notification is only supported on desktop browsers, not on mobile browsers. According to the link below. (scroll down to the compatibility part)
https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API/Using_the_Notifications_API
For mobile browsers below is the way you create a notification (this also works on desktop browsers)
navigator.serviceWorker.ready.then( reg => { reg.showNotification("your arguments goes here")});
Tested on browsers using webkit engine.
For more information please visit below links:
https://developers.google.com/web/updates/2015/05/notifying-you-of-changes-to-notifications
https://developers.google.com/web/fundamentals/push-notifications/display-a-notification
I'm using getUserMedia() for audio recording and it works correctly but have an issue with it.
I want to display a message before starting recording that any microphone is connected with system or not.
For this I have used following code and run this into chrome but it was not working correctly.
if(navigator.getUserMedia || navigator.webkitGetUserMedia)
{
alert("Microphone is connected with your system");
} else {
alert("Microphone is not connected with your system");
}
when microphone is not connected then also above code giving message "Microphone is connected with your system".
so please suggest me a better way to detect microphone using JavaScript in any browser.
Testing for the existence of these functions does not detect the existence of hardware microphone. It only detects if browser has the API to do so.
The browsers that pass your test need not have a physical microphone plugged into microphone jack. It is simply a newer browser. The browsers that fail the test may have a microphone, but are old browsers that do not contain the API.
Also, at least the getUserMedia function is asynchronous, so any code that depends on using the audio or video must be put in a callback function, not the main script.
See https://developer.mozilla.org/en-US/docs/Web/API/Navigator.getUserMedia for an example of how to write cross-browser code for audio/video input.
Something like this:
function success(stream) {
// we have it
}
function fail(error) {
console.log(error);
if (error === 'NO_DEVICES_FOUND') {
// NO_DEVICES_FOUND (no microphone or microphone disabled)
}
}
navigator.getUserMedia({ audio: true }, success, fail);
Try this
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
// Code for success
}).catch(err => {
if(err.includes("NotFoundError: Requested device not found"))
alert("Mic not detected")
else alert("Error recording audio")
})
If the mic is not detected or unplugged means the catch statement will be executed. You can show your error message here.
You can use this link
I used this method
developer.mozilla.org
I'm on Chrome 25 successfully using getUserMedia and RTCPeerConnection to connect audio from a web page to another party, but I'm unable to get the API to stop the red blinking indication icon in the Chrome tab that media is being used on that page. My question is essentially a duplicate of Stop/Close webcam which is opened by navigator.getUserMedia except that the resolution there isn't working. If I have a page that just uses getUserMedia with no remote media (no peer), then stopping the camera turns off the blinking tab indicator. Adding remote streams seems to be a/the issue. Here's what I've currently got for my "close" code:
if (localStream) {
if (peerConnection && peerConnection.removeStream) {
peerConnection.removeStream(localStream);
}
if (localStream.stop) {
localStream.stop();
}
localStream.onended = null;
localStream = null;
}
if (localElement) {
localElement.onerror = null;
localElement.pause();
localElement.src = undefined;
localElement = null;
}
if (remoteStream) {
if (peerConnection && peerConnection.removeStream) {
peerConnection.removeStream(remoteStream);
}
if(remoteStream.stop) {
remoteStream.stop();
}
remoteStream.onended = null;
remoteStream = null;
}
if (remoteElement) {
remoteElement.onerror = null;
remoteElement.pause();
remoteElement.src = undefined;
remoteElement = null;
}
if (peerConnection) {
peerConnection.close();
peerConnection = null;
}
I've tried with and without the removeStream() call, I've tried with and without the stop() call, I've tried the element.src="" and element.src=null, I'm running out of ideas. Anyone know if this is a bug or user/my error in the use of the API?
EDIT: I set my default device (using Windows) to a camera that has a light when it's in use, and upon stopping, the camera light goes off, so perhaps this is a Chrome bug. I also discovered that if I use chrome://settings/content to change the microphone device to anything other than "Default", Chrome audio fails altogether. And finally, I realized that using element.src=undefined resulted in Chrome attempting to load a resource and throwing a 404 so that's clearly not correct... so back to element.src='' on that.
Ended up being my fault (yes, shocking). Turns out I wasn't saving localStream correctly in the onUserMediaSuccess callback of getUserMedia... once that was set, Chrome is turning off the blinking recording icon. That didn't explain the other anomalies, but it closes the main point of the question.
I just got this working yesterday after trawling through the WebRTC specification. I don't know if this is the "right" way to do it, but I found that renegotiating the PeerConnection with a new offer after removing the stream did the trick.
var pc = peerConnections[socketId];
pc.removeStream(stream);
pc.createOffer( function(session_description) {
pc.setLocalDescription(session_description);
_socket.send(JSON.stringify({
"eventName": "send_offer",
"data":{
"socketId": socketId,
"sdp": session_description
}
}));
},
function(error) {},
defaultConstraints);