mirror of https://github.com/OpenVidu/openvidu.git
openvidu-browser: ask for camera and micro at once (one permission popup)
parent
9d15589126
commit
477d9e282d
|
@ -300,7 +300,7 @@ export class OpenVidu {
|
|||
let errorName: OpenViduErrorName;
|
||||
const errorMessage = error.toString();
|
||||
if (!(options.videoSource === 'screen')) {
|
||||
errorName = (options.videoSource === false || options.videoSource === null) ? OpenViduErrorName.MICROPHONE_ACCESS_DENIED : OpenViduErrorName.CAMERA_ACCESS_DENIED;
|
||||
errorName = OpenViduErrorName.DEVICE_ACCESS_DENIED;
|
||||
} else {
|
||||
errorName = OpenViduErrorName.SCREEN_CAPTURE_DENIED;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,11 @@ export class Publisher extends StreamManager {
|
|||
*/
|
||||
accessAllowed = false;
|
||||
|
||||
/**
|
||||
* Whether you have called [[Publisher.subscribeToRemote]] with value `true` or `false` (false by default)
|
||||
*/
|
||||
isSubscribedToRemote = false;
|
||||
|
||||
/**
|
||||
* The [[Session]] to which the Publisher belongs
|
||||
*/
|
||||
|
@ -84,8 +89,10 @@ export class Publisher extends StreamManager {
|
|||
/**
|
||||
* Call this method before [[Session.publish]] to subscribe to your Publisher's stream as any other user would do. The local video will be automatically replaced by the remote video
|
||||
*/
|
||||
subscribeToRemote(): void {
|
||||
this.stream.subscribeToMyRemote();
|
||||
subscribeToRemote(value?: boolean): void {
|
||||
value = (value !== undefined) ? value : true;
|
||||
this.isSubscribedToRemote = value;
|
||||
this.stream.subscribeToMyRemote(value);
|
||||
}
|
||||
|
||||
|
||||
|
@ -200,6 +207,11 @@ export class Publisher extends StreamManager {
|
|||
}
|
||||
|
||||
this.stream.setMediaStream(mediaStream);
|
||||
if (!this.stream.displayMyRemote()) {
|
||||
// When we are subscribed to our remote we don't still set the MediaStream object in the video elements to
|
||||
// avoid early 'streamPlaying' event
|
||||
this.stream.updateMediaStreamInVideos();
|
||||
}
|
||||
this.stream.isLocalStreamReadyToPublish = true;
|
||||
this.stream.ee.emitEvent('stream-ready-to-publish', []);
|
||||
|
||||
|
@ -221,134 +233,119 @@ export class Publisher extends StreamManager {
|
|||
|
||||
this.stream.setOutboundStreamOptions(outboundStreamOptions);
|
||||
|
||||
// Ask independently for audio stream and video stream. If the user asks for both of them and one is blocked, the method still
|
||||
// success only with the allowed input. This is not the desierd behaviour: if any of them is blocked, access should be denied
|
||||
const constraintsAux: MediaStreamConstraints = {};
|
||||
const timeForDialogEvent = 1250;
|
||||
|
||||
if (this.stream.isSendVideo()) {
|
||||
|
||||
constraintsAux.audio = false;
|
||||
if (this.stream.isSendVideo() || this.stream.isSendAudio()) {
|
||||
const definedAudioConstraint = ((constraints.audio === undefined) ? true : constraints.audio);
|
||||
constraintsAux.audio = this.stream.isSendScreen() ? false : definedAudioConstraint;
|
||||
constraintsAux.video = constraints.video;
|
||||
|
||||
let startTime = Date.now();
|
||||
this.setPermissionDialogTimer(timeForDialogEvent);
|
||||
|
||||
navigator.mediaDevices.getUserMedia(constraintsAux)
|
||||
.then(videoOnlyStream => {
|
||||
.then(mediaStream => {
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
|
||||
if (this.stream.isSendAudio()) {
|
||||
|
||||
constraintsAux.audio = (constraints.audio === undefined) ? true : constraints.audio;
|
||||
if (this.stream.isSendScreen() && this.stream.isSendAudio()) {
|
||||
// When getting desktop as user media audio constraint must be false. Now we can ask for it if required
|
||||
constraintsAux.audio = definedAudioConstraint;
|
||||
constraintsAux.video = false;
|
||||
|
||||
startTime = Date.now();
|
||||
this.setPermissionDialogTimer(timeForDialogEvent);
|
||||
|
||||
navigator.mediaDevices.getUserMedia(constraintsAux)
|
||||
.then(audioOnlyStream => {
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
|
||||
videoOnlyStream.addTrack(audioOnlyStream.getAudioTracks()[0]);
|
||||
successCallback(videoOnlyStream);
|
||||
mediaStream.addTrack(audioOnlyStream.getAudioTracks()[0]);
|
||||
successCallback(mediaStream);
|
||||
})
|
||||
.catch(error => {
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
|
||||
videoOnlyStream.getVideoTracks().forEach((track) => {
|
||||
track.stop();
|
||||
});
|
||||
let errorName;
|
||||
let errorMessage;
|
||||
let errorName, errorMessage;
|
||||
switch (error.name.toLowerCase()) {
|
||||
case 'notfounderror':
|
||||
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
break;
|
||||
case 'notallowederror':
|
||||
errorName = OpenViduErrorName.MICROPHONE_ACCESS_DENIED;
|
||||
errorName = OpenViduErrorName.DEVICE_ACCESS_DENIED;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
break;
|
||||
case 'overconstrainederror':
|
||||
if (error.constraint.toLowerCase() === 'deviceid') {
|
||||
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
|
||||
errorMessage = "Audio input device with deviceId '" + (<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.audio).deviceId!!).exact + "' not found";
|
||||
errorMessage = "Audio input device with deviceId '" + (<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.video).deviceId!!).exact + "' not found";
|
||||
} else {
|
||||
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
|
||||
errorMessage = "Audio input device doesn't support the value passed for constraint '" + error.constraint + "'";
|
||||
}
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
break;
|
||||
}
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
});
|
||||
} else {
|
||||
successCallback(videoOnlyStream);
|
||||
successCallback(mediaStream);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
|
||||
let errorName;
|
||||
let errorMessage;
|
||||
let errorName, errorMessage;
|
||||
switch (error.name.toLowerCase()) {
|
||||
case 'notfounderror':
|
||||
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
|
||||
errorMessage = error.toString();
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: constraints.video
|
||||
})
|
||||
.then(mediaStream => {
|
||||
mediaStream.getVideoTracks().forEach((track) => {
|
||||
track.stop();
|
||||
});
|
||||
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
}).catch(e => {
|
||||
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
});
|
||||
break;
|
||||
case 'notallowederror':
|
||||
errorName = this.stream.isSendScreen() ? OpenViduErrorName.SCREEN_CAPTURE_DENIED : OpenViduErrorName.CAMERA_ACCESS_DENIED;
|
||||
errorName = this.stream.isSendScreen() ? OpenViduErrorName.SCREEN_CAPTURE_DENIED : OpenViduErrorName.DEVICE_ACCESS_DENIED;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
break;
|
||||
case 'overconstrainederror':
|
||||
if (error.constraint.toLowerCase() === 'deviceid') {
|
||||
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
|
||||
errorMessage = "Video input device with deviceId '" + (<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.video).deviceId!!).exact + "' not found";
|
||||
} else {
|
||||
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
|
||||
errorMessage = "Video input device doesn't support the value passed for constraint '" + error.constraint + "'";
|
||||
}
|
||||
}
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
});
|
||||
|
||||
} else if (this.stream.isSendAudio()) {
|
||||
|
||||
constraintsAux.audio = (constraints.audio === undefined) ? true : constraints.audio;
|
||||
constraintsAux.video = false;
|
||||
|
||||
const startTime = Date.now();
|
||||
this.setPermissionDialogTimer(timeForDialogEvent);
|
||||
|
||||
navigator.mediaDevices.getUserMedia(constraints)
|
||||
.then(audioOnlyStream => {
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
|
||||
successCallback(audioOnlyStream);
|
||||
})
|
||||
.catch(error => {
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
|
||||
let errorName;
|
||||
let errorMessage;
|
||||
switch (error.name.toLowerCase()) {
|
||||
case 'notfounderror':
|
||||
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
|
||||
errorMessage = error.toString();
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: constraints.video
|
||||
})
|
||||
.then(mediaStream => {
|
||||
mediaStream.getVideoTracks().forEach((track) => {
|
||||
track.stop();
|
||||
});
|
||||
if (error.constraint.toLowerCase() === 'deviceid') {
|
||||
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
|
||||
errorMessage = "Video input device with deviceId '" + (<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.video).deviceId!!).exact + "' not found";
|
||||
} else {
|
||||
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
|
||||
errorMessage = "Video input device doesn't support the value passed for constraint '" + error.constraint + "'";
|
||||
}
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
}).catch(e => {
|
||||
if (error.constraint.toLowerCase() === 'deviceid') {
|
||||
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
|
||||
errorMessage = "Audio input device with deviceId '" + (<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.video).deviceId!!).exact + "' not found";
|
||||
} else {
|
||||
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
|
||||
errorMessage = "Audio input device doesn't support the value passed for constraint '" + error.constraint + "'";
|
||||
}
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
});
|
||||
break;
|
||||
case 'notallowederror':
|
||||
errorName = OpenViduErrorName.MICROPHONE_ACCESS_DENIED;
|
||||
errorMessage = error.toString();
|
||||
break;
|
||||
case 'overconstrainederror':
|
||||
if (error.constraint.toLowerCase() === 'deviceid') {
|
||||
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
|
||||
errorMessage = "Audio input device with deviceId '" + (<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.audio).deviceId!!).exact + "' not found";
|
||||
} else {
|
||||
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
|
||||
errorMessage = "Audio input device doesn't support the value passed for constraint '" + error.constraint + "'";
|
||||
}
|
||||
}
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
});
|
||||
} else {
|
||||
reject(new OpenViduError(OpenViduErrorName.NO_INPUT_SOURCE_SET,
|
||||
|
|
|
@ -168,6 +168,12 @@ export class Stream {
|
|||
*/
|
||||
setMediaStream(mediaStream: MediaStream): void {
|
||||
this.mediaStream = mediaStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
updateMediaStreamInVideos() {
|
||||
this.ee.emitEvent('mediastream-updated');
|
||||
}
|
||||
|
||||
|
@ -188,8 +194,8 @@ export class Stream {
|
|||
/**
|
||||
* @hidden
|
||||
*/
|
||||
subscribeToMyRemote(): void {
|
||||
this.isSubscribeToRemote = true;
|
||||
subscribeToMyRemote(value: boolean): void {
|
||||
this.isSubscribeToRemote = value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -395,6 +401,11 @@ export class Stream {
|
|||
this.processSdpAnswer(response.sdpAnswer)
|
||||
.then(() => {
|
||||
this.isLocalStreamPublished = true;
|
||||
if (this.displayMyRemote()) {
|
||||
// If remote now we can set the srcObject value of video elements
|
||||
// 'streamPlaying' event will be triggered
|
||||
this.updateMediaStreamInVideos();
|
||||
}
|
||||
this.ee.emitEvent('stream-created-by-publisher');
|
||||
resolve();
|
||||
})
|
||||
|
|
|
@ -20,8 +20,7 @@
|
|||
*/
|
||||
export enum OpenViduErrorName {
|
||||
BROWSER_NOT_SUPPORTED = 'BROWSER_NOT_SUPPORTED',
|
||||
CAMERA_ACCESS_DENIED = 'CAMERA_ACCESS_DENIED',
|
||||
MICROPHONE_ACCESS_DENIED = 'MICROPHONE_ACCESS_DENIED',
|
||||
DEVICE_ACCESS_DENIED = 'DEVICE_ACCESS_DENIED',
|
||||
SCREEN_CAPTURE_DENIED = 'SCREEN_CAPTURE_DENIED',
|
||||
SCREEN_SHARING_NOT_SUPPORTED = 'SCREEN_SHARING_NOT_SUPPORTED',
|
||||
SCREEN_EXTENSION_NOT_INSTALLED = 'SCREEN_EXTENSION_NOT_INSTALLED',
|
||||
|
|
Loading…
Reference in New Issue