Dynamic change of Publisher without leaving the Session

pull/30/head
pabloFuente 2018-01-18 15:59:28 +01:00
parent 7c29052e70
commit 34b753a298
9 changed files with 219 additions and 205 deletions

View File

@ -19,6 +19,7 @@ import { OpenViduInternal } from '../OpenViduInternal/OpenViduInternal';
import { Session } from './Session'; import { Session } from './Session';
import { Publisher } from './Publisher'; import { Publisher } from './Publisher';
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/OpenViduError'; import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/OpenViduError';
import { OutboundStreamOptions } from '../OpenViduInternal/index';
import * as adapter from 'webrtc-adapter'; import * as adapter from 'webrtc-adapter';
import * as screenSharing from '../ScreenSharing/Screen-Capturing.js'; import * as screenSharing from '../ScreenSharing/Screen-Capturing.js';
@ -58,7 +59,6 @@ export class OpenVidu {
initPublisher(parentId: string, cameraOptions?: any, callback?: Function): any { initPublisher(parentId: string, cameraOptions?: any, callback?: Function): any {
if (this.checkSystemRequirements()) { if (this.checkSystemRequirements()) {
this.openVidu.storedPublisherOptions = cameraOptions;
let publisher: Publisher; let publisher: Publisher;
if (cameraOptions != null) { if (cameraOptions != null) {
@ -66,6 +66,7 @@ export class OpenVidu {
cameraOptions.video = cameraOptions.video != null ? cameraOptions.video : true; cameraOptions.video = cameraOptions.video != null ? cameraOptions.video : true;
if (!cameraOptions.screen) { if (!cameraOptions.screen) {
// Webcam and/or microphone is being requested // Webcam and/or microphone is being requested
let cameraOptionsAux = { let cameraOptionsAux = {
@ -73,16 +74,19 @@ export class OpenVidu {
sendVideo: cameraOptions.video != null ? cameraOptions.video : true, sendVideo: cameraOptions.video != null ? cameraOptions.video : true,
activeAudio: cameraOptions.audioActive != null ? cameraOptions.audioActive : true, activeAudio: cameraOptions.audioActive != null ? cameraOptions.audioActive : true,
activeVideo: cameraOptions.videoActive != null ? cameraOptions.videoActive : true, activeVideo: cameraOptions.videoActive != null ? cameraOptions.videoActive : true,
data: true, dataChannel: true,
mediaConstraints: this.openVidu.generateMediaConstraints(cameraOptions) mediaConstraints: this.openVidu.generateMediaConstraints(cameraOptions)
}; };
cameraOptions = cameraOptionsAux; cameraOptions = cameraOptionsAux;
publisher = new Publisher(this.openVidu.initPublisherTagged(parentId, cameraOptions, callback), parentId, false);
publisher = new Publisher(this.openVidu.initPublisherTagged(parentId, cameraOptions, true, callback), parentId, false);
console.info("'Publisher' initialized"); console.info("'Publisher' initialized");
return publisher; return publisher;
} else { } else {
publisher = new Publisher(this.openVidu.initPublisherScreen(parentId, callback), parentId, true);
publisher = new Publisher(this.openVidu.initPublisherScreen(parentId, true, callback), parentId, true);
if (adapter.browserDetails.browser === 'firefox' && adapter.browserDetails.version >= 52) { if (adapter.browserDetails.browser === 'firefox' && adapter.browserDetails.version >= 52) {
screenSharingAuto.getScreenId((error, sourceId, screenConstraints) => { screenSharingAuto.getScreenId((error, sourceId, screenConstraints) => {
cameraOptions = { cameraOptions = {
@ -90,14 +94,17 @@ export class OpenVidu {
sendVideo: cameraOptions.video, sendVideo: cameraOptions.video,
activeAudio: cameraOptions.audioActive != null ? cameraOptions.audioActive : true, activeAudio: cameraOptions.audioActive != null ? cameraOptions.audioActive : true,
activeVideo: cameraOptions.videoActive != null ? cameraOptions.videoActive : true, activeVideo: cameraOptions.videoActive != null ? cameraOptions.videoActive : true,
data: true, dataChannel: true,
mediaConstraints: { mediaConstraints: {
video: screenConstraints.video, video: screenConstraints.video,
audio: false audio: false
} }
} }
publisher.stream.configureScreenOptions(cameraOptions); publisher.stream.configureScreenOptions(cameraOptions);
console.info("'Publisher' initialized"); console.info("'Publisher' initialized");
publisher.stream.ee.emitEvent('can-request-screen');
}); });
return publisher; return publisher;
} else if (adapter.browserDetails.browser === 'chrome') { } else if (adapter.browserDetails.browser === 'chrome') {
@ -145,14 +152,16 @@ export class OpenVidu {
sendVideo: cameraOptions.video != null ? cameraOptions.video : true, sendVideo: cameraOptions.video != null ? cameraOptions.video : true,
activeAudio: cameraOptions.audioActive != null ? cameraOptions.audioActive : true, activeAudio: cameraOptions.audioActive != null ? cameraOptions.audioActive : true,
activeVideo: cameraOptions.videoActive != null ? cameraOptions.videoActive : true, activeVideo: cameraOptions.videoActive != null ? cameraOptions.videoActive : true,
data: true, dataChannel: true,
mediaConstraints: { mediaConstraints: {
video: screenConstraints.video, video: screenConstraints.video,
audio: false audio: false
} }
} }
publisher.stream.configureScreenOptions(cameraOptions); publisher.stream.configureScreenOptions(cameraOptions);
publisher.stream.ee.emitEvent('can-request-screen');
}, (error) => { }, (error) => {
console.error('getScreenId error', error); console.error('getScreenId error', error);
return; return;
@ -169,14 +178,15 @@ export class OpenVidu {
sendVideo: true, sendVideo: true,
activeAudio: true, activeAudio: true,
activeVideo: true, activeVideo: true,
data: true, dataChannel: true,
mediaConstraints: { mediaConstraints: {
audio: true, audio: true,
video: { width: { ideal: 1280 } } video: { width: { ideal: 1280 } }
} }
} }
publisher = new Publisher(this.openVidu.initPublisherTagged(parentId, cameraOptions, callback), parentId, false); publisher = new Publisher(this.openVidu.initPublisherTagged(parentId, cameraOptions, true, callback), parentId, false);
console.info("'Publisher' initialized"); console.info("'Publisher' initialized");
return publisher; return publisher;
} }
} else { } else {
@ -184,6 +194,50 @@ export class OpenVidu {
} }
} }
reinitPublisher(publisher: Publisher): any {
if (publisher.stream.typeOfVideo !== 'SCREEN') {
publisher = new Publisher(this.openVidu.initPublisherTagged(publisher.stream.getParentId(), publisher.stream.outboundOptions, false), publisher.stream.getParentId(), false);
console.info("'Publisher' initialized");
return publisher;
} else {
publisher = new Publisher(this.openVidu.initPublisherScreen(publisher.stream.getParentId(), false), publisher.stream.getParentId(), true);
if (adapter.browserDetails.browser === 'firefox' && adapter.browserDetails.version >= 52) {
screenSharingAuto.getScreenId((error, sourceId, screenConstraints) => {
publisher.stream.outboundOptions.mediaConstraints.video = screenConstraints.video;
publisher.stream.configureScreenOptions(publisher.stream.outboundOptions);
console.info("'Publisher' initialized");
publisher.stream.ee.emitEvent('can-request-screen');
});
return publisher;
} else if (adapter.browserDetails.browser === 'chrome') {
screenSharingAuto.getScreenId((error, sourceId, screenConstraints) => {
if (error === 'not-installed') {
let error = new OpenViduError(OpenViduErrorName.SCREEN_EXTENSION_NOT_INSTALLED, 'https://chrome.google.com/webstore/detail/screen-capturing/ajhifddimkapgcifgcodmmfdlknahffk');
console.error(error);
return;
} else if (error === 'permission-denied') {
let error = new OpenViduError(OpenViduErrorName.SCREEN_CAPTURE_DENIED, 'You must allow access to one window of your desktop');
console.error(error);
return;
}
publisher.stream.outboundOptions.mediaConstraints.video = screenConstraints.video;
publisher.stream.configureScreenOptions(publisher.stream.outboundOptions);
publisher.stream.ee.emitEvent('can-request-screen');
}, (error) => {
console.error('getScreenId error', error);
return;
});
console.info("'Publisher' initialized");
return publisher;
} else {
console.error('Screen sharing not supported on ' + adapter.browserDetails.browser);
}
}
}
checkSystemRequirements(): number { checkSystemRequirements(): number {
let browser = adapter.browserDetails.browser; let browser = adapter.browserDetails.browser;
let version = adapter.browserDetails.version; let version = adapter.browserDetails.version;

View File

@ -5,7 +5,7 @@
* *
* stream.hasAudio(); stream.hasVideo(); stream.hasData(); * stream.hasAudio(); stream.hasVideo(); stream.hasData();
*/ */
import { Stream, StreamOptions } from '../OpenViduInternal/Stream'; import { Stream } from '../OpenViduInternal/Stream';
import { Session } from './Session'; import { Session } from './Session';
import EventEmitter = require('wolfy87-eventemitter'); import EventEmitter = require('wolfy87-eventemitter');

View File

@ -11,7 +11,6 @@ import EventEmitter = require('wolfy87-eventemitter');
export class Session { export class Session {
sessionId: String; sessionId: String;
//capabilities: Capabilities
connection: Connection; connection: Connection;
private ee = new EventEmitter(); private ee = new EventEmitter();
@ -89,13 +88,14 @@ export class Session {
this.streamPublish(publisher); this.streamPublish(publisher);
} }
} else { // 'Session.unpublish(Publisher)' has been called } else { // 'Session.unpublish(Publisher)' has been called
let mypublisher = this.openVidu.initPublisher(publisher.stream.getParentId(), this.openVidu.openVidu.storedPublisherOptions); publisher = this.openVidu.reinitPublisher(publisher);
if (mypublisher.isScreenRequested && !mypublisher.stream.isScreenRequestedReady) { // Screen sharing Publisher and video stream not available yet
mypublisher.stream.addOnceEventListener('screen-ready', () => { if (publisher.isScreenRequested && !publisher.stream.isScreenRequestedReady) { // Screen sharing Publisher and video stream not available yet
this.streamPublish(mypublisher); publisher.stream.addOnceEventListener('screen-ready', () => {
this.streamPublish(publisher);
}); });
} else { // Video stream already available } else { // Video stream already available
this.streamPublish(mypublisher); this.streamPublish(publisher);
} }
} }
} }

View File

@ -1,4 +1,4 @@
import { Stream, StreamOptions } from '../OpenViduInternal/Stream'; import { Stream } from '../OpenViduInternal/Stream';
import EventEmitter = require('wolfy87-eventemitter'); import EventEmitter = require('wolfy87-eventemitter');

View File

@ -1,4 +1,4 @@
import { Stream, StreamOptions } from './Stream'; import { Stream, StreamOptionsServer, InboundStreamOptions } from './Stream';
import { OpenViduInternal } from './OpenViduInternal'; import { OpenViduInternal } from './OpenViduInternal';
import { SessionInternal } from './SessionInternal'; import { SessionInternal } from './SessionInternal';
@ -7,9 +7,7 @@ type ObjMap<T> = { [s: string]: T; }
export interface ConnectionOptions { export interface ConnectionOptions {
id: string; id: string;
metadata: string; metadata: string;
streams?: StreamOptions[]; streams: StreamOptionsServer[];
audioActive: boolean;
videoActive: boolean;
} }
export class Connection { export class Connection {
@ -18,7 +16,7 @@ export class Connection {
public data: string; public data: string;
public creationTime: number; public creationTime: number;
private streams: ObjMap<Stream> = {}; private streams: ObjMap<Stream> = {};
private streamsOpts: StreamOptions; private inboundStreamsOpts: InboundStreamOptions;
constructor( private openVidu: OpenViduInternal, private local: boolean, private room: SessionInternal, private options?: ConnectionOptions ) { constructor( private openVidu: OpenViduInternal, private local: boolean, private room: SessionInternal, private options?: ConnectionOptions ) {
@ -27,10 +25,11 @@ export class Connection {
if ( options ) { if ( options ) {
this.connectionId = options.id; this.connectionId = options.id;
if (options.metadata) {
this.data = options.metadata; this.data = options.metadata;
}
if (options.streams) { if (options.streams) {
this.initStreams(options); this.initRemoteStreams(options);
} }
} }
@ -44,7 +43,11 @@ export class Connection {
removeStream( key: string ) { removeStream( key: string ) {
delete this.streams[key]; delete this.streams[key];
delete this.room.getStreams()[key]; delete this.room.getStreams()[key];
delete this.streamsOpts; delete this.inboundStreamsOpts;
}
setOptions(options: ConnectionOptions) {
this.options = options;
} }
getStreams() { getStreams() {
@ -75,28 +78,23 @@ export class Connection {
}); });
} }
initStreams(options) { initRemoteStreams(options: ConnectionOptions) {
for ( let streamOptions of options.streams ) { let opts: StreamOptionsServer;
for ( opts of options.streams ) {
let streamOpts = { let streamOptions: InboundStreamOptions = {
id: streamOptions.id, id: opts.id,
connection: this, connection: this,
sendAudio: streamOptions.sendAudio, recvAudio: ( opts.audioActive == null ? true : opts.audioActive ),
sendVideo: streamOptions.sendVideo, recvVideo: ( opts.videoActive == null ? true : opts.videoActive ),
recvAudio: ( streamOptions.audioActive == undefined ? true : streamOptions.audioActive ), typeOfVideo: opts.typeOfVideo,
recvVideo: ( streamOptions.videoActive == undefined ? true : streamOptions.videoActive ),
typeOfVideo: streamOptions.typeOfVideo,
activeAudio: streamOptions.activeAudio,
activeVideo: streamOptions.activeVideo,
data: streamOptions.data,
mediaConstraints: streamOptions.mediaConstraints
} }
let stream = new Stream(this.openVidu, false, this.room, streamOpts ); let stream = new Stream(this.openVidu, false, this.room, streamOptions);
this.addStream(stream); this.addStream(stream);
this.streamsOpts = streamOpts; this.inboundStreamsOpts = streamOptions;
} }
console.info("Remote 'Connection' with 'connectionId' [" + this.connectionId + "] is now configured for receiving Streams with options: ", this.streamsOpts ); console.info("Remote 'Connection' with 'connectionId' [" + this.connectionId + "] is now configured for receiving Streams with options: ", this.inboundStreamsOpts );
} }
} }

View File

@ -1,4 +1,4 @@
export enum OpenViduErrorName { export const enum OpenViduErrorName {
CAMERA_ACCESS_DENIED = 'CAMERA_ACCESS_DENIED', CAMERA_ACCESS_DENIED = 'CAMERA_ACCESS_DENIED',
MICROPHONE_ACCESS_DENIED = 'MICROPHONE_ACCESS_DENIED', MICROPHONE_ACCESS_DENIED = 'MICROPHONE_ACCESS_DENIED',
SCREEN_CAPTURE_DENIED = 'SCREEN_CAPTURE_DENIED', SCREEN_CAPTURE_DENIED = 'SCREEN_CAPTURE_DENIED',

View File

@ -16,7 +16,7 @@
*/ */
import { SessionInternal, SessionOptions } from './SessionInternal'; import { SessionInternal, SessionOptions } from './SessionInternal';
import { OpenViduError, OpenViduErrorName } from './OpenViduError'; import { OpenViduError, OpenViduErrorName } from './OpenViduError';
import { Stream } from './Stream'; import { Stream, OutboundStreamOptions } from './Stream';
import * as RpcBuilder from '../KurentoUtils/kurento-jsonrpc'; import * as RpcBuilder from '../KurentoUtils/kurento-jsonrpc';
export type Callback<T> = (error?: any, openVidu?: T) => void; export type Callback<T> = (error?: any, openVidu?: T) => void;
@ -28,15 +28,10 @@ export class OpenViduInternal {
private jsonRpcClient: any; private jsonRpcClient: any;
private rpcParams: any; private rpcParams: any;
private callback: Callback<OpenViduInternal>; private callback: Callback<OpenViduInternal>;
private camera: Stream; private localStream: Stream;
private remoteStreams: Stream[] = []; private remoteStreams: Stream[] = [];
private secret: string; private secret: string;
constructor() { };
storedPublisherOptions: any;
/* NEW METHODS */ /* NEW METHODS */
initSession(sessionId) { initSession(sessionId) {
console.info("'Session' initialized with 'sessionId' [" + sessionId + "]"); console.info("'Session' initialized with 'sessionId' [" + sessionId + "]");
@ -44,36 +39,56 @@ export class OpenViduInternal {
return this.session; return this.session;
} }
initPublisherTagged(parentId: string, cameraOptions: any, callback?: Function): Stream { initPublisherTagged(parentId: string, cameraOptions: OutboundStreamOptions, newStream: boolean, callback?: Function): Stream {
this.getCamera(cameraOptions); if (newStream) {
if (cameraOptions == null) {
cameraOptions = {
connection: this.session.getLocalParticipant(),
sendAudio: true,
sendVideo: true,
activeAudio: true,
activeVideo: true,
dataChannel: true,
mediaConstraints: {
audio: true,
video: { width: { ideal: 1280 } }
}
}
} else {
cameraOptions.connection = this.session.getLocalParticipant();
}
this.localStream = new Stream(this, true, this.session, cameraOptions);
}
this.camera.requestCameraAccess((error, camera) => { this.localStream.requestCameraAccess((error, localStream) => {
if (error) { if (error) {
// Neither camera or microphone device is allowed/able to capture media // Neither localStream or microphone device is allowed/able to capture media
console.error(error); console.error(error);
if (callback) { if (callback) {
callback(error); callback(error);
} }
this.camera.ee.emitEvent('access-denied-by-publisher'); this.localStream.ee.emitEvent('access-denied-by-publisher');
} else { } else {
this.camera.setVideoElement(this.cameraReady(camera!, parentId)); this.localStream.setVideoElement(this.cameraReady(localStream!, parentId));
if (callback) { if (callback) {
callback(undefined); callback(undefined);
} }
} }
}); });
return this.camera; return this.localStream;
} }
initPublisherScreen(parentId: string, callback?): Stream { initPublisherScreen(parentId: string, newStream: boolean, callback?): Stream {
if (!this.camera) {
this.camera = new Stream(this, true, this.session, 'screen-options'); if (newStream) {
this.localStream = new Stream(this, true, this.session, 'screen-options');
} }
this.camera.addOnceEventListener('can-request-screen', () => {
this.camera.requestCameraAccess((error, camera) => { this.localStream.addOnceEventListener('can-request-screen', () => {
this.localStream.requestCameraAccess((error, localStream) => {
if (error) { if (error) {
this.camera.ee.emitEvent('access-denied-by-publisher'); this.localStream.ee.emitEvent('access-denied-by-publisher');
let errorName: OpenViduErrorName = OpenViduErrorName.SCREEN_CAPTURE_DENIED; let errorName: OpenViduErrorName = OpenViduErrorName.SCREEN_CAPTURE_DENIED;
let errorMessage = 'You must allow access to one window of your desktop'; let errorMessage = 'You must allow access to one window of your desktop';
let e = new OpenViduError(errorName, errorMessage); let e = new OpenViduError(errorName, errorMessage);
@ -83,20 +98,26 @@ export class OpenViduInternal {
} }
} }
else { else {
this.camera.setVideoElement(this.cameraReady(camera!, parentId)); this.localStream.setVideoElement(this.cameraReady(localStream!, parentId));
if (this.camera.getSendAudio()) { if (this.localStream.getSendAudio()) {
// If the user wants to send audio with the screen capturing // If the user wants to send audio with the screen capturing
navigator.mediaDevices.getUserMedia({ audio: true, video: false }) navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then(userStream => { .then(userStream => {
this.camera.getMediaStream().addTrack(userStream.getAudioTracks()[0]); this.localStream.getMediaStream().addTrack(userStream.getAudioTracks()[0]);
this.camera.isScreenRequestedReady = true;
this.camera.ee.emitEvent('screen-ready'); // Mute audio if 'activeAudio' property is false
if (userStream.getAudioTracks()[0] != null) {
userStream.getAudioTracks()[0].enabled = this.localStream.outboundOptions.activeAudio;
}
this.localStream.isScreenRequestedReady = true;
this.localStream.ee.emitEvent('screen-ready');
if (callback) { if (callback) {
callback(undefined); callback(undefined);
} }
}) })
.catch(error => { .catch(error => {
this.camera.ee.emitEvent('access-denied-by-publisher'); this.localStream.ee.emitEvent('access-denied-by-publisher');
console.error("Error accessing the microphone", error); console.error("Error accessing the microphone", error);
if (callback) { if (callback) {
let errorName: OpenViduErrorName = OpenViduErrorName.MICROPHONE_ACCESS_DENIED; let errorName: OpenViduErrorName = OpenViduErrorName.MICROPHONE_ACCESS_DENIED;
@ -105,8 +126,8 @@ export class OpenViduInternal {
} }
}); });
} else { } else {
this.camera.isScreenRequestedReady = true; this.localStream.isScreenRequestedReady = true;
this.camera.ee.emitEvent('screen-ready'); this.localStream.ee.emitEvent('screen-ready');
if (callback) { if (callback) {
callback(undefined); callback(undefined);
} }
@ -114,18 +135,18 @@ export class OpenViduInternal {
} }
}); });
}); });
return this.camera; return this.localStream;
} }
cameraReady(camera: Stream, parentId: string) { cameraReady(localStream: Stream, parentId: string): HTMLVideoElement {
this.camera = camera; this.localStream = localStream;
let videoElement = this.camera.playOnlyVideo(parentId, null); let videoElement = this.localStream.playOnlyVideo(parentId, null);
this.camera.emitStreamReadyEvent(); this.localStream.emitStreamReadyEvent();
return videoElement; return videoElement;
} }
getLocalStream() { getLocalStream() {
return this.camera; return this.localStream;
} }
getRemoteStreams() { getRemoteStreams() {
@ -327,29 +348,6 @@ export class OpenViduInternal {
} }
} }
getCamera(options?) {
if (this.camera) {
return this.camera;
}
options = options || {
sendAudio: true,
sendVideo: true,
activeAudio: true,
activeVideo: true,
data: true,
mediaConstraints: {
audio: true,
video: { width: { ideal: 1280 } }
}
}
options.connection = this.session.getLocalParticipant();
this.camera = new Stream(this, true, this.session, options);
return this.camera;
};
//CHAT //CHAT
sendMessage(message) { sendMessage(message) {
this.sendRequest('sendMessage', { this.sendRequest('sendMessage', {
@ -361,26 +359,6 @@ export class OpenViduInternal {
}); });
}; };
toggleLocalVideoTrack(activate: boolean) {
this.getCamera().getWebRtcPeer().videoEnabled = activate;
}
toggleLocalAudioTrack(activate: boolean) {
this.getCamera().getWebRtcPeer().audioEnabled = activate;
}
publishLocalVideoAudio() {
this.toggleLocalVideoTrack(true);
this.toggleLocalAudioTrack(true);
}
unpublishLocalVideoAudio() {
this.toggleLocalVideoTrack(false);
this.toggleLocalAudioTrack(false);
}
generateMediaConstraints(cameraOptions: any) { generateMediaConstraints(cameraOptions: any) {
let mediaConstraints = { let mediaConstraints = {
audio: cameraOptions.audio, audio: cameraOptions.audio,

View File

@ -1,9 +1,10 @@
import { Stream } from './Stream'; import { Stream, StreamOptionsServer } from './Stream';
import { OpenViduInternal } from './OpenViduInternal'; import { OpenViduInternal } from './OpenViduInternal';
import { Connection, ConnectionOptions } from './Connection'; import { Connection, ConnectionOptions } from './Connection';
import EventEmitter = require('wolfy87-eventemitter');
import { Publisher } from '../OpenVidu/Publisher'; import { Publisher } from '../OpenVidu/Publisher';
import EventEmitter = require('wolfy87-eventemitter');
const SECRET_PARAM = '?secret='; const SECRET_PARAM = '?secret=';
export interface SessionOptions { export interface SessionOptions {
@ -119,7 +120,7 @@ export class SessionInternal {
this.connected = true; this.connected = true;
let exParticipants = response.value; let exParticipants: ConnectionOptions[] = response.value;
// IMPORTANT: Update connectionId with value send by server // IMPORTANT: Update connectionId with value send by server
this.localParticipant.connectionId = response.id; this.localParticipant.connectionId = response.id;
@ -256,19 +257,19 @@ export class SessionInternal {
}); });
} }
onParticipantPublished(options) { onParticipantPublished(response: ConnectionOptions) {
// Get the existing Connection created on 'onParticipantJoined' for // Get the existing Connection created on 'onParticipantJoined' for
// existing participants or create a new one for new participants // existing participants or create a new one for new participants
let connection = this.participants[options.id]; let connection: Connection = this.participants[response.id];
if (connection) { if (connection != null) {
// Update existing Connection // Update existing Connection
options.metadata = connection.data; response.metadata = connection.data;
connection.options = options; connection.setOptions(response);
connection.initStreams(options); connection.initRemoteStreams(response);
} else { } else {
// Create new Connection // Create new Connection
connection = new Connection(this.openVidu, false, this, options); connection = new Connection(this.openVidu, false, this, response);
} }
let pid = connection.connectionId; let pid = connection.connectionId;
@ -320,6 +321,7 @@ export class SessionInternal {
stream.dispose(); stream.dispose();
this.openVidu.getRemoteStreams().splice(index, 1); this.openVidu.getRemoteStreams().splice(index, 1);
delete this.streams[stream.streamId]; delete this.streams[stream.streamId];
connection.removeStream(stream.streamId);
} }
} else { } else {
@ -329,9 +331,9 @@ export class SessionInternal {
} }
} }
onParticipantJoined(msg) { onParticipantJoined(response: ConnectionOptions) {
let connection = new Connection(this.openVidu, false, this, msg); let connection = new Connection(this.openVidu, false, this, response);
connection.creationTime = new Date().getTime(); connection.creationTime = new Date().getTime();
let pid = connection.connectionId; let pid = connection.connectionId;
@ -574,6 +576,8 @@ export class SessionInternal {
stream.isReadyToPublish = false; stream.isReadyToPublish = false;
stream.isScreenRequestedReady = false; stream.isScreenRequestedReady = false;
delete stream.connection.getStreams()[stream.streamId];
publisher.ee.emitEvent('streamDestroyed', [{ publisher.ee.emitEvent('streamDestroyed', [{
stream: publisher.stream, stream: publisher.stream,
preventDefault: () => { this.ee.removeEvent('stream-destroyed-default'); } preventDefault: () => { this.ee.removeEvent('stream-destroyed-default'); }

View File

@ -32,17 +32,29 @@ function hide(id: string) {
document.getElementById(jq(id))!.style.display = 'none'; document.getElementById(jq(id))!.style.display = 'none';
} }
export interface StreamOptions { export interface StreamOptionsServer {
id: string;
audioActive: boolean;
videoActive: boolean;
typeOfVideo: string;
}
export interface InboundStreamOptions {
id: string; id: string;
connection: Connection; connection: Connection;
recvVideo: boolean;
recvAudio: boolean; recvAudio: boolean;
sendVideo: boolean; recvVideo: boolean;
sendAudio: boolean; typeOfVideo: string;
}
export interface OutboundStreamOptions {
activeAudio: boolean; activeAudio: boolean;
activeVideo: boolean; activeVideo: boolean;
data: boolean; connection: Connection;
dataChannel: boolean;
mediaConstraints: any; mediaConstraints: any;
sendAudio: boolean;
sendVideo: boolean;
} }
export class Stream { export class Stream {
@ -58,19 +70,13 @@ export class Stream {
private wp: any; private wp: any;
private video: HTMLVideoElement; private video: HTMLVideoElement;
private speechEvent: any; private speechEvent: any;
private recvVideo: boolean;
private recvAudio: boolean;
private sendVideo: boolean;
private sendAudio: boolean;
private mediaConstraints: any;
private showMyRemote = false; private showMyRemote = false;
private localMirrored = false; private localMirrored = false;
private chanId = 0; private chanId = 0;
private dataChannel: boolean;
private dataChannelOpened = false; private dataChannelOpened = false;
private activeAudio = true; inboundOptions: InboundStreamOptions;
private activeVideo = true; outboundOptions: OutboundStreamOptions;
private parentId: string; private parentId: string;
public isReadyToPublish: boolean = false; public isReadyToPublish: boolean = false;
@ -83,9 +89,17 @@ export class Stream {
constructor(private openVidu: OpenViduInternal, private local: boolean, private room: SessionInternal, options: any) { constructor(private openVidu: OpenViduInternal, private local: boolean, private room: SessionInternal, options: any) {
if (options !== 'screen-options') { if (options !== 'screen-options') {
this.configureOptions(options); if ('id' in options) {
this.inboundOptions = options;
} else {
this.outboundOptions = options;
}
this.streamId = (options.id != null) ? options.id : ((options.sendVideo) ? "CAMERA" : "MICRO");
this.typeOfVideo = (options.typeOfVideo != null) ? options.typeOfVideo : '';
this.connection = options.connection;
} else { } else {
this.isScreenRequested = true; this.isScreenRequested = true;
this.typeOfVideo = 'SCREEN';
this.connection = this.room.getLocalParticipant(); this.connection = this.room.getLocalParticipant();
} }
this.addEventListener('mediastream-updated', () => { this.addEventListener('mediastream-updated', () => {
@ -131,19 +145,19 @@ export class Stream {
} }
getRecvVideo() { getRecvVideo() {
return this.recvVideo; return this.inboundOptions.recvVideo;
} }
getRecvAudio() { getRecvAudio() {
return this.recvAudio; return this.inboundOptions.recvAudio;
} }
getSendVideo() { getSendVideo() {
return this.sendVideo; return this.outboundOptions.sendVideo;
} }
getSendAudio() { getSendAudio() {
return this.sendAudio; return this.outboundOptions.sendAudio;
} }
@ -174,7 +188,7 @@ export class Stream {
isDataChannelEnabled() { isDataChannelEnabled() {
return this.dataChannel; return this.outboundOptions.dataChannel;
} }
@ -318,7 +332,7 @@ export class Stream {
this.connection.addStream(this); this.connection.addStream(this);
let constraints = this.mediaConstraints; let constraints = this.outboundOptions.mediaConstraints;
/*let constraints2 = { /*let constraints2 = {
audio: true, audio: true,
@ -334,14 +348,14 @@ export class Stream {
this.userMediaHasVideo((hasVideo) => { this.userMediaHasVideo((hasVideo) => {
if (!hasVideo) { if (!hasVideo) {
if (this.sendVideo) { if (this.outboundOptions.sendVideo) {
callback(new OpenViduError(OpenViduErrorName.NO_VIDEO_DEVICE, 'You have requested camera access but there is no video input device available. Trying to connect with an audio input device only'), this); callback(new OpenViduError(OpenViduErrorName.NO_VIDEO_DEVICE, 'You have requested camera access but there is no video input device available. Trying to connect with an audio input device only'), this);
} }
if (!this.sendAudio) { if (!this.outboundOptions.sendAudio) {
callback(new OpenViduError(OpenViduErrorName.NO_INPUT_DEVICE, 'You must init Publisher object with audio or video streams enabled'), undefined); callback(new OpenViduError(OpenViduErrorName.NO_INPUT_DEVICE, 'You must init Publisher object with audio or video streams enabled'), undefined);
} else { } else {
constraints.video = false; constraints.video = false;
this.sendVideo = false; this.outboundOptions.sendVideo = false;
this.requestCameraAccesAux(constraints, callback); this.requestCameraAccesAux(constraints, callback);
} }
} else { } else {
@ -351,6 +365,7 @@ export class Stream {
} }
private requestCameraAccesAux(constraints, callback) { private requestCameraAccesAux(constraints, callback) {
console.log(constraints);
navigator.mediaDevices.getUserMedia(constraints) navigator.mediaDevices.getUserMedia(constraints)
.then(userStream => { .then(userStream => {
this.cameraAccessSuccess(userStream, callback); this.cameraAccessSuccess(userStream, callback);
@ -361,7 +376,7 @@ export class Stream {
let errorName: OpenViduErrorName; let errorName: OpenViduErrorName;
let errorMessage = error.toString();; let errorMessage = error.toString();;
if (!this.isScreenRequested) { if (!this.isScreenRequested) {
errorName = this.sendVideo ? OpenViduErrorName.CAMERA_ACCESS_DENIED : OpenViduErrorName.MICROPHONE_ACCESS_DENIED; errorName = this.outboundOptions.sendVideo ? OpenViduErrorName.CAMERA_ACCESS_DENIED : OpenViduErrorName.MICROPHONE_ACCESS_DENIED;
} else { } else {
errorName = OpenViduErrorName.SCREEN_CAPTURE_DENIED; // This code is only reachable for Firefox errorName = OpenViduErrorName.SCREEN_CAPTURE_DENIED; // This code is only reachable for Firefox
} }
@ -375,10 +390,10 @@ export class Stream {
this.ee.emitEvent('access-allowed-by-publisher'); this.ee.emitEvent('access-allowed-by-publisher');
if (userStream.getAudioTracks()[0] != null) { if (userStream.getAudioTracks()[0] != null) {
userStream.getAudioTracks()[0].enabled = this.activeAudio; userStream.getAudioTracks()[0].enabled = this.outboundOptions.activeAudio;
} }
if (userStream.getVideoTracks()[0] != null) { if (userStream.getVideoTracks()[0] != null) {
userStream.getVideoTracks()[0].enabled = this.activeVideo; userStream.getVideoTracks()[0].enabled = this.outboundOptions.activeVideo;
} }
this.mediaStream = userStream; this.mediaStream = userStream;
@ -416,9 +431,9 @@ export class Stream {
this.openVidu.sendRequest("publishVideo", { this.openVidu.sendRequest("publishVideo", {
sdpOffer: sdpOfferParam, sdpOffer: sdpOfferParam,
doLoopback: this.displayMyRemote() || false, doLoopback: this.displayMyRemote() || false,
audioActive: this.sendAudio, audioActive: this.outboundOptions.sendAudio,
videoActive: this.sendVideo, videoActive: this.outboundOptions.sendVideo,
typeOfVideo: ((this.sendVideo) ? ((this.isScreenRequested) ? 'SCREEN' :'CAMERA') : '') typeOfVideo: ((this.outboundOptions.sendVideo) ? ((this.isScreenRequested) ? 'SCREEN' :'CAMERA') : '')
}, (error, response) => { }, (error, response) => {
if (error) { if (error) {
console.error("Error on publishVideo: " + JSON.stringify(error)); console.error("Error on publishVideo: " + JSON.stringify(error));
@ -452,8 +467,8 @@ export class Stream {
if (this.local) { if (this.local) {
let userMediaConstraints = { let userMediaConstraints = {
audio: this.sendAudio, audio: this.outboundOptions.sendAudio,
video: this.sendVideo video: this.outboundOptions.sendVideo
} }
let options: any = { let options: any = {
@ -462,7 +477,7 @@ export class Stream {
onicecandidate: this.connection.sendIceCandidate.bind(this.connection), onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
} }
if (this.dataChannel) { if (this.outboundOptions.dataChannel) {
options.dataChannelConfig = { options.dataChannelConfig = {
id: this.getChannelName(), id: this.getChannelName(),
onopen: this.onDataChannelOpen, onopen: this.onDataChannelOpen,
@ -490,8 +505,8 @@ export class Stream {
this.ee.emitEvent('stream-created-by-publisher'); this.ee.emitEvent('stream-created-by-publisher');
} else { } else {
let offerConstraints = { let offerConstraints = {
audio: this.recvAudio, audio: this.inboundOptions.recvAudio,
video: this.recvVideo video: this.inboundOptions.recvVideo
}; };
console.debug("'Session.subscribe(Stream)' called. Constraints of generate SDP offer", console.debug("'Session.subscribe(Stream)' called. Constraints of generate SDP offer",
offerConstraints); offerConstraints);
@ -656,43 +671,8 @@ export class Stream {
console.info((this.local ? "Local " : "Remote ") + "'Stream' with id [" + this.streamId + "]' has been succesfully disposed"); console.info((this.local ? "Local " : "Remote ") + "'Stream' with id [" + this.streamId + "]' has been succesfully disposed");
} }
private configureOptions(options) { configureScreenOptions(options: OutboundStreamOptions) {
this.connection = options.connection; this.outboundOptions = options;
this.recvVideo = options.recvVideo || false;
this.recvAudio = options.recvAudio || false;
this.sendVideo = options.sendVideo;
this.sendAudio = options.sendAudio;
this.activeAudio = options.activeAudio;
this.activeVideo = options.activeVideo;
this.dataChannel = options.data || false;
this.mediaConstraints = options.mediaConstraints;
this.hasAudio = ((this.recvAudio || this.sendAudio) != undefined) ? (this.recvAudio || this.sendAudio) : false;
this.hasVideo = ((this.recvVideo || this.sendVideo) != undefined) ? (this.recvVideo || this.sendVideo) : false;
this.typeOfVideo = options.typeOfVideo;
if (options.id) {
this.streamId = options.id;
} else {
this.streamId = this.sendVideo ? "WEBCAM" : "MICRO";
}
}
configureScreenOptions(options) {
if (options.id) {
this.streamId = options.id;
} else {
this.streamId = "SCREEN"; this.streamId = "SCREEN";
} }
this.recvVideo = options.recvVideo || false;
this.recvAudio = options.recvAudio || false;
this.sendVideo = options.sendVideo;
this.sendAudio = options.sendAudio;
this.activeAudio = options.activeAudio;
this.activeVideo = options.activeVideo;
this.dataChannel = options.data || false;
this.mediaConstraints = options.mediaConstraints;
this.ee.emitEvent('can-request-screen');
}
} }