We have a website with a background video. And have an issue, when a user is on Low power mode on IOS devices (iPhone, Mac, etc.).
Is it possible to handle only low power mode and set fallback image instead of video with play button?
I saw a variant with a suspend event, but it fired also when the video fully loaded, so it's not a correct solution for us.
You can use javaScript battery API to track the battery status
Seems I found a solution:
The most correct way, in my opinion, is just handle error throwing (in catch block) and set a fallback image instead of video.
FYI: This solution is relevant only when the video is in the background (from a UX point of view)
So the solution is:
`
useEffect(() => {
if (videoRef.current) {
videoRef.current?
.play()
.then(() => {})
.catch((error) => {
setVideoPaused(true);
return error;
});
}
}, []);
`
Check this link in a low power mode https://video-dev.github.io/can-autoplay/
You will see that the error you get is "NotAllowedError" because the request is not allowed in the current context. That really goes for any situation where autoPlay is prevented by the userAgent or the system settings.
So throw it in the catch and set state for the fallback like mentioned above
.catch((error) => {
if (error.name === "NotAllowedError") {
//low power mode
}
Seems to work pretty reliably
Related
Is there a JavaScript API to detect low power mode for iOS 11 Safari?
I have not found any info on the subject yet.
A workaround to get this done is:
Put a video (hidden) in the webpage with autoplay 'on',
and then detect the 'suspend' event for that video.
Whenever it is invoked, 'power mode is on'.
Here is the script:
const videoElement = document.getElementById('ID_of_video');
videoElement.addEventListener('suspend', () => {
// suspend invoked
// show play button
// iphone is in low power mode
});
videoElement.addEventListener('play', () => {
// video is played
// remove play button UI
// iphone is not in low power mode
});
You can also read the explanation here: Playing video when low power mode is on
Safari on iOS does not support accessing the status of the low power mode setting (it does not even support accessing the battery level).
.catch((error) => {
if (error.name === "NotAllowedError") {
//low power mode
}
I struggled with with same concept and one helpful link proved to be this browser check: https://video-dev.github.io/can-autoplay/
result: Video muted (Error "NotAllowedError": The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
So I now have a promise checking if the video is playing, if it is great. Otherwise I catch the NotAllowedError, seems to work ever time.
Just like the "suspend" event suggestion, use a hidden video on the with autoplay loop muted attrs, then listen to the play event when the video is mounted, like so:
testVideo
.play()
.then(() => {})
.catch((error) => {
//Do something like adding controls to allow user to manually play
});
});
This is the solution as of March 2022, since browsers aren't triggering the suspend event for autoplay cancellation.
For iOS 16 Safari, "Low Power Mode" (or say power saving mode) can be detected by checking actual interval of setInterval, because cumulative delay [^1] effect happens in Low Power Mode.
For desktop Chromium 108+, experimental Battery Saver Mode (or say power saving mode) can be detected by checking frame rate of requestAnimationFrame
see online demo:
Power Saving Mode detection
requestAnimationFrame fps detection
[^1]: cumulative-delay means actual average interval is obviously greater than given interval
I need to tell if a person dismisses the media popup or blocks the media popup
const [permissions, setPermissions] = useState(false)
const handleClick = () => {
setPermissions('pending');
navigator.getUserMedia({ video: true, audio: true }, stream => {
stream.getTracks().forEach(track => {
return setPermissions(track.enabled);
});
}, (error) => {
if(error ==='DOMException: Permission denied'){
setPermissions('denied')
}
if(error === 'DOMException: Permission dismissed'){
setPermissions('dismissed')
}
});
};
I tried using the error string that I got back but it is not working. I need to be able to tell the difference between the errors. Does anyone have a good way to do this? Thanks!
You can't tell the difference in any exact way. Browsers are very uncooperative with Javascript code that tries to access media devices but doesn't get permission from the user. Because cybercreeps.
I've had a bit of success with this to see if the user denied access:
Try again. If you get an immediate (within a couple of seconds) error they probably have already denied access to the devices. If it takes longer, they probably are looking at a repeat of the permission dialog.
Not a great situation. Especially if you want to explain to the user how to go back and un-deny permission. But it's a necessary privacy feature in the surveillance era.
I want to capture video with the webcamera.
And there is the right decision:
window.onload = function () {
var video = document.getElementById('video');
var videoStreamUrl = false;
navigator.getUserMedia({video: true}, function (stream) {
videoStreamUrl = window.URL.createObjectURL(stream);
video.src = videoStreamUrl;
}, function () {
console.log('error');
});
};
but produces an error in the browser:
[Deprecation] URL.createObjectURL with media streams is deprecated and will be removed in M68, around July 2018. Please use HTMLMediaElement.srcObject instead. See https://www.chromestatus.com/features/5618491470118912 for more details.
how to use HTMLMediaElement.srcObject for my purposes ? Thanks for your time!
MediaElement.srcObject should allow Blobs, MediaSources and MediaStreams to be played in the MediaElement without the need to bind these sources in the memory for the lifetime of the document like blobURIs do.
(Currently no browser support anything else than MediaStream though...)
Indeed, when you do URL.createObjectURL(MediaStream), you are telling the browser that it should keep alive this Source until your revoke the blobURI, or until the document dies.
In the case of a LocalMediaStream served from a capturing device (camera or microphone), this also means that the browser has to keep the connection to this device open.
Firefox initiated the deprecation of this feature, one year or so ago, since srcObject can provide the same result in better ways, easier to handle for everyone, and hence Chrome seems to finally follow (not sure what's the specs status about this).
So to use it, simply do
MediaElement.srcObject = MediaStream;
Also note that the API you are using is itself deprecated (and not only in FF), and you shouldn't use it anymore. Indeed, the correct API to capture MediaStreams from user Media is the MediaDevices.getUserMedia one.
This API now returns a Promise which gets resolved to the MediaStream.
So a complete correction of your code would be
var video = document.getElementById('video');
navigator.mediaDevices.getUserMedia({
video: true
})
.then(function(stream) {
video.srcObject = stream;
})
.catch(function(error) {
console.log('error', error);
});
<video id="video"></video>
Or as a fiddle since StackSnippets® overprotected iframe may not deal well with gUM.
I use webRTC (getUserMedia) for recording sound and uploading it to backend server. All works well except i am unable to determine the microphone type (is it a built-in mic, usb mic, headset mic, sth else?)
Does anybody know how can i detect the type?
You can use navigator.mediaDevices.enumerateDevices() to list the user's cameras and microphones, and try to infer types from their labels (there's no mic-type field unfortunately).
The following code works in Firefox 39 and Chrome 45 *:
var stream;
navigator.mediaDevices.getUserMedia({ audio:true })
.then(s => (stream = s), e => console.log(e.message))
.then(() => navigator.mediaDevices.enumerateDevices())
.then(devices => {
stream && stream.stop();
console.log(devices.length + " devices.");
devices.forEach(d => console.log(d.kind + ": " + d.label));
})
.catch(e => console.log(e));
var console = { log: msg => div.innerHTML += msg + "<br>" };
<div id="div"></div>
In Firefox on my system, this produces:
5 devices.
videoinput: Logitech Camera
videoinput: FaceTime HD Camera (Built-in)
audioinput: default (Logitech Camera)
audioinput: Built-in Microphone
audioinput: Logitech Camera
Now, there are some caveats: By spec the labels only show if device access is granted, which is why the snippet asks for it (try it both ways).
Furthermore, Chrome 45 requires persistent permissions (a bug?) which is not available in insecure HTTP, so you may need to reload this question in HTTPS first to see labels. If you do that, don't forget to revoke access in the URL bar afterwards, or Chrome will persist it, which is probably a bad idea on stackoverflow!
Alternatively, try https://webrtc.github.io/samples/src/content/devices/input-output which works in regular Chrome thanks to the adapter.js polyfill, but requires you to grant persistent permission and reload the page before you see labels (because of how it was written).
(*) EDIT: Apparently, enumerateDevices just got put back under an experimental flag in Chrome 45, so you need to enable it as explained here. Sorry about that. Shouldn't be long I hope.
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