Merge branch 'openvidu_browser_refactor'

pull/508/head
csantosm 2020-07-02 11:50:44 +02:00
commit bc65f454a2
8 changed files with 253 additions and 171 deletions

View File

@ -0,0 +1,4 @@
npm run build
npm pack
cp openvidu-browser-2.14.0.tgz ../../openvidu-tutorials/openvidu-react-native
cp openvidu-browser-2.14.0.tgz ../../openvidu-react-native-adapter/

View File

@ -30,7 +30,6 @@ import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import * as screenSharingAuto from '../OpenViduInternal/ScreenSharing/Screen-Capturing-Auto';
import * as screenSharing from '../OpenViduInternal/ScreenSharing/Screen-Capturing';
/**
* @hidden
*/
@ -748,115 +747,7 @@ export class OpenVidu {
}
// CASE 4: deviceId or screen sharing
if (typeof audioSource === 'string') {
myConstraints.constraints!.audio = { deviceId: { exact: audioSource } };
}
if (typeof videoSource === 'string') {
if (!this.isScreenShare(videoSource)) {
if (!myConstraints.constraints!.video) {
myConstraints.constraints!.video = {};
}
(<MediaTrackConstraints>myConstraints.constraints!.video)['deviceId'] = { exact: videoSource };
} else {
// Screen sharing
if (!this.checkScreenSharingCapabilities()) {
const error = new OpenViduError(OpenViduErrorName.SCREEN_SHARING_NOT_SUPPORTED, 'You can only screen share in desktop Chrome, Firefox, Opera, Safari (>=13.0) or Electron. Detected client: ' + platform.name);
logger.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 {
if (!!this.advancedConfiguration.screenShareChromeExtension && !(platform.name!.indexOf('Firefox') !== -1) && !navigator.mediaDevices['getDisplayMedia']) {
// Custom screen sharing extension for Chrome (and Opera) and no support for MediaDevices.getDisplayMedia()
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');
logger.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');
logger.error(error);
reject(error);
}
if (status === 'not-installed') {
const error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_NOT_INSTALLED, (<string>this.advancedConfiguration.screenShareChromeExtension));
logger.error(error);
reject(error);
}
});
return;
}
} else {
myConstraints.constraints!.video = screenConstraints;
resolve(myConstraints);
}
});
return;
} else {
if (navigator.mediaDevices['getDisplayMedia']) {
// getDisplayMedia support (Chrome >= 72, Firefox >= 66, Safari >= 13)
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);
logger.error(err);
reject(err);
} else if (error === 'installed-disabled') {
const err = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_DISABLED, 'You must enable the screen extension');
logger.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');
logger.error(err);
reject(err);
} else {
const err = new OpenViduError(OpenViduErrorName.GENERIC_ERROR, 'Unknown error when accessing screen share');
logger.error(err);
logger.error(error);
reject(err);
}
} else {
myConstraints.constraints!.video = screenConstraints.video;
resolve(myConstraints);
}
});
return;
}
}
}
}
}
}
this.configureDeviceIdOrScreensharing(myConstraints, publisherProperties, resolve, reject);
resolve(myConstraints);
});
@ -984,6 +875,133 @@ export class OpenVidu {
return mediaStream;
}
/**
* @hidden
*/
protected configureDeviceIdOrScreensharing(myConstraints: CustomMediaStreamConstraints, publisherProperties: PublisherProperties, resolve, reject) {
const audioSource = publisherProperties.audioSource;
const videoSource = publisherProperties.videoSource;
if (typeof audioSource === 'string') {
myConstraints.constraints!.audio = { deviceId: { exact: audioSource } };
}
if (typeof videoSource === 'string') {
if (!this.isScreenShare(videoSource)) {
this.setVideoSource(myConstraints, videoSource);
} else {
// Screen sharing
if (!this.checkScreenSharingCapabilities()) {
const error = new OpenViduError(OpenViduErrorName.SCREEN_SHARING_NOT_SUPPORTED, 'You can only screen share in desktop Chrome, Firefox, Opera, Safari (>=13.0) or Electron. Detected client: ' + platform.name);
logger.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 {
if (!!this.advancedConfiguration.screenShareChromeExtension && !(platform.name!.indexOf('Firefox') !== -1) && !navigator.mediaDevices['getDisplayMedia']) {
// Custom screen sharing extension for Chrome (and Opera) and no support for MediaDevices.getDisplayMedia()
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');
logger.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');
logger.error(error);
reject(error);
}
if (status === 'not-installed') {
const error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_NOT_INSTALLED, (<string>this.advancedConfiguration.screenShareChromeExtension));
logger.error(error);
reject(error);
}
});
return;
}
} else {
myConstraints.constraints!.video = screenConstraints;
resolve(myConstraints);
}
});
return;
} else {
if (navigator.mediaDevices['getDisplayMedia']) {
// getDisplayMedia support (Chrome >= 72, Firefox >= 66, Safari >= 13)
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);
logger.error(err);
reject(err);
} else if (error === 'installed-disabled') {
const err = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_DISABLED, 'You must enable the screen extension');
logger.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');
logger.error(err);
reject(err);
} else {
const err = new OpenViduError(OpenViduErrorName.GENERIC_ERROR, 'Unknown error when accessing screen share');
logger.error(err);
logger.error(error);
reject(err);
}
} else {
myConstraints.constraints!.video = screenConstraints.video;
resolve(myConstraints);
}
});
return;
}
}
}
}
}
}
}
/**
* @hidden
*/
protected setVideoSource(myConstraints: CustomMediaStreamConstraints, videoSource: string) {
if (!myConstraints.constraints!.video) {
myConstraints.constraints!.video = {};
}
(<MediaTrackConstraints>myConstraints.constraints!.video)['deviceId'] = { exact: videoSource };
}
/* Private methods */
private disconnectCallback(): void {

View File

@ -67,7 +67,7 @@ export class Publisher extends StreamManager {
session: Session; // Initialized by Session.publish(Publisher)
private accessDenied = false;
private properties: PublisherProperties;
protected properties: PublisherProperties;
private permissionDialogTimeout: NodeJS.Timer;
/**
@ -385,19 +385,7 @@ export class Publisher extends StreamManager {
mediaStream.getVideoTracks()[0].enabled = enabled;
}
this.videoReference = document.createElement('video');
if (platform.name === 'Safari') {
this.videoReference.setAttribute('playsinline', 'true');
}
this.stream.setMediaStream(mediaStream);
if (!!this.firstVideoElement) {
this.createVideoElement(this.firstVideoElement.targetElement, <VideoInsertMode>this.properties.insertMode);
}
this.videoReference.srcObject = mediaStream;
this.initializeVideoReference(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
@ -443,7 +431,7 @@ export class Publisher extends StreamManager {
// Rest of platforms
// With no screen share, video dimension can be set directly from MediaStream (getSettings)
// Orientation must be checked for mobile devices (width and height are reversed)
const { width, height } = mediaStream.getVideoTracks()[0].getSettings();
const { width, height } = this.getVideoDimensions(mediaStream);
if ((platform.os!!.family === 'iOS' || platform.os!!.family === 'Android') && (window.innerHeight > window.innerWidth)) {
// Mobile portrait mode
@ -666,6 +654,13 @@ export class Publisher extends StreamManager {
});
}
/**
* @hidden
*/
getVideoDimensions(mediaStream: MediaStream): MediaTrackSettings {
return mediaStream.getVideoTracks()[0].getSettings();
}
/**
* @hidden
*/
@ -675,6 +670,25 @@ export class Publisher extends StreamManager {
}
}
/**
* @hidden
*/
initializeVideoReference(mediaStream: MediaStream) {
this.videoReference = document.createElement('video');
if (platform.name === 'Safari') {
this.videoReference.setAttribute('playsinline', 'true');
}
this.stream.setMediaStream(mediaStream);
if (!!this.firstVideoElement) {
this.createVideoElement(this.firstVideoElement.targetElement, <VideoInsertMode>this.properties.insertMode);
}
this.videoReference.srcObject = mediaStream;
}
/* Private methods */

View File

@ -1098,6 +1098,21 @@ export class Session extends EventDispatcher {
}
}
/**
* @hidden
*/
initializeParams(token: string) {
const joinParams = {
token: (!!token) ? token : '',
session: this.sessionId,
platform: !!platform.description ? platform.description : 'unknown',
metadata: !!this.options.metadata ? this.options.metadata : '',
secret: this.openvidu.getSecret(),
recorder: this.openvidu.getRecorder()
};
return joinParams;
}
/* Private methods */
@ -1108,14 +1123,7 @@ export class Session extends EventDispatcher {
reject(error);
} else {
const joinParams = {
token: (!!token) ? token : '',
session: this.sessionId,
platform: !!platform.description ? platform.description : 'unknown',
metadata: !!this.options.metadata ? this.options.metadata : '',
secret: this.openvidu.getSecret(),
recorder: this.openvidu.getRecorder()
};
const joinParams = this.initializeParams(token);
this.openvidu.sendRequest('joinRoom', joinParams, (error, response) => {
if (!!error) {
@ -1193,7 +1201,7 @@ export class Session extends EventDispatcher {
}
}
private getConnection(connectionId: string, errorMessage: string): Promise<Connection> {
protected getConnection(connectionId: string, errorMessage: string): Promise<Connection> {
return new Promise<Connection>((resolve, reject) => {
const connection = this.remoteConnections[connectionId];
if (!!connection) {

View File

@ -133,8 +133,8 @@ export class Stream extends EventDispatcher {
*/
filter: Filter;
private webRtcPeer: WebRtcPeer;
private mediaStream: MediaStream;
protected webRtcPeer: WebRtcPeer;
protected mediaStream: MediaStream;
private webRtcStats: WebRtcStats;
private isSubscribeToRemote = false;
@ -964,7 +964,10 @@ export class Stream extends EventDispatcher {
});
}
private remotePeerSuccessfullyEstablished(): void {
/**
* @hidden
*/
remotePeerSuccessfullyEstablished(): void {
this.mediaStream = new MediaStream();
let receiver: RTCRtpReceiver;
for (receiver of this.webRtcPeer.pc.getReceivers()) {

View File

@ -297,7 +297,7 @@ export class StreamManager extends EventDispatcher {
throw new Error("The provided 'targetElement' couldn't be resolved to any HTML element: " + targetElement);
}
const video = document.createElement('video');
const video = this.createVideo();
this.initializeVideoProperties(video);
let insMode = !!insertMode ? insertMode : VideoInsertMode.APPEND;
@ -414,8 +414,9 @@ export class StreamManager extends EventDispatcher {
this.videos.forEach(streamManagerVideo => {
// Remove oncanplay event listener (only OpenVidu browser listener, not the user ones)
streamManagerVideo.video.removeEventListener('canplay', this.canPlayListener);
streamManagerVideo.canplayListenerAdded = false;
if(!!streamManagerVideo.video && !!streamManagerVideo.video.removeEventListener){
streamManagerVideo.video.removeEventListener('canplay', this.canPlayListener);
} streamManagerVideo.canplayListenerAdded = false;
if (!!streamManagerVideo.targetElement) {
// Only remove from DOM videos created by OpenVidu Browser (those generated by passing a valid targetElement in OpenVidu.initPublisher
// and Session.subscribe or those created by StreamManager.createVideoElement). All this videos triggered a videoElementCreated event
@ -423,7 +424,7 @@ export class StreamManager extends EventDispatcher {
this.ee.emitEvent('videoElementDestroyed', [new VideoElementEvent(streamManagerVideo.video, this, 'videoElementDestroyed')]);
}
// Remove srcObject from the video
streamManagerVideo.video.srcObject = null;
this.removeSrcObject(streamManagerVideo);
// Remove from collection of videos every video managed by OpenVidu Browser
this.videos.filter(v => !v.targetElement);
});
@ -480,9 +481,23 @@ export class StreamManager extends EventDispatcher {
this.ee.emitEvent(type, eventArray);
}
/**
* @hidden
*/
createVideo(): HTMLVideoElement {
return document.createElement('video');
}
/**
* @hidden
*/
removeSrcObject(streamManagerVideo: StreamManagerVideo) {
streamManagerVideo.video.srcObject = null;
}
/* Private methods */
private pushNewStreamManagerVideo(streamManagerVideo: StreamManagerVideo) {
protected pushNewStreamManagerVideo(streamManagerVideo: StreamManagerVideo) {
this.videos.push(streamManagerVideo);
this.addPlayEventToFirstVideo();
if (this.stream.session.streamManagers.indexOf(this) === -1) {

View File

@ -2,16 +2,19 @@
var chromeMediaSource = 'screen';
var sourceId;
var screenCallback;
var isFirefox = typeof window.InstallTrigger !== 'undefined';
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
var isChrome = !!window.chrome && !isOpera;
window.addEventListener('message', function (event) {
if (event.origin != window.location.origin) {
return;
}
onMessageCallback(event.data);
});
if(typeof window !== 'undefined' && typeof navigator !== 'undefined' && typeof navigator.userAgent !== 'undefined'){
var isFirefox = typeof window.InstallTrigger !== 'undefined';
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
var isChrome = !!window.chrome && !isOpera;
window.addEventListener('message', function (event) {
if (event.origin != window.location.origin) {
return;
}
onMessageCallback(event.data);
});
}
// and the function that handles received messages
function onMessageCallback(data) {
@ -148,7 +151,7 @@ function getScreenConstraints(callback, captureSourceIdWithAudio) {
return;
}
// this statement sets gets 'sourceId" and sets "chromeMediaSourceId"
// this statement sets gets 'sourceId" and sets "chromeMediaSourceId"
if (chromeMediaSource == 'desktop') {
screen_constraints.mandatory.chromeMediaSourceId = sourceId;
}

View File

@ -49,11 +49,11 @@ export class WebRtcPeer {
private candidategatheringdone = false;
constructor(private configuration: WebRtcPeerConfiguration) {
constructor(protected configuration: WebRtcPeerConfiguration) {
this.configuration.iceServers = (!!this.configuration.iceServers && this.configuration.iceServers.length > 0) ? this.configuration.iceServers : freeice();
this.pc = new RTCPeerConnection({ iceServers: this.configuration.iceServers });
this.id = !!configuration.id ? configuration.id : uuid.v4();
this.id = !!configuration.id ? configuration.id : this.generateUniqueId();
this.pc.onicecandidate = event => {
if (!!event.candidate) {
@ -71,7 +71,8 @@ export class WebRtcPeer {
this.pc.onsignalingstatechange = () => {
if (this.pc.signalingState === 'stable') {
while (this.iceCandidateList.length > 0) {
this.pc.addIceCandidate(<RTCIceCandidate>this.iceCandidateList.shift());
let candidate = this.iceCandidateList.shift();
this.pc.addIceCandidate(<RTCIceCandidate>candidate);
}
}
};
@ -206,25 +207,34 @@ export class WebRtcPeer {
if (this.pc.signalingState === 'closed') {
reject('RTCPeerConnection is closed');
}
if (platform['isIonicIos']) {
// Ionic iOS platform
if (needsTimeoutOnProcessAnswer) {
// 400 ms have not elapsed yet since first remote stream triggered Stream#initWebRtcPeerReceive
setTimeout(() => {
logger.info('setRemoteDescription run after timeout for Ionic iOS device');
this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
}, 250);
} else {
// 400 ms have elapsed
this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
}
} else {
// Rest of platforms
this.pc.setRemoteDescription(answer).then(() => resolve()).catch(error => reject(error));
}
this.setRemoteDescription(answer, needsTimeoutOnProcessAnswer, resolve, reject);
});
}
/**
* @hidden
*/
setRemoteDescription(answer: RTCSessionDescriptionInit, needsTimeoutOnProcessAnswer: boolean, resolve: (value?: string | PromiseLike<string> | undefined) => void, reject: (reason?: any) => void) {
if (platform['isIonicIos']) {
// Ionic iOS platform
if (needsTimeoutOnProcessAnswer) {
// 400 ms have not elapsed yet since first remote stream triggered Stream#initWebRtcPeerReceive
setTimeout(() => {
logger.info('setRemoteDescription run after timeout for Ionic iOS device');
this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
}, 250);
} else {
// 400 ms have elapsed
this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
}
} else {
// Rest of platforms
this.pc.setRemoteDescription(answer).then(() => resolve()).catch(error => reject(error));
}
}
/**
* Callback function invoked when an ICE candidate is received
*/
@ -281,6 +291,13 @@ export class WebRtcPeer {
}
}
/**
* @hidden
*/
generateUniqueId(): string {
return uuid.v4();
}
}