mirror of https://github.com/OpenVidu/openvidu.git
openvidu-browser: OpenVidu.getUserMedia fix. generateMediaConstraints refactoring
parent
26750e6167
commit
df4db147f5
|
@ -23,6 +23,7 @@ import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPro
|
||||||
import { Device } from '../OpenViduInternal/Interfaces/Public/Device';
|
import { Device } from '../OpenViduInternal/Interfaces/Public/Device';
|
||||||
import { OpenViduAdvancedConfiguration } from '../OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration';
|
import { OpenViduAdvancedConfiguration } from '../OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration';
|
||||||
import { PublisherProperties } from '../OpenViduInternal/Interfaces/Public/PublisherProperties';
|
import { PublisherProperties } from '../OpenViduInternal/Interfaces/Public/PublisherProperties';
|
||||||
|
import { CustomMediaStreamConstraints } from '../OpenViduInternal/Interfaces/Private/CustomMediaStreamConstraints';
|
||||||
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
|
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
|
||||||
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
|
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
|
||||||
|
|
||||||
|
@ -551,20 +552,41 @@ export class OpenVidu {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.generateMediaConstraints(options)
|
this.generateMediaConstraints(options).then(myConstraints => {
|
||||||
.then(constraints => {
|
|
||||||
|
if (!!myConstraints.videoTrack && !!myConstraints.audioTrack ||
|
||||||
|
!!myConstraints.audioTrack && myConstraints.constraints?.video === false ||
|
||||||
|
!!myConstraints.videoTrack && myConstraints.constraints?.audio === false) {
|
||||||
|
|
||||||
|
// No need to call getUserMedia at all. Both tracks provided, or only AUDIO track provided or only VIDEO track provided
|
||||||
|
resolve(this.addAlreadyProvidedTracks(myConstraints, new MediaStream()));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// getUserMedia must be called. AUDIO or VIDEO are requesting a new track
|
||||||
|
|
||||||
|
// Delete already provided constraints for audio or video
|
||||||
|
if (!!myConstraints.videoTrack) {
|
||||||
|
delete myConstraints.constraints!.video;
|
||||||
|
}
|
||||||
|
if (!!myConstraints.audioTrack) {
|
||||||
|
delete myConstraints.constraints!.audio;
|
||||||
|
}
|
||||||
|
|
||||||
let mustAskForAudioTrackLater = false;
|
let mustAskForAudioTrackLater = false;
|
||||||
if (typeof options.videoSource === 'string') {
|
if (typeof options.videoSource === 'string') {
|
||||||
|
// Video is deviceId or screen sharing
|
||||||
if (options.videoSource === 'screen' ||
|
if (options.videoSource === 'screen' ||
|
||||||
options.videoSource === 'window' ||
|
options.videoSource === 'window' ||
|
||||||
(platform.name === 'Electron' && options.videoSource.startsWith('screen:'))) {
|
(platform.name === 'Electron' && options.videoSource.startsWith('screen:'))) {
|
||||||
// Screen sharing
|
// Video is screen sharing
|
||||||
mustAskForAudioTrackLater = options.audioSource !== null && options.audioSource !== false;
|
mustAskForAudioTrackLater = !myConstraints.audioTrack && (options.audioSource !== null && options.audioSource !== false);
|
||||||
if (navigator.mediaDevices['getDisplayMedia'] && platform.name !== 'Electron') {
|
if (navigator.mediaDevices['getDisplayMedia'] && platform.name !== 'Electron') {
|
||||||
|
// getDisplayMedia supported
|
||||||
navigator.mediaDevices['getDisplayMedia']({ video: true })
|
navigator.mediaDevices['getDisplayMedia']({ video: true })
|
||||||
.then(mediaStream => {
|
.then(mediaStream => {
|
||||||
|
this.addAlreadyProvidedTracks(myConstraints, mediaStream);
|
||||||
if (mustAskForAudioTrackLater) {
|
if (mustAskForAudioTrackLater) {
|
||||||
askForAudioStreamOnly(mediaStream, constraints);
|
askForAudioStreamOnly(mediaStream, <MediaStreamConstraints>myConstraints.constraints);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
resolve(mediaStream);
|
resolve(mediaStream);
|
||||||
|
@ -575,15 +597,21 @@ export class OpenVidu {
|
||||||
const errorMessage = error.toString();
|
const errorMessage = error.toString();
|
||||||
reject(new OpenViduError(errorName, errorMessage));
|
reject(new OpenViduError(errorName, errorMessage));
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// getDisplayMedia NOT supported. Can perform getUserMedia below with already calculated constraints
|
||||||
}
|
}
|
||||||
return;
|
} else {
|
||||||
|
// Video is deviceId. Can perform getUserMedia below with already calculated constraints
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const constraintsAux = mustAskForAudioTrackLater ? { video: constraints.video } : constraints;
|
// Use already calculated constraints
|
||||||
|
const constraintsAux = mustAskForAudioTrackLater ? { video: myConstraints.constraints!.video } : myConstraints.constraints;
|
||||||
navigator.mediaDevices.getUserMedia(constraintsAux)
|
navigator.mediaDevices.getUserMedia(constraintsAux)
|
||||||
.then(mediaStream => {
|
.then(mediaStream => {
|
||||||
|
this.addAlreadyProvidedTracks(myConstraints, mediaStream);
|
||||||
if (mustAskForAudioTrackLater) {
|
if (mustAskForAudioTrackLater) {
|
||||||
askForAudioStreamOnly(mediaStream, constraints);
|
askForAudioStreamOnly(mediaStream, <MediaStreamConstraints>myConstraints.constraints);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
resolve(mediaStream);
|
resolve(mediaStream);
|
||||||
|
@ -599,10 +627,10 @@ export class OpenVidu {
|
||||||
}
|
}
|
||||||
reject(new OpenViduError(errorName, errorMessage));
|
reject(new OpenViduError(errorName, errorMessage));
|
||||||
});
|
});
|
||||||
})
|
}
|
||||||
.catch((error: OpenViduError) => {
|
}).catch((error: OpenViduError) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -638,168 +666,192 @@ export class OpenVidu {
|
||||||
/**
|
/**
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
generateMediaConstraints(publisherProperties: PublisherProperties): Promise<MediaStreamConstraints> {
|
generateMediaConstraints(publisherProperties: PublisherProperties): Promise<CustomMediaStreamConstraints> {
|
||||||
return new Promise<MediaStreamConstraints>((resolve, reject) => {
|
return new Promise<CustomMediaStreamConstraints>((resolve, reject) => {
|
||||||
let audio, video;
|
|
||||||
|
|
||||||
if (publisherProperties.audioSource === null || publisherProperties.audioSource === false) {
|
const myConstraints: CustomMediaStreamConstraints = {
|
||||||
audio = false;
|
audioTrack: undefined,
|
||||||
} else if (publisherProperties.audioSource === undefined) {
|
videoTrack: undefined,
|
||||||
audio = true;
|
constraints: {
|
||||||
} else {
|
audio: undefined,
|
||||||
audio = publisherProperties.audioSource;
|
video: undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
const audioSource = publisherProperties.audioSource;
|
||||||
|
const videoSource = publisherProperties.videoSource;
|
||||||
|
|
||||||
if (publisherProperties.videoSource === null || publisherProperties.videoSource === false) {
|
// CASE 1: null/false
|
||||||
video = false;
|
if (audioSource === null || audioSource === false) {
|
||||||
} else {
|
// No audio track
|
||||||
video = {
|
myConstraints.constraints!.audio = false;
|
||||||
height: {
|
|
||||||
ideal: 480
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
ideal: 640
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
if (videoSource === null || videoSource === false) {
|
||||||
if (audio === false && video === false) {
|
// No video track
|
||||||
|
myConstraints.constraints!.video = false;
|
||||||
|
}
|
||||||
|
if (myConstraints.constraints!.audio === false && myConstraints.constraints!.video === false) {
|
||||||
|
// ERROR! audioSource and videoSource cannot be both false at the same time
|
||||||
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"));
|
"Properties 'audioSource' and 'videoSource' cannot be set to false or null at the same time"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const mediaConstraints: MediaStreamConstraints = {
|
// CASE 2: MediaStreamTracks
|
||||||
audio,
|
if (typeof MediaStreamTrack !== 'undefined' && audioSource instanceof MediaStreamTrack) {
|
||||||
video
|
// Already provided audio track
|
||||||
};
|
myConstraints.audioTrack = audioSource;
|
||||||
|
}
|
||||||
if (typeof mediaConstraints.audio === 'string') {
|
if (typeof MediaStreamTrack !== 'undefined' && videoSource instanceof MediaStreamTrack) {
|
||||||
mediaConstraints.audio = { deviceId: { exact: mediaConstraints.audio } };
|
// Already provided video track
|
||||||
|
myConstraints.videoTrack = videoSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mediaConstraints.video) {
|
// CASE 3: Default tracks
|
||||||
|
if (audioSource === undefined) {
|
||||||
|
myConstraints.constraints!.audio = true;
|
||||||
|
}
|
||||||
|
if (videoSource === undefined) {
|
||||||
|
myConstraints.constraints!.video = {
|
||||||
|
width: {
|
||||||
|
ideal: 640
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
ideal: 480
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// CASE 3.5: give values to resolution and frameRate if video not null/false
|
||||||
|
if (videoSource !== null && videoSource !== false) {
|
||||||
if (!!publisherProperties.resolution) {
|
if (!!publisherProperties.resolution) {
|
||||||
const widthAndHeight = publisherProperties.resolution.toLowerCase().split('x');
|
const widthAndHeight = publisherProperties.resolution.toLowerCase().split('x');
|
||||||
const width = Number(widthAndHeight[0]);
|
const idealWidth = Number(widthAndHeight[0]);
|
||||||
const height = Number(widthAndHeight[1]);
|
const idealHeight = Number(widthAndHeight[1]);
|
||||||
(mediaConstraints.video as any).width.ideal = width;
|
myConstraints.constraints!.video = {
|
||||||
(mediaConstraints.video as any).height.ideal = height;
|
width: {
|
||||||
|
ideal: idealWidth
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
ideal: idealHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!!publisherProperties.frameRate) {
|
if (!!publisherProperties.frameRate) {
|
||||||
(mediaConstraints.video as any).frameRate = { ideal: publisherProperties.frameRate };
|
(<MediaTrackConstraints>myConstraints.constraints!.video).frameRate = { ideal: publisherProperties.frameRate };
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!!publisherProperties.videoSource && typeof publisherProperties.videoSource === 'string') {
|
// CASE 4: deviceId or screen sharing
|
||||||
|
if (typeof audioSource === 'string') {
|
||||||
|
myConstraints.constraints!.audio = { deviceId: { exact: audioSource } };
|
||||||
|
}
|
||||||
|
if (typeof videoSource === 'string') {
|
||||||
|
|
||||||
if (publisherProperties.videoSource === 'screen' ||
|
if (!this.isScreenShare(videoSource)) {
|
||||||
publisherProperties.videoSource === 'window' ||
|
if (!myConstraints.constraints!.video) {
|
||||||
(platform.name === 'Electron' && publisherProperties.videoSource.startsWith('screen:'))) {
|
myConstraints.constraints!.video = {};
|
||||||
|
}
|
||||||
|
(<MediaTrackConstraints>myConstraints.constraints!.video)['deviceId'] = { exact: videoSource };
|
||||||
|
} else {
|
||||||
|
|
||||||
if (!this.checkScreenSharingCapabilities()) {
|
// Screen sharing
|
||||||
|
|
||||||
const error = new OpenViduError(OpenViduErrorName.SCREEN_SHARING_NOT_SUPPORTED, 'You can only screen share in desktop Chrome, Firefox, Opera or Electron. Detected client: ' + platform.name);
|
if (!this.checkScreenSharingCapabilities()) {
|
||||||
console.error(error);
|
const error = new OpenViduError(OpenViduErrorName.SCREEN_SHARING_NOT_SUPPORTED, 'You can only screen share in desktop Chrome, Firefox, Opera or Electron. Detected client: ' + platform.name);
|
||||||
reject(error);
|
console.error(error);
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (platform.name === 'Electron') {
|
||||||
|
const prefix = "screen:";
|
||||||
|
const videoSourceString: string = videoSource;
|
||||||
|
const electronScreenId = videoSourceString.substr(videoSourceString.indexOf(prefix) + prefix.length);
|
||||||
|
(<any>myConstraints.constraints!.video) = {
|
||||||
|
mandatory: {
|
||||||
|
chromeMediaSource: 'desktop',
|
||||||
|
chromeMediaSourceId: electronScreenId
|
||||||
|
}
|
||||||
|
};
|
||||||
|
resolve(myConstraints);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (platform.name === 'Electron') {
|
if (!!this.advancedConfiguration.screenShareChromeExtension && !(platform.name!.indexOf('Firefox') !== -1) && !navigator.mediaDevices['getDisplayMedia']) {
|
||||||
|
|
||||||
const prefix = "screen:";
|
// Custom screen sharing extension for Chrome (and Opera) and no support for MediaDevices.getDisplayMedia()
|
||||||
const videoSourceString: string = publisherProperties.videoSource;
|
|
||||||
const electronScreenId = videoSourceString.substr(videoSourceString.indexOf(prefix) + prefix.length);
|
|
||||||
(<any>mediaConstraints['video']) = {
|
|
||||||
mandatory: {
|
|
||||||
chromeMediaSource: 'desktop',
|
|
||||||
chromeMediaSourceId: electronScreenId
|
|
||||||
}
|
|
||||||
};
|
|
||||||
resolve(mediaConstraints);
|
|
||||||
|
|
||||||
} else {
|
screenSharing.getScreenConstraints((error, screenConstraints) => {
|
||||||
|
if (!!error || !!screenConstraints.mandatory && screenConstraints.mandatory.chromeMediaSource === 'screen') {
|
||||||
if (!!this.advancedConfiguration.screenShareChromeExtension && !(platform.name!.indexOf('Firefox') !== -1) && !navigator.mediaDevices['getDisplayMedia']) {
|
if (error === 'permission-denied' || error === 'PermissionDeniedError') {
|
||||||
|
const error = new OpenViduError(OpenViduErrorName.SCREEN_CAPTURE_DENIED, 'You must allow access to one window of your desktop');
|
||||||
// Custom screen sharing extension for Chrome (and Opera) and no support for MediaDevices.getDisplayMedia()
|
console.error(error);
|
||||||
|
reject(error);
|
||||||
screenSharing.getScreenConstraints((error, screenConstraints) => {
|
|
||||||
if (!!error || !!screenConstraints.mandatory && screenConstraints.mandatory.chromeMediaSource === 'screen') {
|
|
||||||
if (error === 'permission-denied' || error === 'PermissionDeniedError') {
|
|
||||||
const error = new OpenViduError(OpenViduErrorName.SCREEN_CAPTURE_DENIED, 'You must allow access to one window of your desktop');
|
|
||||||
console.error(error);
|
|
||||||
reject(error);
|
|
||||||
} else {
|
|
||||||
const extensionId = this.advancedConfiguration.screenShareChromeExtension!.split('/').pop()!!.trim();
|
|
||||||
screenSharing.getChromeExtensionStatus(extensionId, (status) => {
|
|
||||||
if (status === 'installed-disabled') {
|
|
||||||
const error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_DISABLED, 'You must enable the screen extension');
|
|
||||||
console.error(error);
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
if (status === 'not-installed') {
|
|
||||||
const error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_NOT_INSTALLED, (<string>this.advancedConfiguration.screenShareChromeExtension));
|
|
||||||
console.error(error);
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
mediaConstraints.video = screenConstraints;
|
const extensionId = this.advancedConfiguration.screenShareChromeExtension!.split('/').pop()!!.trim();
|
||||||
resolve(mediaConstraints);
|
screenSharing.getChromeExtensionStatus(extensionId, status => {
|
||||||
}
|
if (status === 'installed-disabled') {
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
if (navigator.mediaDevices['getDisplayMedia']) {
|
|
||||||
// getDisplayMedia support (Chrome >= 72, Firefox >= 66)
|
|
||||||
resolve(mediaConstraints);
|
|
||||||
} else {
|
|
||||||
// Default screen sharing extension for Chrome/Opera, or is Firefox < 66
|
|
||||||
const firefoxString = platform.name!.indexOf('Firefox') !== -1 ? publisherProperties.videoSource : undefined;
|
|
||||||
|
|
||||||
screenSharingAuto.getScreenId(firefoxString, (error, sourceId, screenConstraints) => {
|
|
||||||
if (!!error) {
|
|
||||||
if (error === 'not-installed') {
|
|
||||||
const extensionUrl = !!this.advancedConfiguration.screenShareChromeExtension ? this.advancedConfiguration.screenShareChromeExtension :
|
|
||||||
'https://chrome.google.com/webstore/detail/openvidu-screensharing/lfcgfepafnobdloecchnfaclibenjold';
|
|
||||||
const error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_NOT_INSTALLED, extensionUrl);
|
|
||||||
console.error(error);
|
|
||||||
reject(error);
|
|
||||||
} else if (error === 'installed-disabled') {
|
|
||||||
const error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_DISABLED, 'You must enable the screen extension');
|
const error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_DISABLED, 'You must enable the screen extension');
|
||||||
console.error(error);
|
console.error(error);
|
||||||
reject(error);
|
reject(error);
|
||||||
} else if (error === 'permission-denied') {
|
}
|
||||||
const error = new OpenViduError(OpenViduErrorName.SCREEN_CAPTURE_DENIED, 'You must allow access to one window of your desktop');
|
if (status === 'not-installed') {
|
||||||
|
const error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_NOT_INSTALLED, (<string>this.advancedConfiguration.screenShareChromeExtension));
|
||||||
console.error(error);
|
console.error(error);
|
||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
mediaConstraints.video = screenConstraints.video;
|
return;
|
||||||
resolve(mediaConstraints);
|
}
|
||||||
}
|
} else {
|
||||||
});
|
myConstraints.constraints!.video = screenConstraints;
|
||||||
|
resolve(myConstraints);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (navigator.mediaDevices['getDisplayMedia']) {
|
||||||
|
// getDisplayMedia support (Chrome >= 72, Firefox >= 66)
|
||||||
|
resolve(myConstraints);
|
||||||
|
} else {
|
||||||
|
// Default screen sharing extension for Chrome/Opera, or is Firefox < 66
|
||||||
|
const firefoxString = platform.name!.indexOf('Firefox') !== -1 ? publisherProperties.videoSource : undefined;
|
||||||
|
|
||||||
|
screenSharingAuto.getScreenId(firefoxString, (error, sourceId, screenConstraints) => {
|
||||||
|
if (!!error) {
|
||||||
|
if (error === 'not-installed') {
|
||||||
|
const extensionUrl = !!this.advancedConfiguration.screenShareChromeExtension ? this.advancedConfiguration.screenShareChromeExtension :
|
||||||
|
'https://chrome.google.com/webstore/detail/openvidu-screensharing/lfcgfepafnobdloecchnfaclibenjold';
|
||||||
|
const err = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_NOT_INSTALLED, extensionUrl);
|
||||||
|
console.error(err);
|
||||||
|
reject(err);
|
||||||
|
} else if (error === 'installed-disabled') {
|
||||||
|
const err = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_DISABLED, 'You must enable the screen extension');
|
||||||
|
console.error(err);
|
||||||
|
reject(err);
|
||||||
|
} else if (error === 'permission-denied') {
|
||||||
|
const err = new OpenViduError(OpenViduErrorName.SCREEN_CAPTURE_DENIED, 'You must allow access to one window of your desktop');
|
||||||
|
console.error(err);
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
const err = new OpenViduError(OpenViduErrorName.GENERIC_ERROR, 'Unknown error when accessing screen share');
|
||||||
|
console.error(err);
|
||||||
|
console.error(error);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
myConstraints.constraints!.video = screenConstraints.video;
|
||||||
|
resolve(myConstraints);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
publisherProperties.videoSource = 'screen';
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// tslint:disable-next-line:no-string-literal
|
|
||||||
mediaConstraints.video['deviceId'] = { exact: publisherProperties.videoSource };
|
|
||||||
resolve(mediaConstraints);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
resolve(mediaConstraints);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
resolve(mediaConstraints);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolve(myConstraints);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -912,6 +964,18 @@ export class OpenVidu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
addAlreadyProvidedTracks(myConstraints: CustomMediaStreamConstraints, mediaStream: MediaStream) {
|
||||||
|
if (!!myConstraints.videoTrack) {
|
||||||
|
mediaStream.addTrack(myConstraints.videoTrack);
|
||||||
|
}
|
||||||
|
if (!!myConstraints.audioTrack) {
|
||||||
|
mediaStream.addTrack(myConstraints.audioTrack);
|
||||||
|
}
|
||||||
|
return mediaStream;
|
||||||
|
}
|
||||||
|
|
||||||
/* Private methods */
|
/* Private methods */
|
||||||
|
|
||||||
|
@ -961,4 +1025,10 @@ export class OpenVidu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isScreenShare(videoSource: string) {
|
||||||
|
return videoSource === 'screen' ||
|
||||||
|
videoSource === 'window' ||
|
||||||
|
(platform.name === 'Electron' && videoSource.startsWith('screen:'))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -279,7 +279,22 @@ export class Publisher extends StreamManager {
|
||||||
*/
|
*/
|
||||||
replaceTrack(track: MediaStreamTrack): Promise<any> {
|
replaceTrack(track: MediaStreamTrack): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.stream.getRTCPeerConnection().getSenders()[0].replaceTrack(track).then(() => {
|
const senders: RTCRtpSender[] = this.stream.getRTCPeerConnection().getSenders();
|
||||||
|
let sender: RTCRtpSender | undefined;
|
||||||
|
if (track.kind === 'video') {
|
||||||
|
sender = senders.find(s => !!s.track && s.track.kind === 'video');
|
||||||
|
if (!sender) {
|
||||||
|
reject(new Error('There\'s no replaceable track for that kind of MediaStreamTrack in this Publisher object'))
|
||||||
|
}
|
||||||
|
} else if (track.kind === 'audio') {
|
||||||
|
sender = senders.find(s => !!s.track && s.track.kind === 'audio');
|
||||||
|
if (!sender) {
|
||||||
|
reject(new Error('There\'s no replaceable track for that kind of MediaStreamTrack in this Publisher object'))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reject(new Error('Unknown track kind ' + track.kind));
|
||||||
|
}
|
||||||
|
(<any>sender).replaceTrack(track).then(() => {
|
||||||
let removedTrack: MediaStreamTrack;
|
let removedTrack: MediaStreamTrack;
|
||||||
if (track.kind === 'video') {
|
if (track.kind === 'video') {
|
||||||
removedTrack = this.stream.getMediaStream().getVideoTracks()[0];
|
removedTrack = this.stream.getMediaStream().getVideoTracks()[0];
|
||||||
|
@ -567,30 +582,17 @@ export class Publisher extends StreamManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if new constraints need to be generated. No constraints needed if
|
|
||||||
// - video track is given and no audio
|
|
||||||
// - audio track is given and no video
|
|
||||||
// - both video and audio tracks are given
|
|
||||||
if ((typeof MediaStreamTrack !== 'undefined' && this.properties.videoSource instanceof MediaStreamTrack && !this.properties.audioSource)
|
|
||||||
|| (typeof MediaStreamTrack !== 'undefined' && this.properties.audioSource instanceof MediaStreamTrack && !this.properties.videoSource)
|
|
||||||
|| (typeof MediaStreamTrack !== 'undefined' && this.properties.videoSource instanceof MediaStreamTrack && this.properties.audioSource instanceof MediaStreamTrack)) {
|
|
||||||
const mediaStream = new MediaStream();
|
|
||||||
if (typeof MediaStreamTrack !== 'undefined' && this.properties.videoSource instanceof MediaStreamTrack) {
|
|
||||||
mediaStream.addTrack((<MediaStreamTrack>this.properties.videoSource));
|
|
||||||
}
|
|
||||||
if (typeof MediaStreamTrack !== 'undefined' && this.properties.audioSource instanceof MediaStreamTrack) {
|
|
||||||
mediaStream.addTrack((<MediaStreamTrack>this.properties.audioSource));
|
|
||||||
}
|
|
||||||
// MediaStreamTracks are handled within callback - just call callback with new MediaStream() and let it handle the sources
|
|
||||||
successCallback(mediaStream);
|
|
||||||
// Return as we do not need to process further
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.openvidu.generateMediaConstraints(this.properties)
|
this.openvidu.generateMediaConstraints(this.properties)
|
||||||
.then(myConstraints => {
|
.then(myConstraints => {
|
||||||
|
|
||||||
constraints = myConstraints;
|
if (myConstraints.constraints === undefined) {
|
||||||
|
// No need to call getUserMedia at all. MediaStreamTracks already provided
|
||||||
|
successCallback(this.openvidu.addAlreadyProvidedTracks(myConstraints, new MediaStream()));
|
||||||
|
// Return as we do not need to process further
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints = myConstraints.constraints;
|
||||||
|
|
||||||
const outboundStreamOptions = {
|
const outboundStreamOptions = {
|
||||||
mediaConstraints: constraints,
|
mediaConstraints: constraints,
|
||||||
|
@ -605,19 +607,18 @@ export class Publisher extends StreamManager {
|
||||||
this.setPermissionDialogTimer(timeForDialogEvent);
|
this.setPermissionDialogTimer(timeForDialogEvent);
|
||||||
|
|
||||||
if (this.stream.isSendScreen() && navigator.mediaDevices['getDisplayMedia'] && platform.name !== 'Electron') {
|
if (this.stream.isSendScreen() && navigator.mediaDevices['getDisplayMedia'] && platform.name !== 'Electron') {
|
||||||
|
|
||||||
navigator.mediaDevices['getDisplayMedia']({ video: true })
|
navigator.mediaDevices['getDisplayMedia']({ video: true })
|
||||||
.then(mediaStream => {
|
.then(mediaStream => {
|
||||||
|
this.openvidu.addAlreadyProvidedTracks(myConstraints, mediaStream);
|
||||||
getMediaSuccess(mediaStream, definedAudioConstraint);
|
getMediaSuccess(mediaStream, definedAudioConstraint);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
getMediaError(error);
|
getMediaError(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
navigator.mediaDevices.getUserMedia(constraintsAux)
|
navigator.mediaDevices.getUserMedia(constraintsAux)
|
||||||
.then(mediaStream => {
|
.then(mediaStream => {
|
||||||
|
this.openvidu.addAlreadyProvidedTracks(myConstraints, mediaStream);
|
||||||
getMediaSuccess(mediaStream, definedAudioConstraint);
|
getMediaSuccess(mediaStream, definedAudioConstraint);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface CustomMediaStreamConstraints {
|
||||||
|
constraints: MediaStreamConstraints | undefined;
|
||||||
|
audioTrack: MediaStreamTrack | undefined;
|
||||||
|
videoTrack: MediaStreamTrack | undefined;
|
||||||
|
}
|
Loading…
Reference in New Issue