openvidu-browser: MediaDevices.getDisplayMedia

pull/255/head
pabloFuente 2019-02-25 11:25:53 +01:00
parent 84def32f3d
commit 4f3cce732f
2 changed files with 160 additions and 141 deletions

View File

@ -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';

View File

@ -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'"));