mirror of https://github.com/OpenVidu/openvidu.git
openvidu-browser: MediaDevices.getDisplayMedia
parent
84def32f3d
commit
4f3cce732f
|
@ -521,9 +521,9 @@ export class OpenVidu {
|
||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (!!this.advancedConfiguration.screenShareChromeExtension && !(platform.name!.indexOf('Firefox') !== -1)) {
|
if (!!this.advancedConfiguration.screenShareChromeExtension && !(platform.name!.indexOf('Firefox') !== -1) && !navigator.mediaDevices['getDisplayMedia']) {
|
||||||
|
|
||||||
// Custom screen sharing extension for Chrome (and Opera)
|
// Custom screen sharing extension for Chrome (and Opera) and no support for MediaDevices.getDisplayMedia()
|
||||||
|
|
||||||
screenSharing.getScreenConstraints((error, screenConstraints) => {
|
screenSharing.getScreenConstraints((error, screenConstraints) => {
|
||||||
if (!!error || !!screenConstraints.mandatory && screenConstraints.mandatory.chromeMediaSource === 'screen') {
|
if (!!error || !!screenConstraints.mandatory && screenConstraints.mandatory.chromeMediaSource === 'screen') {
|
||||||
|
@ -553,32 +553,36 @@ export class OpenVidu {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Default screen sharing extension for Chrome (or is Firefox)
|
if ((platform.name === 'Chrome' || platform.name === 'Opera') && navigator.mediaDevices['getDisplayMedia']) {
|
||||||
|
// Chrome or Opera with MediaDevices.getDisplayMedia() support
|
||||||
|
resolve(mediaConstraints);
|
||||||
|
} else {
|
||||||
|
// Default screen sharing extension for Chrome/Opera, or is Firefox
|
||||||
|
const firefoxString = platform.name!.indexOf('Firefox') !== -1 ? publisherProperties.videoSource : undefined;
|
||||||
|
|
||||||
const firefoxString = platform.name!.indexOf('Firefox') !== -1 ? publisherProperties.videoSource : undefined;
|
screenSharingAuto.getScreenId(firefoxString, (error, sourceId, screenConstraints) => {
|
||||||
|
if (!!error) {
|
||||||
screenSharingAuto.getScreenId(firefoxString, (error, sourceId, screenConstraints) => {
|
if (error === 'not-installed') {
|
||||||
if (!!error) {
|
const extensionUrl = !!this.advancedConfiguration.screenShareChromeExtension ? this.advancedConfiguration.screenShareChromeExtension :
|
||||||
if (error === 'not-installed') {
|
'https://chrome.google.com/webstore/detail/openvidu-screensharing/lfcgfepafnobdloecchnfaclibenjold';
|
||||||
const extensionUrl = !!this.advancedConfiguration.screenShareChromeExtension ? this.advancedConfiguration.screenShareChromeExtension :
|
const error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_NOT_INSTALLED, extensionUrl);
|
||||||
'https://chrome.google.com/webstore/detail/openvidu-screensharing/lfcgfepafnobdloecchnfaclibenjold';
|
console.error(error);
|
||||||
const error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_NOT_INSTALLED, extensionUrl);
|
reject(error);
|
||||||
console.error(error);
|
} else if (error === 'installed-disabled') {
|
||||||
reject(error);
|
const error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_DISABLED, 'You must enable the screen extension');
|
||||||
} else if (error === 'installed-disabled') {
|
console.error(error);
|
||||||
const error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_DISABLED, 'You must enable the screen extension');
|
reject(error);
|
||||||
console.error(error);
|
} else if (error === 'permission-denied') {
|
||||||
reject(error);
|
const error = new OpenViduError(OpenViduErrorName.SCREEN_CAPTURE_DENIED, 'You must allow access to one window of your desktop');
|
||||||
} else if (error === 'permission-denied') {
|
console.error(error);
|
||||||
const error = new OpenViduError(OpenViduErrorName.SCREEN_CAPTURE_DENIED, 'You must allow access to one window of your desktop');
|
reject(error);
|
||||||
console.error(error);
|
}
|
||||||
reject(error);
|
} else {
|
||||||
|
mediaConstraints.video = screenConstraints.video;
|
||||||
|
resolve(mediaConstraints);
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
mediaConstraints.video = screenConstraints.video;
|
}
|
||||||
resolve(mediaConstraints);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
publisherProperties.videoSource = 'screen';
|
publisherProperties.videoSource = 'screen';
|
||||||
|
|
|
@ -269,6 +269,11 @@ export class Publisher extends StreamManager {
|
||||||
initialize(): Promise<any> {
|
initialize(): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
let constraints: MediaStreamConstraints = {};
|
||||||
|
let constraintsAux: MediaStreamConstraints = {};
|
||||||
|
const timeForDialogEvent = 1250;
|
||||||
|
let startTime;
|
||||||
|
|
||||||
const errorCallback = (openViduError: OpenViduError) => {
|
const errorCallback = (openViduError: OpenViduError) => {
|
||||||
this.accessDenied = true;
|
this.accessDenied = true;
|
||||||
this.accessAllowed = false;
|
this.accessAllowed = false;
|
||||||
|
@ -421,6 +426,56 @@ export class Publisher extends StreamManager {
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const afterGetMedia = (mediaStream: MediaStream, definedAudioConstraint) => {
|
||||||
|
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||||
|
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);
|
||||||
|
mediaStream.addTrack(audioOnlyStream.getAudioTracks()[0]);
|
||||||
|
successCallback(mediaStream);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||||
|
if (error.name === 'Error') {
|
||||||
|
// Safari OverConstrainedError has as name property 'Error' instead of 'OverConstrainedError'
|
||||||
|
error.name = error.constructor.name;
|
||||||
|
}
|
||||||
|
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.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.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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
successCallback(mediaStream);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Check if new constraints need to be generated. No constraints needed if
|
// Check if new constraints need to be generated. No constraints needed if
|
||||||
// - video track is given and no audio
|
// - video track is given and no audio
|
||||||
// - audio track is given and no video
|
// - audio track is given and no video
|
||||||
|
@ -442,7 +497,9 @@ export class Publisher extends StreamManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.openvidu.generateMediaConstraints(this.properties)
|
this.openvidu.generateMediaConstraints(this.properties)
|
||||||
.then(constraints => {
|
.then(myConstraints => {
|
||||||
|
|
||||||
|
constraints = myConstraints;
|
||||||
|
|
||||||
const outboundStreamOptions = {
|
const outboundStreamOptions = {
|
||||||
mediaConstraints: constraints,
|
mediaConstraints: constraints,
|
||||||
|
@ -451,139 +508,97 @@ export class Publisher extends StreamManager {
|
||||||
|
|
||||||
this.stream.setOutboundStreamOptions(outboundStreamOptions);
|
this.stream.setOutboundStreamOptions(outboundStreamOptions);
|
||||||
|
|
||||||
const constraintsAux: MediaStreamConstraints = {};
|
|
||||||
const timeForDialogEvent = 1250;
|
|
||||||
|
|
||||||
if (this.stream.isSendVideo() || this.stream.isSendAudio()) {
|
if (this.stream.isSendVideo() || this.stream.isSendAudio()) {
|
||||||
const definedAudioConstraint = ((constraints.audio === undefined) ? true : constraints.audio);
|
const definedAudioConstraint = ((constraints.audio === undefined) ? true : constraints.audio);
|
||||||
constraintsAux.audio = this.stream.isSendScreen() ? false : definedAudioConstraint;
|
constraintsAux.audio = this.stream.isSendScreen() ? false : definedAudioConstraint;
|
||||||
constraintsAux.video = constraints.video;
|
constraintsAux.video = constraints.video;
|
||||||
let startTime = Date.now();
|
startTime = Date.now();
|
||||||
this.setPermissionDialogTimer(timeForDialogEvent);
|
this.setPermissionDialogTimer(timeForDialogEvent);
|
||||||
|
|
||||||
navigator.mediaDevices.getUserMedia(constraintsAux)
|
if (this.stream.isSendScreen() && navigator.mediaDevices['getDisplayMedia']) {
|
||||||
.then(mediaStream => {
|
navigator.mediaDevices['getDisplayMedia']({ video: true })
|
||||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
.then(mediaStream => {
|
||||||
|
afterGetMedia(mediaStream, definedAudioConstraint);
|
||||||
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
|
} else {
|
||||||
constraintsAux.audio = definedAudioConstraint;
|
navigator.mediaDevices.getUserMedia(constraintsAux)
|
||||||
constraintsAux.video = false;
|
.then(mediaStream => {
|
||||||
startTime = Date.now();
|
afterGetMedia(mediaStream, definedAudioConstraint);
|
||||||
this.setPermissionDialogTimer(timeForDialogEvent);
|
})
|
||||||
|
.catch(error => {
|
||||||
navigator.mediaDevices.getUserMedia(constraintsAux)
|
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||||
.then(audioOnlyStream => {
|
if (error.name === 'Error') {
|
||||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
// Safari OverConstrainedError has as name property 'Error' instead of 'OverConstrainedError'
|
||||||
mediaStream.addTrack(audioOnlyStream.getAudioTracks()[0]);
|
error.name = error.constructor.name;
|
||||||
successCallback(mediaStream);
|
}
|
||||||
})
|
let errorName, errorMessage;
|
||||||
.catch(error => {
|
switch (error.name.toLowerCase()) {
|
||||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
case 'notfounderror':
|
||||||
if (error.name === 'Error') {
|
navigator.mediaDevices.getUserMedia({
|
||||||
// Safari OverConstrainedError has as name property 'Error' instead of 'OverConstrainedError'
|
audio: false,
|
||||||
error.name = error.constructor.name;
|
video: constraints.video
|
||||||
}
|
})
|
||||||
let errorName, errorMessage;
|
.then(mediaStream => {
|
||||||
switch (error.name.toLowerCase()) {
|
mediaStream.getVideoTracks().forEach((track) => {
|
||||||
case 'notfounderror':
|
track.stop();
|
||||||
|
});
|
||||||
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));
|
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||||
break;
|
}).catch(e => {
|
||||||
case 'notallowederror':
|
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
|
||||||
errorName = OpenViduErrorName.DEVICE_ACCESS_DENIED;
|
|
||||||
errorMessage = error.toString();
|
errorMessage = error.toString();
|
||||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||||
break;
|
});
|
||||||
case 'overconstrainederror':
|
break;
|
||||||
|
case 'notallowederror':
|
||||||
|
errorName = this.stream.isSendScreen() ? OpenViduErrorName.SCREEN_CAPTURE_DENIED : OpenViduErrorName.DEVICE_ACCESS_DENIED;
|
||||||
|
errorMessage = error.toString();
|
||||||
|
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||||
|
break;
|
||||||
|
case 'overconstrainederror':
|
||||||
|
navigator.mediaDevices.getUserMedia({
|
||||||
|
audio: false,
|
||||||
|
video: constraints.video
|
||||||
|
})
|
||||||
|
.then(mediaStream => {
|
||||||
|
mediaStream.getVideoTracks().forEach((track) => {
|
||||||
|
track.stop();
|
||||||
|
});
|
||||||
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.video).deviceId!!).exact + "' not found";
|
errorMessage = "Audio input device with deviceId '" + (<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.audio).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));
|
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||||
break;
|
}).catch(e => {
|
||||||
}
|
if (error.constraint.toLowerCase() === 'deviceid') {
|
||||||
});
|
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
|
||||||
} else {
|
errorMessage = "Video input device with deviceId '" + (<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.video).deviceId!!).exact + "' not found";
|
||||||
successCallback(mediaStream);
|
} else {
|
||||||
}
|
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
|
||||||
})
|
errorMessage = "Video input device doesn't support the value passed for constraint '" + error.constraint + "'";
|
||||||
.catch(error => {
|
}
|
||||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||||
if (error.name === 'Error') {
|
|
||||||
// Safari OverConstrainedError has as name property 'Error' instead of 'OverConstrainedError'
|
|
||||||
error.name = error.constructor.name;
|
|
||||||
}
|
|
||||||
let errorName, errorMessage;
|
|
||||||
switch (error.name.toLowerCase()) {
|
|
||||||
case 'notfounderror':
|
|
||||||
navigator.mediaDevices.getUserMedia({
|
|
||||||
audio: false,
|
|
||||||
video: constraints.video
|
|
||||||
})
|
|
||||||
.then(mediaStream => {
|
|
||||||
mediaStream.getVideoTracks().forEach((track) => {
|
|
||||||
track.stop();
|
|
||||||
});
|
});
|
||||||
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
|
break;
|
||||||
errorMessage = error.toString();
|
case 'aborterror':
|
||||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
case 'notreadableerror':
|
||||||
}).catch(e => {
|
errorName = OpenViduErrorName.DEVICE_ALREADY_IN_USE;
|
||||||
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
|
errorMessage = error.toString();
|
||||||
errorMessage = error.toString();
|
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
break;
|
||||||
});
|
default:
|
||||||
break;
|
errorName = OpenViduErrorName.GENERIC_ERROR;
|
||||||
case 'notallowederror':
|
errorMessage = error.toString();
|
||||||
errorName = this.stream.isSendScreen() ? OpenViduErrorName.SCREEN_CAPTURE_DENIED : OpenViduErrorName.DEVICE_ACCESS_DENIED;
|
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||||
errorMessage = error.toString();
|
break;
|
||||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
|
||||||
break;
|
}
|
||||||
case 'overconstrainederror':
|
});
|
||||||
navigator.mediaDevices.getUserMedia({
|
}
|
||||||
audio: false,
|
|
||||||
video: constraints.video
|
|
||||||
})
|
|
||||||
.then(mediaStream => {
|
|
||||||
mediaStream.getVideoTracks().forEach((track) => {
|
|
||||||
track.stop();
|
|
||||||
});
|
|
||||||
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));
|
|
||||||
}).catch(e => {
|
|
||||||
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));
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'aborterror':
|
|
||||||
case 'notreadableerror':
|
|
||||||
errorName = OpenViduErrorName.DEVICE_ALREADY_IN_USE;
|
|
||||||
errorMessage = error.toString();
|
|
||||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errorName = OpenViduErrorName.GENERIC_ERROR;
|
|
||||||
errorMessage = error.toString();
|
|
||||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
reject(new OpenViduError(OpenViduErrorName.NO_INPUT_SOURCE_SET,
|
reject(new OpenViduError(OpenViduErrorName.NO_INPUT_SOURCE_SET,
|
||||||
"Properties 'audioSource' and 'videoSource' cannot be set to false or null at the same time when calling 'OpenVidu.initPublisher'"));
|
"Properties 'audioSource' and 'videoSource' cannot be set to false or null at the same time when calling 'OpenVidu.initPublisher'"));
|
||||||
|
|
Loading…
Reference in New Issue