diff --git a/openvidu-browser/src/OpenVidu/OpenVidu.ts b/openvidu-browser/src/OpenVidu/OpenVidu.ts index e2de4ff6..34bdeea1 100644 --- a/openvidu-browser/src/OpenVidu/OpenVidu.ts +++ b/openvidu-browser/src/OpenVidu/OpenVidu.ts @@ -109,6 +109,10 @@ export class OpenVidu { * @hidden */ mediaServer: string; + /** + * @hidden + */ + videoSimulcast: boolean; /** * @hidden */ @@ -257,6 +261,7 @@ export class OpenVidu { publishVideo: (typeof properties.publishVideo !== 'undefined') ? properties.publishVideo : true, resolution: (typeof MediaStreamTrack !== 'undefined' && properties.videoSource instanceof MediaStreamTrack) ? undefined : ((typeof properties.resolution !== 'undefined') ? properties.resolution : '640x480'), videoSource: (typeof properties.videoSource !== 'undefined') ? properties.videoSource : undefined, + videoSimulcast: properties.videoSimulcast, filter: properties.filter }; } else { diff --git a/openvidu-browser/src/OpenVidu/Session.ts b/openvidu-browser/src/OpenVidu/Session.ts index ad243837..b25c7e10 100644 --- a/openvidu-browser/src/OpenVidu/Session.ts +++ b/openvidu-browser/src/OpenVidu/Session.ts @@ -1540,6 +1540,7 @@ export class Session extends EventDispatcher { this.openvidu.role = opts.role; this.openvidu.finalUserId = opts.finalUserId; this.openvidu.mediaServer = opts.mediaServer; + this.openvidu.videoSimulcast = opts.videoSimulcast; this.capabilities = { subscribe: true, publish: this.openvidu.role !== 'SUBSCRIBER', diff --git a/openvidu-browser/src/OpenVidu/Stream.ts b/openvidu-browser/src/OpenVidu/Stream.ts index 16d8c9b2..ae5cf795 100644 --- a/openvidu-browser/src/OpenVidu/Stream.ts +++ b/openvidu-browser/src/OpenVidu/Stream.ts @@ -938,7 +938,8 @@ export class Stream { audio: this.hasAudio, video: this.hasVideo, }, - simulcast: this.session.openvidu.advancedConfiguration.enableSimulcastExperimental || false, + simulcast: + this.outboundStreamOpts.publisherProperties.videoSimulcast ?? this.session.openvidu.videoSimulcast, onIceCandidate: this.connection.sendIceCandidate.bind(this.connection), onIceConnectionStateException: (exceptionName: ExceptionEventName, message: string, data?: any) => { this.session.emitEvent('exception', [new ExceptionEvent(this.session, exceptionName, this, message, data)]) }, iceServers: this.getIceServersConf(), @@ -946,6 +947,11 @@ export class Stream { mediaServer: this.session.openvidu.mediaServer }; + if (this.session.openvidu.mediaServer !== 'mediasoup') { + // Simulcast is only supported by mediasoup + config.simulcast = false; + } + if (reconnect) { this.disposeWebRtcPeer(); } diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/LocalConnectionOptions.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/LocalConnectionOptions.ts index bc8d2ab3..8520aad1 100644 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Private/LocalConnectionOptions.ts +++ b/openvidu-browser/src/OpenViduInternal/Interfaces/Private/LocalConnectionOptions.ts @@ -33,5 +33,6 @@ export interface LocalConnectionOptions { turnCredential: string; version: string; mediaServer: string; + videoSimulcast: boolean; life: number; -} \ No newline at end of file +} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration.ts index 2bc8f046..7327b3c2 100644 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration.ts +++ b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration.ts @@ -71,11 +71,4 @@ export interface OpenViduAdvancedConfiguration { */ noStreamPlayingEventExceptionTimeout?: number; - /** - * Whether to enable simulcast for Publishers or not. - * - * Default to `false`. - */ - enableSimulcastExperimental?: boolean; - -} \ No newline at end of file +} diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/PublisherProperties.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/PublisherProperties.ts index 2ef5df08..dbe0c715 100644 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/PublisherProperties.ts +++ b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/PublisherProperties.ts @@ -82,6 +82,16 @@ export interface PublisherProperties { */ videoSource?: string | MediaStreamTrack | boolean; + /** + * Send Simulcast video. + * Publishers will encode duplicate video streams with different qualities, + * so the media server is able to select the most appropriate quality stream + * for each Subscriber. + * This setting is honored only if OpenVidu Server was configured to use the + * mediasoup media server. Otherwise, Simulcast will be disabled. + */ + videoSimulcast?: boolean; + /** * **WARNING**: experimental option. This property may change in the near future * @@ -89,4 +99,4 @@ export interface PublisherProperties { */ filter?: Filter; -} \ No newline at end of file +} 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 4e917016..85a327d5 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 @@ -160,6 +160,7 @@ public class ProtocolElements { public static final String PARTICIPANTJOINED_SESSION_PARAM = "session"; public static final String PARTICIPANTJOINED_VERSION_PARAM = "version"; public static final String PARTICIPANTJOINED_MEDIASERVER_PARAM = "mediaServer"; + public static final String PARTICIPANTJOINED_SIMULCAST_PARAM = "videoSimulcast"; public static final String PARTICIPANTJOINED_RECORD_PARAM = "record"; public static final String PARTICIPANTJOINED_ROLE_PARAM = "role"; public static final String PARTICIPANTJOINED_COTURNIP_PARAM = "coturnIp"; diff --git a/openvidu-server/deployments/enterprise/master-node/.env b/openvidu-server/deployments/enterprise/master-node/.env index ace47e9f..1db77389 100644 --- a/openvidu-server/deployments/enterprise/master-node/.env +++ b/openvidu-server/deployments/enterprise/master-node/.env @@ -247,6 +247,16 @@ OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000 # 0 means unconstrained OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300 +# Send Simulcast video. +# Publishers will encode duplicate video streams with different qualities, +# so the media server is able to select the most appropriate quality stream +# for each Subscriber. +# This setting is honored only if OpenVidu Server was configured to use the +# mediasoup media server. Otherwise, Simulcast will be disabled. +# Values: true | false +# Default: true +#OPENVIDU_STREAMS_VIDEO_SIMULCAST=true + # All sessions of OpenVidu will try to force this codec. If OPENVIDU_STREAMS_ALLOW_TRANSCODING=true # when a codec can not be forced, transcoding will be allowed # Values: VP8, VP9, H264, NONE diff --git a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java index dede7ea0..47fc14b1 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java +++ b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java @@ -217,8 +217,13 @@ public class OpenviduConfig { private boolean isTurnadminAvailable = false; // Media Server properties + private MediaServer mediaServerInfo = MediaServer.kurento; + // Media properties + + private boolean streamsVideoSimulcast = false; + // Plain config properties getters public String getCoturnDatabaseDbname() { @@ -281,6 +286,10 @@ public class OpenviduConfig { this.mediaServerInfo = mediaServerInfo; } + public boolean isStreamsVideoSimulcast() { + return this.streamsVideoSimulcast; + } + public String getOpenViduRecordingPath() { return this.openviduRecordingPath; } 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 3c54b939..2ec4fd68 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 @@ -165,6 +165,19 @@ public class SessionEventsHandler { result.addProperty(ProtocolElements.PARTICIPANTJOINED_MEDIASERVER_PARAM, this.openviduConfig.getMediaServer().name()); + switch (this.openviduConfig.getMediaServer()) { + case mediasoup: + // mediasoup supports simulcast + result.addProperty(ProtocolElements.PARTICIPANTJOINED_SIMULCAST_PARAM, + this.openviduConfig.isStreamsVideoSimulcast()); + break; + case kurento: + default: + // Kurento does not support simulcast + result.addProperty(ProtocolElements.PARTICIPANTJOINED_SIMULCAST_PARAM, false); + break; + } + if (participant.getToken() != null) { result.addProperty(ProtocolElements.PARTICIPANTJOINED_RECORD_PARAM, participant.getToken().record()); if (participant.getToken().getRole() != null) { diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/ConfigRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/ConfigRestController.java index 9c1d1e2f..3f3edc39 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rest/ConfigRestController.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rest/ConfigRestController.java @@ -114,6 +114,7 @@ public class ConfigRestController { json.addProperty("OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH", openviduConfig.getVideoMinRecvBandwidth()); json.addProperty("OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH", openviduConfig.getVideoMaxSendBandwidth()); json.addProperty("OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH", openviduConfig.getVideoMinSendBandwidth()); + json.addProperty("OPENVIDU_STREAMS_VIDEO_SIMULCAST", openviduConfig.isStreamsVideoSimulcast()); json.addProperty("OPENVIDU_STREAMS_FORCED_VIDEO_CODEC", openviduConfig.getOpenviduForcedCodec().name()); json.addProperty("OPENVIDU_STREAMS_ALLOW_TRANSCODING", openviduConfig.isOpenviduAllowingTranscoding()); json.addProperty("OPENVIDU_SESSIONS_GARBAGE_INTERVAL", openviduConfig.getSessionGarbageInterval()); diff --git a/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 6ba031d8..e34d6a5e 100644 --- a/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -147,6 +147,12 @@ "description": "Minimum video bandwidth sent from OpenVidu Server to clients, in kbps. 0 means unconstrained", "defaultValue": 300 }, + { + "name": "OPENVIDU_STREAMS_VIDEO_SIMULCAST", + "type": "java.lang.Boolean", + "description": "Send Simulcast video.", + "defaultValue": true + }, { "name": "OPENVIDU_STREAMS_FORCED_VIDEO_CODEC", "type": "java.lang.String", diff --git a/openvidu-server/src/main/resources/application.properties b/openvidu-server/src/main/resources/application.properties index fe3076e7..6311b88a 100644 --- a/openvidu-server/src/main/resources/application.properties +++ b/openvidu-server/src/main/resources/application.properties @@ -42,6 +42,7 @@ OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH=1000 OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH=300 OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000 OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300 +OPENVIDU_STREAMS_VIDEO_SIMULCAST=true OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8 OPENVIDU_STREAMS_ALLOW_TRANSCODING=false diff --git a/openvidu-testapp/src/app/components/dialogs/publisher-properties-dialog/publisher-properties-dialog.component.html b/openvidu-testapp/src/app/components/dialogs/publisher-properties-dialog/publisher-properties-dialog.component.html index 816c8b6b..2044560c 100644 --- a/openvidu-testapp/src/app/components/dialogs/publisher-properties-dialog/publisher-properties-dialog.component.html +++ b/openvidu-testapp/src/app/components/dialogs/publisher-properties-dialog/publisher-properties-dialog.component.html @@ -8,6 +8,7 @@ Publish audio Publish video Mirror + Video Simulcast @@ -52,4 +53,4 @@ - \ No newline at end of file + diff --git a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts index dc46397e..0c8d8a1e 100644 --- a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts +++ b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts @@ -121,7 +121,8 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { resolution: '640x480', mirror: true, publishAudio: true, - publishVideo: true + publishVideo: true, + videoSimulcast: true }; publisherPropertiesAux: PublisherProperties; @@ -230,9 +231,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { this.OV = new OpenVidu(); - const advancedConfiguration: OpenViduAdvancedConfiguration = { - enableSimulcastExperimental: false - }; + const advancedConfiguration: OpenViduAdvancedConfiguration = {}; if (this.turnConf === 'freeice') { advancedConfiguration.iceServers = 'freeice'; } else if (this.turnConf === 'manual') {