diff --git a/openvidu-browser/src/OpenVidu/Connection.ts b/openvidu-browser/src/OpenVidu/Connection.ts index 2002550b..9fa378d5 100644 --- a/openvidu-browser/src/OpenVidu/Connection.ts +++ b/openvidu-browser/src/OpenVidu/Connection.ts @@ -142,7 +142,7 @@ export class Connection { }; // TODO: CLEAN 2.15.0 LEGACY CODE // THIS LINE: - const stream = this.session.openvidu.openviduServerVersion.startsWith('2.16') ? new Stream(this.session, streamOptions) : new StreamLEGACY(this.session, streamOptions); + const stream = !this.session.openvidu.openviduServerVersion ? /*2.15.0 (LEGACY)*/ new StreamLEGACY(this.session, streamOptions) : /*2.16.0 (NORMAL API)*/ new Stream(this.session, streamOptions); // SHOULD GET BACK TO: // const stream = new Stream(this.session, streamOptions); diff --git a/openvidu-browser/src/OpenVidu/Publisher.ts b/openvidu-browser/src/OpenVidu/Publisher.ts index 4c8cabb4..4c4cdc9e 100644 --- a/openvidu-browser/src/OpenVidu/Publisher.ts +++ b/openvidu-browser/src/OpenVidu/Publisher.ts @@ -95,7 +95,7 @@ export class Publisher extends StreamManager { // TODO: CLEAN 2.15.0 LEGACY CODE // THIS LINE: - super(openvidu.openviduServerVersion.startsWith('2.16') ? new Stream((!!openvidu.session) ? openvidu.session : new Session(openvidu), { publisherProperties: properties, mediaConstraints: {} }) : new StreamLEGACY((!!openvidu.session) ? openvidu.session : new Session(openvidu), { publisherProperties: properties, mediaConstraints: {} }), targEl); + super(!openvidu.openviduServerVersion ? /*2.15.0 or 2.16.0 NOT CONNECTED (LEGACY)*/ new StreamLEGACY((!!openvidu.session) ? openvidu.session : new Session(openvidu), { publisherProperties: properties, mediaConstraints: {} }) : /*2.16.0 CONNECTED (NORMAL API)*/ new Stream(openvidu.session, { publisherProperties: properties, mediaConstraints: {} }), targEl); // SHOULD GET BACK TO: // super(new Stream((!!openvidu.session) ? openvidu.session : new Session(openvidu), { publisherProperties: properties, mediaConstraints: {} }), targEl); @@ -612,8 +612,8 @@ export class Publisher extends StreamManager { .then(myConstraints => { if (!!myConstraints.videoTrack && !!myConstraints.audioTrack || - !!myConstraints.audioTrack && myConstraints.constraints?.video === false || - !!myConstraints.videoTrack && myConstraints.constraints?.audio === false) { + !!myConstraints.audioTrack && myConstraints.constraints?.video === false || + !!myConstraints.videoTrack && myConstraints.constraints?.audio === false) { // 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 diff --git a/openvidu-browser/src/OpenVidu/Session.ts b/openvidu-browser/src/OpenVidu/Session.ts index 49a14185..52f4f245 100644 --- a/openvidu-browser/src/OpenVidu/Session.ts +++ b/openvidu-browser/src/OpenVidu/Session.ts @@ -1154,6 +1154,7 @@ export class Session extends EventDispatcher { // Initialize local Connection object with values returned by openvidu-server this.connection = new Connection(this); + this.openvidu.openviduServerVersion = response.openviduServerVersion; this.connection.connectionId = response.id; this.connection.creationTime = response.createdAt; this.connection.data = response.metadata; @@ -1312,7 +1313,6 @@ export class Session extends EventDispatcher { this.openvidu.wsUri = 'wss://' + url.host + '/openvidu'; this.openvidu.httpUri = 'https://' + url.host; - this.openvidu.openviduServerVersion = openviduServerVersion; } else { logger.error('Token "' + token + '" is not valid') diff --git a/openvidu-browser/src/OpenVidu/StreamLEGACY.ts b/openvidu-browser/src/OpenVidu/StreamLEGACY.ts index f0949665..19443a32 100644 --- a/openvidu-browser/src/OpenVidu/StreamLEGACY.ts +++ b/openvidu-browser/src/OpenVidu/StreamLEGACY.ts @@ -42,201 +42,228 @@ export class StreamLEGACY extends Stream { * @hidden */ initWebRtcPeerSend(reconnect: boolean): Promise { - return new Promise((resolve, reject) => { - if (!reconnect) { - this.initHarkEvents(); // Init hark events for the local stream - } + if (!!this.session.openvidu.openviduServerVersion) { + // 2.16.0 + return super.initWebRtcPeerSend(reconnect); - const userMediaConstraints = { - audio: this.isSendAudio(), - video: this.isSendVideo() - }; + } else { + // 2.15.0 + return new Promise((resolve, reject) => { - const options = { - mediaStream: this.mediaStream, - mediaConstraints: userMediaConstraints, - onicecandidate: this.connection.sendIceCandidate.bind(this.connection), - iceServers: this.getIceServersConf(), - simulcast: false - }; - - const successCallback = (sdpOfferParam) => { - logger.debug('Sending SDP offer to publish as ' - + this.streamId, sdpOfferParam); - - const method = reconnect ? 'reconnectStream' : 'publishVideo'; - let params; - if (reconnect) { - params = { - stream: this.streamId - } - } else { - let typeOfVideo = ''; - if (this.isSendVideo()) { - typeOfVideo = (typeof MediaStreamTrack !== 'undefined' && this.outboundStreamOpts.publisherProperties.videoSource instanceof MediaStreamTrack) ? 'CUSTOM' : (this.isSendScreen() ? 'SCREEN' : 'CAMERA'); - } - params = { - doLoopback: this.displayMyRemote() || false, - hasAudio: this.isSendAudio(), - hasVideo: this.isSendVideo(), - audioActive: this.audioActive, - videoActive: this.videoActive, - typeOfVideo, - frameRate: !!this.frameRate ? this.frameRate : -1, - videoDimensions: JSON.stringify(this.videoDimensions), - filter: this.outboundStreamOpts.publisherProperties.filter - } + if (!reconnect) { + this.initHarkEvents(); // Init hark events for the local stream } - params['sdpOffer'] = sdpOfferParam; - this.session.openvidu.sendRequest(method, params, (error, response) => { - if (error) { - if (error.code === 401) { - reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to publish")); - } else { - reject('Error on publishVideo: ' + JSON.stringify(error)); + const userMediaConstraints = { + audio: this.isSendAudio(), + video: this.isSendVideo() + }; + + const options = { + mediaStream: this.mediaStream, + mediaConstraints: userMediaConstraints, + onicecandidate: this.connection.sendIceCandidate.bind(this.connection), + iceServers: this.getIceServersConf(), + simulcast: false + }; + + const successCallback = (sdpOfferParam) => { + logger.debug('Sending SDP offer to publish as ' + + this.streamId, sdpOfferParam); + + const method = reconnect ? 'reconnectStream' : 'publishVideo'; + let params; + if (reconnect) { + params = { + stream: this.streamId } } else { - (this.webRtcPeer).processAnswer(response.sdpAnswer, false) - .then(() => { - this.streamId = response.id; - this.creationTime = response.createdAt; - this.isLocalStreamPublished = true; - this.publishedOnce = true; - if (this.displayMyRemote()) { - this.localMediaStreamWhenSubscribedToRemote = this.mediaStream; - this.remotePeerSuccessfullyEstablished(); - } - if (reconnect) { - this.ee.emitEvent('stream-reconnected-by-publisher', []); - } else { - this.ee.emitEvent('stream-created-by-publisher', []); - } - this.initWebRtcStats(); - logger.info("'Publisher' (" + this.streamId + ") successfully " + (reconnect ? "reconnected" : "published") + " to session"); - resolve(); - }) - .catch(error => { - reject(error); - }); + let typeOfVideo = ''; + if (this.isSendVideo()) { + typeOfVideo = (typeof MediaStreamTrack !== 'undefined' && this.outboundStreamOpts.publisherProperties.videoSource instanceof MediaStreamTrack) ? 'CUSTOM' : (this.isSendScreen() ? 'SCREEN' : 'CAMERA'); + } + params = { + doLoopback: this.displayMyRemote() || false, + hasAudio: this.isSendAudio(), + hasVideo: this.isSendVideo(), + audioActive: this.audioActive, + videoActive: this.videoActive, + typeOfVideo, + frameRate: !!this.frameRate ? this.frameRate : -1, + videoDimensions: JSON.stringify(this.videoDimensions), + filter: this.outboundStreamOpts.publisherProperties.filter + } } - }); - }; + params['sdpOffer'] = sdpOfferParam; - if (reconnect) { - this.disposeWebRtcPeer(); - } - if (this.displayMyRemote()) { - this.webRtcPeer = new WebRtcPeerSendrecvLEGACY(options); - } else { - this.webRtcPeer = new WebRtcPeerSendonlyLEGACY(options); - } - this.webRtcPeer.addIceConnectionStateChangeListener('publisher of ' + this.connection.connectionId); - (this.webRtcPeer).generateOffer().then(sdpOffer => { - successCallback(sdpOffer); - }).catch(error => { - reject(new Error('(publish) SDP offer error: ' + JSON.stringify(error))); + this.session.openvidu.sendRequest(method, params, (error, response) => { + if (error) { + if (error.code === 401) { + reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to publish")); + } else { + reject('Error on publishVideo: ' + JSON.stringify(error)); + } + } else { + (this.webRtcPeer).processAnswer(response.sdpAnswer, false) + .then(() => { + this.streamId = response.id; + this.creationTime = response.createdAt; + this.isLocalStreamPublished = true; + this.publishedOnce = true; + if (this.displayMyRemote()) { + this.localMediaStreamWhenSubscribedToRemote = this.mediaStream; + this.remotePeerSuccessfullyEstablished(); + } + if (reconnect) { + this.ee.emitEvent('stream-reconnected-by-publisher', []); + } else { + this.ee.emitEvent('stream-created-by-publisher', []); + } + this.initWebRtcStats(); + logger.info("'Publisher' (" + this.streamId + ") successfully " + (reconnect ? "reconnected" : "published") + " to session"); + resolve(); + }) + .catch(error => { + reject(error); + }); + } + }); + }; + + if (reconnect) { + this.disposeWebRtcPeer(); + } + if (this.displayMyRemote()) { + this.webRtcPeer = new WebRtcPeerSendrecvLEGACY(options); + } else { + this.webRtcPeer = new WebRtcPeerSendonlyLEGACY(options); + } + this.webRtcPeer.addIceConnectionStateChangeListener('publisher of ' + this.connection.connectionId); + (this.webRtcPeer).generateOffer().then(sdpOffer => { + successCallback(sdpOffer); + }).catch(error => { + reject(new Error('(publish) SDP offer error: ' + JSON.stringify(error))); + }); }); - }); + + } } /** * @hidden */ initWebRtcPeerReceive(reconnect: boolean): Promise { - return new Promise((resolve, reject) => { - const offerConstraints = { - audio: this.inboundStreamOpts.hasAudio, - video: this.inboundStreamOpts.hasVideo - }; - logger.debug("'Session.subscribe(Stream)' called. Constraints of generate SDP offer", - offerConstraints); - const options = { - onicecandidate: this.connection.sendIceCandidate.bind(this.connection), - mediaConstraints: offerConstraints, - iceServers: this.getIceServersConf(), - simulcast: false - }; + if (!!this.session.openvidu.openviduServerVersion) { + // 2.16.0 + return super.initWebRtcPeerReceive(reconnect); - const successCallback = (sdpOfferParam) => { - logger.debug('Sending SDP offer to subscribe to ' - + this.streamId, sdpOfferParam); + } else { + // 2.15.0 + return new Promise((resolve, reject) => { - const method = reconnect ? 'reconnectStream' : 'receiveVideoFrom'; - const params = { sdpOffer: sdpOfferParam }; - params[reconnect ? 'stream' : 'sender'] = this.streamId; + const offerConstraints = { + audio: this.inboundStreamOpts.hasAudio, + video: this.inboundStreamOpts.hasVideo + }; + logger.debug("'Session.subscribe(Stream)' called. Constraints of generate SDP offer", + offerConstraints); + const options = { + onicecandidate: this.connection.sendIceCandidate.bind(this.connection), + mediaConstraints: offerConstraints, + iceServers: this.getIceServersConf(), + simulcast: false + }; - this.session.openvidu.sendRequest(method, params, (error, response) => { - if (error) { - reject(new Error('Error on recvVideoFrom: ' + JSON.stringify(error))); - } else { - // Ios Ionic. Limitation: some bug in iosrtc cordova plugin makes it necessary - // to add a timeout before calling PeerConnection#setRemoteDescription during - // some time (400 ms) from the moment first subscriber stream is received - if (this.session.isFirstIonicIosSubscriber) { - this.session.isFirstIonicIosSubscriber = false; - setTimeout(() => { - // After 400 ms Ionic iOS subscribers won't need to run - // PeerConnection#setRemoteDescription after 250 ms timeout anymore - this.session.countDownForIonicIosSubscribersActive = false; - }, 400); + const successCallback = (sdpOfferParam) => { + logger.debug('Sending SDP offer to subscribe to ' + + this.streamId, sdpOfferParam); + + const method = reconnect ? 'reconnectStream' : 'receiveVideoFrom'; + const params = { sdpOffer: sdpOfferParam }; + params[reconnect ? 'stream' : 'sender'] = this.streamId; + + this.session.openvidu.sendRequest(method, params, (error, response) => { + if (error) { + reject(new Error('Error on recvVideoFrom: ' + JSON.stringify(error))); + } else { + // Ios Ionic. Limitation: some bug in iosrtc cordova plugin makes it necessary + // to add a timeout before calling PeerConnection#setRemoteDescription during + // some time (400 ms) from the moment first subscriber stream is received + if (this.session.isFirstIonicIosSubscriber) { + this.session.isFirstIonicIosSubscriber = false; + setTimeout(() => { + // After 400 ms Ionic iOS subscribers won't need to run + // PeerConnection#setRemoteDescription after 250 ms timeout anymore + this.session.countDownForIonicIosSubscribersActive = false; + }, 400); + } + const needsTimeoutOnProcessAnswer = this.session.countDownForIonicIosSubscribersActive; + (this.webRtcPeer).processAnswer(response.sdpAnswer, needsTimeoutOnProcessAnswer).then(() => { + logger.info("'Subscriber' (" + this.streamId + ") successfully " + (reconnect ? "reconnected" : "subscribed")); + this.remotePeerSuccessfullyEstablished(); + this.initWebRtcStats(); + resolve(); + }).catch(error => { + reject(error); + }); } - const needsTimeoutOnProcessAnswer = this.session.countDownForIonicIosSubscribersActive; - (this.webRtcPeer).processAnswer(response.sdpAnswer, needsTimeoutOnProcessAnswer).then(() => { - logger.info("'Subscriber' (" + this.streamId + ") successfully " + (reconnect ? "reconnected" : "subscribed")); - this.remotePeerSuccessfullyEstablished(); - this.initWebRtcStats(); - resolve(); - }).catch(error => { - reject(error); - }); - } - }); - }; + }); + }; - this.webRtcPeer = new WebRtcPeerRecvonlyLEGACY(options); - this.webRtcPeer.addIceConnectionStateChangeListener(this.streamId); - (this.webRtcPeer).generateOffer() - .then(sdpOffer => { - successCallback(sdpOffer); - }) - .catch(error => { - reject(new Error('(subscribe) SDP offer error: ' + JSON.stringify(error))); - }); - }); + this.webRtcPeer = new WebRtcPeerRecvonlyLEGACY(options); + this.webRtcPeer.addIceConnectionStateChangeListener(this.streamId); + (this.webRtcPeer).generateOffer() + .then(sdpOffer => { + successCallback(sdpOffer); + }) + .catch(error => { + reject(new Error('(subscribe) SDP offer error: ' + JSON.stringify(error))); + }); + }); + + } } /** * @hidden */ remotePeerSuccessfullyEstablished(): void { - this.mediaStream = new MediaStream(); - let receiver: RTCRtpReceiver; - for (receiver of this.webRtcPeer.pc.getReceivers()) { - if (!!receiver.track) { - this.mediaStream.addTrack(receiver.track); - } - } - logger.debug('Peer remote stream', this.mediaStream); - if (!!this.mediaStream) { + if (!!this.session.openvidu.openviduServerVersion) { + // 2.16.0 + super.remotePeerSuccessfullyEstablished(); - if (this.streamManager instanceof Subscriber) { - // Apply SubscriberProperties.subscribeToAudio and SubscriberProperties.subscribeToVideo - if (!!this.mediaStream.getAudioTracks()[0]) { - const enabled = !!((this.streamManager).properties.subscribeToAudio); - this.mediaStream.getAudioTracks()[0].enabled = enabled; - } - if (!!this.mediaStream.getVideoTracks()[0]) { - const enabled = !!((this.streamManager).properties.subscribeToVideo); - this.mediaStream.getVideoTracks()[0].enabled = enabled; + } else { + // 2.15.0 + this.mediaStream = new MediaStream(); + let receiver: RTCRtpReceiver; + for (receiver of this.webRtcPeer.pc.getReceivers()) { + if (!!receiver.track) { + this.mediaStream.addTrack(receiver.track); } } + logger.debug('Peer remote stream', this.mediaStream); + + if (!!this.mediaStream) { + + if (this.streamManager instanceof Subscriber) { + // Apply SubscriberProperties.subscribeToAudio and SubscriberProperties.subscribeToVideo + if (!!this.mediaStream.getAudioTracks()[0]) { + const enabled = !!((this.streamManager).properties.subscribeToAudio); + this.mediaStream.getAudioTracks()[0].enabled = enabled; + } + if (!!this.mediaStream.getVideoTracks()[0]) { + const enabled = !!((this.streamManager).properties.subscribeToVideo); + this.mediaStream.getVideoTracks()[0].enabled = enabled; + } + } + + this.updateMediaStreamInVideos(); + this.initHarkEvents(); // Init hark events for the remote stream + } - this.updateMediaStreamInVideos(); - this.initHarkEvents(); // Init hark events for the remote stream } } diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java b/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java index 676489d8..f27a9e78 100644 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java +++ b/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java @@ -41,6 +41,7 @@ public class ProtocolElements { public static final String JOINROOM_RECORDER_PARAM = "recorder"; public static final String JOINROOM_PEERID_PARAM = "id"; + public static final String JOINROOM_OPENVIDUSERVERVERSION_PARAM = "openviduServerVersion"; public static final String JOINROOM_PEERCREATEDAT_PARAM = "createdAt"; public static final String JOINROOM_PEERSTREAMS_PARAM = "streams"; public static final String JOINROOM_PEERSTREAMID_PARAM = "id"; diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java index 34609036..48c6b555 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java @@ -40,6 +40,7 @@ import io.openvidu.client.internal.ProtocolElements; import io.openvidu.java.client.OpenViduRole; import io.openvidu.server.cdr.CallDetailRecord; import io.openvidu.server.config.InfoHandler; +import io.openvidu.server.config.OpenviduBuildInfo; import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.kurento.core.KurentoParticipant; import io.openvidu.server.kurento.endpoint.KurentoFilter; @@ -62,6 +63,9 @@ public class SessionEventsHandler { @Autowired protected OpenviduConfig openviduConfig; + @Autowired + protected OpenviduBuildInfo openviduBuildConfig; + Map recordingsStarted = new ConcurrentHashMap<>(); ReentrantLock lock = new ReentrantLock(); @@ -156,6 +160,8 @@ public class SessionEventsHandler { result.addProperty(ProtocolElements.PARTICIPANTJOINED_USER_PARAM, participant.getParticipantPublicId()); result.addProperty(ProtocolElements.PARTICIPANTJOINED_CREATEDAT_PARAM, participant.getCreatedAt()); result.addProperty(ProtocolElements.PARTICIPANTJOINED_METADATA_PARAM, participant.getFullMetadata()); + result.addProperty(ProtocolElements.JOINROOM_OPENVIDUSERVERVERSION_PARAM, + openviduBuildConfig.getOpenViduServerVersion()); result.add("value", resultArray); rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, result);