getUserMedia differentiating between what hardware is erroring - permissions

I am running a getUserMedia for camera and microphone,
navigator.mediaDevices
.getUserMedia({audio:true, video: true)
.then((stream) => {})
.catch((error) => {})
Is there a way to differentiate what device is causing the promise to fail? i.e if its the camera that is unreadable or the mic, are you able to find it's the camera from the error object? I can find anything other than error.name and error.message?

No, unfortunately when you capture from both at the same time, they either both succeed for both fail together.
Many applications will capture the audio and video separately, and then create a new MediaStream with the tracks from the MediaStreams from each separate device. I have a hunch that this can lead to synchronization problems in the cases where the audio/video are sent a single stream from the device internally, but haven't proven this. It must not be a significant problem, at least for video conferencing, as this is what Google does for Hangouts/Meet.

What sorts of errors do you want to detect?
If your machine doesn't have the hardware (camera, mic) it needs to make your MediaStream, you can find this out by using .enumerateDevices() before you try to use .getUserMedia(). A function like this might give you the information you need for an {audio: true, video:true} MediaStream.
async function canIDoIt () {
const devices = await navigator.mediaDevices.enumerateDevices()
let hasAudio = false
let hasVideo = false
devices.forEach(function(device) {
if (device.kind === 'videoinput') hasVideo = true
if (device.kind === 'audioinput') hasAudio = true
})
return hasAudio && hasVideo
}
Using that is a good start for a robust media app: you can tell your user they don't have the correct hardware right away before diving into to the arcana of the errors thrown by .getUserMedia().
If your user denies permission to .getUserMedia() access their media devices, it will throw an error with error.message containing the string "permission denied". Keep in mind that when the user denies permission, your program doesn't get back much descriptive information about the devices. Because cybercreeps.
If your user's devices cannot handle the constraints you present to .getUserMedia(), you'll get "constraint violation" in the error.message string. The kinds of device constraints you can violate are things like
{video: { width:{exact:1920},
height:{exact:1080}}}
Avoiding exact in your constraints reduces the chance of constraint violations. Instead you can give something like this.
{video: { width:{min:480, ideal: 720, max: 1920},
height:{min:270, ideal:1280, max: 1040}}}
Other errors are probably more specific to a particular machine and browser. In practically all cases the thrown error object contains an explanatory error.message.

Related

TokBox/Vonage allowing audio capture support when screensharing

Screen Capture API, specifically getDisplayMedia(), currently supports screensharing and sharing the audio playing in your device (e.g: youtube) at the same time. Docs. Is this currently supported using TokBox/Vonage Video API? Has someone been able to achieve this?
I guess there could be some workaround using getDisplayMedia and passing the audio source when publishing, e.g: OT.initPublisher({ audioSource: newDisplayMediaAudioTrack }), but doesn't seem like a clean solution.
Thanks,
Manik here from the Vonage Client SDK team.
Although this feature does not exist in the Video Client SDK just yet, you can accomplish the sharing of audio with screen by creating a publisher like so:
let publisher;
try {
const stream = await navigator.mediaDevices.getDisplayMedia({video: true, audio: true });
const audioTrack = stream.getAudioTracks()[0];
const videoTrack = stream.getVideoTracks()[0];
publisher = OT.initPublisher({audioSource: audioTrack, videoSource: videoTrack});
} catch (e) {
// handle error
}
If you share a tab, but the tab doesn't play audio (static pdf or ppt) then the screen flickers. To avoid this, specify frameRate constraint for the video stream. see - https://gist.github.com/rktalusani/ca854ca8621c20488bea6e62ad04e341

Disable synchronization before launchApp

Is it possible to disable synchronization before/during launchApp (with newInstance: true)? Ideally I'd like the:
await device.launchApp({ newInstance: true, url });
to resolve immediately.
I've inherited an app that does weird things at launch, so I'd like to bypass synchronization at the beginning and only reenable it afterwards.
I tried something like this:
await device.disableSynchronization();
await device.launchApp({ newInstance: true, url });
await waitFor(element(by.id('root'))).toBeVisible().withTimeout(10000);
await device.enableSynchronization();
but from the docs I read that synchronization is always re-enabled for new instances.
Is there a way to force synchronization to be off so that device.launchApp can actually resolve?
This is now possible using the launch argument -detoxEnableSynchronization NO.
See the documentation here:
https://github.com/wix/Detox/blob/master/docs/APIRef.DeviceObjectAPI.md#10-detoxenablesynchronizationinitialize-detox-with-synchronization-enabled-or-disabled-at-app-launch
Old answer:
Detox does not support disabling synchronization on launch, but if a network request is causing issues, you can pass a URL blacklist as a launch argument, which will disable synchronization for that network request.
await device.launchApp({
newInstance: true,
launchArgs: { detoxURLBlacklistRegex: ' \\("http://192.168.1.253:19001/onchange","https://e.crashlytics.com/spi/v2/events"\\)' },
});
https://github.com/wix/Detox/blob/master/docs/APIRef.DeviceObjectAPI.md#10-initialize-the-url-blacklist-at-device-launch

pouchdb + vuex update live with changes

I have an app that takes updates into VUEX store and syncs those change from pouchdb to couchdb. Which is great but now I need to have two clients connected and see the change in near realtime.
So I have the https://pouchdb.com/guides/changes.html API which I can use to listen for changes to the DB and when that happens call a action which mutates the vuex state on Client 2. Code below.
However the bit I cannot seem to work out is this code is not just listening all the time ? So where should I put this in Vue to ensure that it hears any changes. I can call it when I make a state change and I see that it hears the change but ofcourse I want to trigger a state change on client 2, without them having to make change. Do I need a timer ? The pouch docs seem to suggest this changes api should be able to update UI based on a change to the data, which I can probably call with a button press to check for changes ...but I want to listen in near realtime ?
pouchdb
.changes({
since: 'now',
include_docs: true
})
.on('change', function(change) {
// received a change
store.commit('CHANGE_STATE', change.doc.flavour)
})
.on('error', function(err) {
// handle errors
console.log(err)
})
Your explanation is a bit fuzzy in that you talk about client 2 without ever mentioning client 1. I assume client 2 is a passive listener and client 1 is where data is changed. If I remember correctly from when I was building my Vue / PouchDB project last year I looked into how to coordinate the Store and the Database, and then thought, "Why bother? They're just two kinds of local storage". As long as changes in client 1 replicate to your Couch server and client 2 detects those server side changes and writes them into reactive variables, they'll propagate to the UI.
I used replicate.to() for storing client-side changes and replicate.from() to detect server-side changes. The replicate() functions have their own timer, constantly monitoring the changes queue, so you don't need to roll your own.
This is what I ended up doing !
actions: {
SYNC_DB() {
// do one way, one-off sync from the server until completion
pouchdb.replicate.from(remote).on('complete', function(info) {
// then two-way, continuous, retriable sync
pouchdb
.sync(remote, { live: true, retry: true })
.on('change', function(info) {
store.commit('CHANGE_STATE', info.change.docs[0].flavour)
})
.on('paused', function(err) {
// replication paused (e.g. replication up to date, user went offline)
})
.on('active', function() {
// replicate resumed (e.g. new changes replicating, user went back online)
})
.on('denied', function(err) {
// a document failed to replicate (e.g. due to permissions)
})
.on('complete', function(info) {
// handle complete
})
.on('error', function(err) {
// handle error
})
})
},

Best Practice: handlng no network in a ReactNative / Redux App

I've built a ReactNative app that uses redux, as well as Wix's react-native-navigation library (HIGHLY recommend btw), and am trying to improve the user experience when network connection is lost - really just a simple alert that lets the user know.
I've got all of my fetch() calls in one api.js lib file, and what I would love to do, is wrap each request with a function that checks for connection before fetching (as opposed to doing some sort of per-component implementation I've seen in other SO suggestions).
I've read a bunch about facebook's NetInfo (https://facebook.github.io/react-native/docs/netinfo.html) API, however there are still open issues on it w/ regards to the simple NetInfo.isConnected.fetch().then((isConnected) => {} use case (it always returns false, per the issues open).
I've tried something like this inside the fetch function:
static get(route, params, header) {
return NetInfo.isConnected.fetch().then((isConnected) => {
if ( isConnected )
{
// Run / return your API call
}
else
{
Alert.alert('No Internet Connection', 'Please connect and retry.',[{text: 'OK'}],{ cancelable: false });
}
});
}
I'm wondering if anyone has either successfully utilized the NetInfo api in this way, or if you've come across a better solution for this (likely) common problem.

MediaStream reference not removed / why does my webcam stay busy?

Background / Issue
Using navigator.mediaDevices.getUserMedia(constraints) I can obtain a MediaStream object for various devices, amongst them webcam and microphone, allowing you to do whatever you want with the data that comes through.
The method getUserMedia returns a Promise which resolve to a media stream or rejects if there is no stream available for the given constraints (video, audio etc.) If I do obtain a stream object BUT don't save any reference to the MediaStream - I understand that the garbage collector should remove it.
What I've observed is that the stream is not removed - if I obtain a stream for the webcam for example, it keeps being busy even though I have no reference left to the stream.
Questions
Where is the MediaStream object stored if I don't save a reference to it?
Why is it not removed by the garbage collector?
Why does my webcam stay busy?
The MediaStream API requires you to stop each track contained in the MediaStream instance that you obtained. Until you do so, the media capture will keep going.
navigator.mediaDevices
.getUserMedia({
audio: true,
video: true
})
.then(function (stream) {
console.log('got stream with id ' + stream.id)
stream.getTracks().forEach(function (track) { track.stop() })
// WebCam will not be busy anymore
})
.catch(function (reason) {
console.error('capture failed ' + reason)
})