openvidu-browser: ask for camera and micro at once (one permission popup)

pull/73/head
pabloFuente 2018-05-30 12:24:18 +02:00
parent 9d15589126
commit 477d9e282d
4 changed files with 91 additions and 84 deletions

View File

@ -300,7 +300,7 @@ export class OpenVidu {
let errorName: OpenViduErrorName; let errorName: OpenViduErrorName;
const errorMessage = error.toString(); const errorMessage = error.toString();
if (!(options.videoSource === 'screen')) { 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 { } else {
errorName = OpenViduErrorName.SCREEN_CAPTURE_DENIED; errorName = OpenViduErrorName.SCREEN_CAPTURE_DENIED;
} }

View File

@ -40,6 +40,11 @@ export class Publisher extends StreamManager {
*/ */
accessAllowed = false; 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 * 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 * 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 { subscribeToRemote(value?: boolean): void {
this.stream.subscribeToMyRemote(); 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); 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.isLocalStreamReadyToPublish = true;
this.stream.ee.emitEvent('stream-ready-to-publish', []); this.stream.ee.emitEvent('stream-ready-to-publish', []);
@ -221,134 +233,119 @@ export class Publisher extends StreamManager {
this.stream.setOutboundStreamOptions(outboundStreamOptions); 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 constraintsAux: MediaStreamConstraints = {};
const timeForDialogEvent = 1250; const timeForDialogEvent = 1250;
if (this.stream.isSendVideo()) { if (this.stream.isSendVideo() || this.stream.isSendAudio()) {
const definedAudioConstraint = ((constraints.audio === undefined) ? true : constraints.audio);
constraintsAux.audio = false; constraintsAux.audio = this.stream.isSendScreen() ? false : definedAudioConstraint;
constraintsAux.video = constraints.video; constraintsAux.video = constraints.video;
let startTime = Date.now(); let startTime = Date.now();
this.setPermissionDialogTimer(timeForDialogEvent); this.setPermissionDialogTimer(timeForDialogEvent);
navigator.mediaDevices.getUserMedia(constraintsAux) navigator.mediaDevices.getUserMedia(constraintsAux)
.then(videoOnlyStream => { .then(mediaStream => {
this.clearPermissionDialogTimer(startTime, timeForDialogEvent); this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
if (this.stream.isSendAudio()) { 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 = (constraints.audio === undefined) ? true : constraints.audio; constraintsAux.audio = definedAudioConstraint;
constraintsAux.video = false; constraintsAux.video = false;
startTime = Date.now(); startTime = Date.now();
this.setPermissionDialogTimer(timeForDialogEvent); this.setPermissionDialogTimer(timeForDialogEvent);
navigator.mediaDevices.getUserMedia(constraintsAux) navigator.mediaDevices.getUserMedia(constraintsAux)
.then(audioOnlyStream => { .then(audioOnlyStream => {
this.clearPermissionDialogTimer(startTime, timeForDialogEvent); this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
mediaStream.addTrack(audioOnlyStream.getAudioTracks()[0]);
videoOnlyStream.addTrack(audioOnlyStream.getAudioTracks()[0]); successCallback(mediaStream);
successCallback(videoOnlyStream);
}) })
.catch(error => { .catch(error => {
this.clearPermissionDialogTimer(startTime, timeForDialogEvent); this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
let errorName, errorMessage;
videoOnlyStream.getVideoTracks().forEach((track) => {
track.stop();
});
let errorName;
let errorMessage;
switch (error.name.toLowerCase()) { switch (error.name.toLowerCase()) {
case 'notfounderror': case 'notfounderror':
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND; errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
errorMessage = error.toString(); errorMessage = error.toString();
errorCallback(new OpenViduError(errorName, errorMessage));
break; break;
case 'notallowederror': case 'notallowederror':
errorName = OpenViduErrorName.MICROPHONE_ACCESS_DENIED; errorName = OpenViduErrorName.DEVICE_ACCESS_DENIED;
errorMessage = error.toString(); errorMessage = error.toString();
errorCallback(new OpenViduError(errorName, errorMessage));
break; break;
case 'overconstrainederror': case 'overconstrainederror':
if (error.constraint.toLowerCase() === 'deviceid') { if (error.constraint.toLowerCase() === 'deviceid') {
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND; 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 { } else {
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR; errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
errorMessage = "Audio input device doesn't support the value passed for constraint '" + error.constraint + "'"; 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 { } else {
successCallback(videoOnlyStream); successCallback(mediaStream);
} }
}) })
.catch(error => { .catch(error => {
this.clearPermissionDialogTimer(startTime, timeForDialogEvent); this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
let errorName, errorMessage;
let errorName;
let errorMessage;
switch (error.name.toLowerCase()) { switch (error.name.toLowerCase()) {
case 'notfounderror': case 'notfounderror':
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND; navigator.mediaDevices.getUserMedia({
errorMessage = error.toString(); 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; break;
case 'notallowederror': 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(); errorMessage = error.toString();
errorCallback(new OpenViduError(errorName, errorMessage));
break; break;
case 'overconstrainederror': case 'overconstrainederror':
if (error.constraint.toLowerCase() === 'deviceid') { navigator.mediaDevices.getUserMedia({
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND; audio: false,
errorMessage = "Video input device with deviceId '" + (<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.video).deviceId!!).exact + "' not found"; video: constraints.video
} else { })
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR; .then(mediaStream => {
errorMessage = "Video input device doesn't support the value passed for constraint '" + error.constraint + "'"; mediaStream.getVideoTracks().forEach((track) => {
} track.stop();
} });
errorCallback(new OpenViduError(errorName, errorMessage)); 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 if (this.stream.isSendAudio()) { } else {
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
constraintsAux.audio = (constraints.audio === undefined) ? true : constraints.audio; errorMessage = "Video input device doesn't support the value passed for constraint '" + error.constraint + "'";
constraintsAux.video = false; }
errorCallback(new OpenViduError(errorName, errorMessage));
const startTime = Date.now(); }).catch(e => {
this.setPermissionDialogTimer(timeForDialogEvent); if (error.constraint.toLowerCase() === 'deviceid') {
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
navigator.mediaDevices.getUserMedia(constraints) errorMessage = "Audio input device with deviceId '" + (<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.video).deviceId!!).exact + "' not found";
.then(audioOnlyStream => { } else {
this.clearPermissionDialogTimer(startTime, timeForDialogEvent); errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
errorMessage = "Audio input device doesn't support the value passed for constraint '" + error.constraint + "'";
successCallback(audioOnlyStream); }
}) errorCallback(new OpenViduError(errorName, errorMessage));
.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();
break; 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 { } else {
reject(new OpenViduError(OpenViduErrorName.NO_INPUT_SOURCE_SET, reject(new OpenViduError(OpenViduErrorName.NO_INPUT_SOURCE_SET,

View File

@ -168,6 +168,12 @@ export class Stream {
*/ */
setMediaStream(mediaStream: MediaStream): void { setMediaStream(mediaStream: MediaStream): void {
this.mediaStream = mediaStream; this.mediaStream = mediaStream;
}
/**
* @hidden
*/
updateMediaStreamInVideos() {
this.ee.emitEvent('mediastream-updated'); this.ee.emitEvent('mediastream-updated');
} }
@ -188,8 +194,8 @@ export class Stream {
/** /**
* @hidden * @hidden
*/ */
subscribeToMyRemote(): void { subscribeToMyRemote(value: boolean): void {
this.isSubscribeToRemote = true; this.isSubscribeToRemote = value;
} }
/** /**
@ -395,6 +401,11 @@ export class Stream {
this.processSdpAnswer(response.sdpAnswer) this.processSdpAnswer(response.sdpAnswer)
.then(() => { .then(() => {
this.isLocalStreamPublished = true; 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'); this.ee.emitEvent('stream-created-by-publisher');
resolve(); resolve();
}) })

View File

@ -20,8 +20,7 @@
*/ */
export enum OpenViduErrorName { export enum OpenViduErrorName {
BROWSER_NOT_SUPPORTED = 'BROWSER_NOT_SUPPORTED', BROWSER_NOT_SUPPORTED = 'BROWSER_NOT_SUPPORTED',
CAMERA_ACCESS_DENIED = 'CAMERA_ACCESS_DENIED', DEVICE_ACCESS_DENIED = 'DEVICE_ACCESS_DENIED',
MICROPHONE_ACCESS_DENIED = 'MICROPHONE_ACCESS_DENIED',
SCREEN_CAPTURE_DENIED = 'SCREEN_CAPTURE_DENIED', SCREEN_CAPTURE_DENIED = 'SCREEN_CAPTURE_DENIED',
SCREEN_SHARING_NOT_SUPPORTED = 'SCREEN_SHARING_NOT_SUPPORTED', SCREEN_SHARING_NOT_SUPPORTED = 'SCREEN_SHARING_NOT_SUPPORTED',
SCREEN_EXTENSION_NOT_INSTALLED = 'SCREEN_EXTENSION_NOT_INSTALLED', SCREEN_EXTENSION_NOT_INSTALLED = 'SCREEN_EXTENSION_NOT_INSTALLED',