diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java index 59842409..0f5ae90f 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java @@ -144,11 +144,12 @@ public class SessionProperties { } /** - * * Call this method to define which video codec do you want to be forcibly used for this session. - * This allows browsers to use the same codec avoiding transcoding in the media server. - * To force this video codec you need to set {@link #allowTranscoding(boolean)} to false. + * This allows browsers/clients to use the same codec avoiding transcoding in the media server. + * If the browser/client is not compatible with the specified codec and {@link #allowTranscoding(boolean)} + * is false and exception will occur. * + * If forcedVideoCodec is set to NONE, no codec will be forced. */ public SessionProperties.Builder forcedVideoCodec(VideoCodec forcedVideoCodec) { this.forcedVideoCodec = forcedVideoCodec; @@ -156,15 +157,8 @@ public class SessionProperties { } /** - * - * Call this method to define if you want to allowTranscoding or not. If you define it as - * as false, the default video codec VP8 will be used for all browsers, and the media - * server will not do any transcoding. If you define it as true, transcoding can be - * executed by the media server when necessary. - * - * If you want to set a different video codec, you can configure it - * by calling {@link #forcedVideoCodec(VideoCodec)} to your preferred one. - * + * Call this method to define if you want to allow transcoding in the media server or not + * when {@link #forcedVideoCodec(VideoCodec)} is not compatible with the browser/client. */ public SessionProperties.Builder allowTranscoding(boolean allowTranscoding) { this.allowTranscoding = allowTranscoding; @@ -270,20 +264,15 @@ public class SessionProperties { } /** - * - * Defines which video codec is being forced to be used when - * {@link io.openvidu.java.client.SessionProperties.Builder#allowTranscoding(boolean)} - * has been set to false + * Defines which video codec is being forced to be used in the browser/client */ public VideoCodec forcedVideoCodec() { return this.forcedVideoCodec; } /** - * - * Defines if transcoding is allowed or not. If this method returns false, a video codec - * will be forcibly used for all browsers (See - * {@link io.openvidu.java.client.SessionProperties.Builder#forcedVideoCodec(VideoCodec)}). + * Defines if transcoding is allowed or not when {@link #forcedVideoCodec} + * is not a compatible codec with the browser/client. */ public boolean isTranscodingAllowed() { return this.allowTranscoding; diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/VideoCodec.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/VideoCodec.java index 9f6e002f..3f088fa1 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/VideoCodec.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/VideoCodec.java @@ -21,5 +21,5 @@ package io.openvidu.java.client; * See {@link io.openvidu.java.client.SessionProperties.Builder#forcedVideoCodec(VideoCodec)} */ public enum VideoCodec { - VP8, VP9, H264 + VP8, VP9, H264, NONE } \ No newline at end of file diff --git a/openvidu-node-client/src/SessionProperties.ts b/openvidu-node-client/src/SessionProperties.ts index df45ac41..91e182c8 100644 --- a/openvidu-node-client/src/SessionProperties.ts +++ b/openvidu-node-client/src/SessionProperties.ts @@ -19,6 +19,7 @@ import { MediaMode } from './MediaMode'; import { Recording } from './Recording'; import { RecordingLayout } from './RecordingLayout'; import { RecordingMode } from './RecordingMode'; +import { VideoCodec } from './VideoCodec'; /** * See [[OpenVidu.createSession]] @@ -65,23 +66,20 @@ export interface SessionProperties { */ customSessionId?: string; - /** - * Call this method to define which video codec do you want to be forcibly used for this session. - * This allows browsers to use the same codec avoiding transcoding in the media server. - * To force this video codec you need to set [[allowTranscoding]] to false. - */ - forcedVideoCodec?: string; + /** + * It defines which video codec do you want to be forcibly used for this session. + * This allows browsers/clients to use the same codec avoiding transcoding in the media server. + * If the browser/client is not compatible with the specified codec and [[allowTranscoding]] + * is false and exception will occur. + * + * If forcedVideoCodec is set to NONE, no codec will be forced. + */ + forcedVideoCodec?: VideoCodec; /** - * Call this method to define if you want to allowTranscoding or not. If you define it as - * as false, the default video codec VP8 will be used for all browsers, and the media - * server will not do any transcoding. If you define it as true, transcoding can be - * executed by the media server when necessary. - * - * If you want to set a different video codec, you can configure it - * by calling [[forcedVideoCodec]] to your preferred one. - * - */ + * It defines if you want to allow transcoding in the media server or not + * when [[forcedVideoCodec]] is not compatible with the browser/client. + */ allowTranscoding?: boolean; } diff --git a/openvidu-node-client/src/VideoCodec.ts b/openvidu-node-client/src/VideoCodec.ts index e1077f77..464bef34 100644 --- a/openvidu-node-client/src/VideoCodec.ts +++ b/openvidu-node-client/src/VideoCodec.ts @@ -5,6 +5,7 @@ export enum VideoCodec { VP8 = 'VP8', VP9 = 'VP9', - H264 = 'H264' + H264 = 'H264', + NONE = 'NONE' } \ No newline at end of file diff --git a/openvidu-server/deployments/ce/docker-compose/.env b/openvidu-server/deployments/ce/docker-compose/.env index 29ac0cb5..fe1e7693 100644 --- a/openvidu-server/deployments/ce/docker-compose/.env +++ b/openvidu-server/deployments/ce/docker-compose/.env @@ -137,6 +137,13 @@ OPENVIDU_SESSIONS_GARBAGE_INTERVAL=900 # (property 'OPENVIDU_SESSIONS_GARBAGE_INTERVAL' to 0) this property is ignored OPENVIDU_SESSIONS_GARBAGE_THRESHOLD=3600 +# All sessions of OpenVidu will try to force this codec. If OPENVIDU_ALLOW_TRANSCODING=true +# when a codec can not be forced, transcoding will be allowed +# OPENVIDU_FORCED_CODEC=VP8 + +# Allow transcoding if codec specified in OPENVIDU_FORCED_CODEC can not be applied +# OPENVIDU_ALLOW_TRANSCODING=false + # Call Detail Record enabled # Whether to enable Call Detail Record or not # Values: true | false diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env index 056cf286..458697fa 100644 --- a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env +++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env @@ -178,6 +178,13 @@ OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000 # 0 means unconstrained OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300 +# All sessions of OpenVidu will try to force this codec. If OPENVIDU_ALLOW_TRANSCODING=true +# when a codec can not be forced, transcoding will be allowed +# OPENVIDU_FORCED_CODEC=VP8 + +# Allow transcoding if codec specified in OPENVIDU_FORCED_CODEC can not be applied +# OPENVIDU_ALLOW_TRANSCODING=false + # true to enable OpenVidu Webhook service. false' otherwise # Values: true | false OPENVIDU_WEBHOOK=false 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 1beb6a41..e2b27fe0 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 @@ -37,6 +37,10 @@ import java.util.Map; import javax.annotation.PostConstruct; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonSyntaxException; + import org.apache.commons.io.FilenameUtils; import org.apache.http.Header; import org.apache.http.message.BasicHeader; @@ -48,11 +52,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonSyntaxException; - import io.openvidu.java.client.OpenViduRole; +import io.openvidu.java.client.VideoCodec; import io.openvidu.server.OpenViduServer; import io.openvidu.server.cdr.CDREventName; import io.openvidu.server.config.Dotenv.DotenvFormatException; @@ -176,6 +177,10 @@ public class OpenviduConfig { protected int openviduSessionsGarbageThreshold; + private VideoCodec openviduForcedCodec; + + private boolean openviduAllowTranscoding; + private String dotenvPath; // Derived properties @@ -190,6 +195,14 @@ public class OpenviduConfig { return this.coturnRedisDbname; } + public boolean isOpenviduAllowingTranscoding() { + return openviduAllowTranscoding; + } + + public VideoCodec getOpenviduForcedCodec() { + return openviduForcedCodec; + } + public String getCoturnDatabasePassword() { return this.coturnRedisPassword; } @@ -335,20 +348,20 @@ public class OpenviduConfig { public OpenViduRole[] getRolesFromRecordingNotification() { OpenViduRole[] roles; switch (this.openviduRecordingNotification) { - case none: - roles = new OpenViduRole[0]; - break; - case moderator: - roles = new OpenViduRole[] { OpenViduRole.MODERATOR }; - break; - case publisher_moderator: - roles = new OpenViduRole[] { OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR }; - break; - case all: - roles = new OpenViduRole[] { OpenViduRole.SUBSCRIBER, OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR }; - break; - default: - roles = new OpenViduRole[] { OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR }; + case none: + roles = new OpenViduRole[0]; + break; + case moderator: + roles = new OpenViduRole[] { OpenViduRole.MODERATOR }; + break; + case publisher_moderator: + roles = new OpenViduRole[] { OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR }; + break; + case all: + roles = new OpenViduRole[] { OpenViduRole.SUBSCRIBER, OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR }; + break; + default: + roles = new OpenViduRole[] { OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR }; } return roles; } @@ -500,6 +513,9 @@ public class OpenviduConfig { openviduSessionsGarbageInterval = asNonNegativeInteger("OPENVIDU_SESSIONS_GARBAGE_INTERVAL"); openviduSessionsGarbageThreshold = asNonNegativeInteger("OPENVIDU_SESSIONS_GARBAGE_THRESHOLD"); + openviduForcedCodec = asEnumValue("OPENVIDU_FORCED_CODEC", VideoCodec.class); + openviduAllowTranscoding = asBoolean("OPENVIDU_ALLOW_TRANSCODING"); + kmsUrisList = checkKmsUris(); checkCoturnIp(); diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java index fb4399b7..27a7fd32 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java @@ -371,20 +371,26 @@ public class KurentoSessionManager extends SessionManager { KurentoMediaOptions kurentoOptions = (KurentoMediaOptions) mediaOptions; KurentoParticipant kParticipant = (KurentoParticipant) participant; KurentoSession kSession = kParticipant.getSession(); - - // Modify sdp if transcoding is not allowed - if(!kSession.getSessionProperties().isTranscodingAllowed()) { - VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodec(); + boolean isTranscodingAllowed = kSession.getSessionProperties().isTranscodingAllowed(); + VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodec(); + + // Modify sdp if forced codec is defined + if (forcedVideoCodec != VideoCodec.NONE) { String sdpOffer = kurentoOptions.sdpOffer; - try { - kurentoOptions.sdpOffer = modifySdpToForceCodec(forcedVideoCodec, sdpOffer); + log.debug("PARTICIPANT '{}' in Session '{}' SDP Offer before munging: \n {}", + participant.getParticipantPublicId(), kSession.getSessionId(), kurentoOptions.sdpOffer); + kurentoOptions.sdpOffer = this.sdpMunging.setCodecPreference(forcedVideoCodec, sdpOffer); } catch (OpenViduException e) { - String errorMessage = "Error forcing codec: ''" + forcedVideoCodec + "', for publisher on Session: '" + kSession.getSessionId() - + "'\nException: " + e.getMessage() + "\nSDP:\n" + sdpOffer; - throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage); + String errorMessage = "Error forcing codec: '" + forcedVideoCodec + "', for PARTICIPANT" + + participant.getParticipantPublicId() + "' publishing in Session: '" + + kSession.getSessionId() + "'\nException: " + e.getMessage() + "\nSDP:\n" + sdpAnswer; + if(!isTranscodingAllowed) { + throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage); + } + log.info("Codec: '" + forcedVideoCodec + "' is not supported for PARTICIPANT: '" + participant.getParticipantPublicId() + + " publishing in Session: '" + kSession.getSessionId() + "'. Transcoding will be allowed"); } - } log.debug( @@ -418,7 +424,7 @@ public class KurentoSessionManager extends SessionManager { } sdpAnswer = kParticipant.publishToRoom(kurentoOptions.sdpOffer, kurentoOptions.doLoopback, false); - + if (sdpAnswer == null) { OpenViduException e = new OpenViduException(Code.MEDIA_SDP_ERROR_CODE, "Error generating SDP response for publishing user " + participant.getParticipantPublicId()); @@ -582,16 +588,25 @@ public class KurentoSessionManager extends SessionManager { KurentoParticipant kParticipant = (KurentoParticipant) participant; session = ((KurentoParticipant) participant).getSession(); Participant senderParticipant = session.getParticipantByPublicId(senderName); + boolean isTranscodingAllowed = session.getSessionProperties().isTranscodingAllowed(); + VideoCodec forcedVideoCodec = session.getSessionProperties().forcedVideoCodec(); - // Modify sdp if transcoding is not allowed - if (!session.getSessionProperties().isTranscodingAllowed()) { - VideoCodec forcedVideoCodec = session.getSessionProperties().forcedVideoCodec(); + // Modify sdp if forced codec is defined + if (forcedVideoCodec != VideoCodec.NONE) { try { - sdpAnswer = this.modifySdpToForceCodec(forcedVideoCodec, sdpAnswer); + log.debug("PARTICIPANT '{}' in Session '{}' SDP Answer before munging: \n {}", + participant.getParticipantPublicId(), session.getSessionId(), sdpAnswer); + sdpAnswer = this.sdpMunging.setCodecPreference(forcedVideoCodec, sdpAnswer); } catch (OpenViduException e) { - String errorMessage = "Error forcing codec: ''" + forcedVideoCodec + "', for subscriber on Session: '" + String errorMessage = "Error forcing codec: '" + forcedVideoCodec + "', for PARTICIPANT: '" + + participant.getParticipantPublicId() + "' subscribing in Session: '" + session.getSessionId() + "'\nException: " + e.getMessage() + "\nSDP:\n" + sdpAnswer; - throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage); + + if(!isTranscodingAllowed) { + throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage); + } + log.info("Codec: '" + forcedVideoCodec + "' is not supported for PARTICIPANT: '" + participant.getParticipantPublicId() + + " subscribing in Session: '" + session.getSessionId() + "'. Transcoding will be allowed"); } } @@ -1126,19 +1141,26 @@ public class KurentoSessionManager extends SessionManager { KurentoParticipant kParticipant = (KurentoParticipant) participant; KurentoSession kSession = kParticipant.getSession(); boolean isPublisher = streamId.equals(participant.getPublisherStreamId()); + boolean isTranscodingAllowed = kSession.getSessionProperties().isTranscodingAllowed(); + VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodec(); - // Modify sdp if transcoding is not allowed - if (!kSession.getSessionProperties().isTranscodingAllowed()) { - VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodec(); + // Modify sdp if forced codec is defined + if (forcedVideoCodec != VideoCodec.NONE) { try { - sdpString = modifySdpToForceCodec(forcedVideoCodec, sdpString); + log.debug("PARTICIPANT '{}' in Session '{}' reconnecting SDP before munging: \n {}", + participant.getParticipantPublicId(), kSession.getSessionId(), sdpString); + sdpString = sdpMunging.setCodecPreference(forcedVideoCodec, sdpString); } catch (OpenViduException e) { - String errorMessage = "Error on reconnecting and forcing codec: ''" + forcedVideoCodec + "', for " - + (isPublisher ? "publisher" : "subscriber") + " on Session: '" + kSession.getSessionId() - + "'\nException: " + e.getMessage() + "\nSDP:\n" + sdpString; - throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage); + String errorMessage = "Error in reconnect and forcing codec: '" + forcedVideoCodec + "', for PARTICIPANT: '" + + participant.getParticipantPublicId() + "' " + (isPublisher ? "publishing" : "subscribing") + + " in Session: '" + kSession.getSessionId() + "'\nException: " + + e.getMessage() + "\nSDP:\n" + sdpString; + if(!isTranscodingAllowed) { + throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage); + } + log.info("Codec: '" + forcedVideoCodec + "' is not supported for PARTICIPANT: '" + participant.getParticipantPublicId() + + "' " + (isPublisher ? "publishing" : "subscribing") + " in Session: '" + kSession.getSessionId() + "'. Transcoding will be allowed"); } - } if (isPublisher) { @@ -1243,14 +1265,4 @@ public class KurentoSessionManager extends SessionManager { filter.removeEventListener(pub.removeListener(eventType)); } } - - - private String modifySdpToForceCodec(VideoCodec codec, String sdpOffer) { - // Modify sdpOffer if transcoding is not allowed - String modSdpOffer = this.sdpMunging.setCodecPreference(codec, sdpOffer); - if (modSdpOffer != null) { - sdpOffer = modSdpOffer; - } - return sdpOffer; - } } diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java index e773c314..76b1470b 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java @@ -158,12 +158,12 @@ public class SessionRestController { if (forcedVideoCodec != null) { builder = builder.forcedVideoCodec(VideoCodec.valueOf(forcedVideoCodec)); } else { - builder = builder.forcedVideoCodec(VideoCodec.VP8); + builder = builder.forcedVideoCodec(openviduConfig.getOpenviduForcedCodec()); } if (allowTranscoding != null) { builder = builder.allowTranscoding(allowTranscoding); } else { - builder = builder.allowTranscoding(false); + builder = builder.allowTranscoding(openviduConfig.isOpenviduAllowingTranscoding()); } } catch (IllegalArgumentException e) { diff --git a/openvidu-server/src/main/resources/application.properties b/openvidu-server/src/main/resources/application.properties index 48b9859a..2ae45ad0 100644 --- a/openvidu-server/src/main/resources/application.properties +++ b/openvidu-server/src/main/resources/application.properties @@ -42,6 +42,9 @@ OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300 OPENVIDU_SESSIONS_GARBAGE_INTERVAL=900 OPENVIDU_SESSIONS_GARBAGE_THRESHOLD=3600 +OPENVIDU_FORCED_CODEC=VP8 +OPENVIDU_ALLOW_TRANSCODING=false + COTURN_REDIS_IP=127.0.0.1 COTURN_REDIS_DBNAME=0 COTURN_REDIS_PASSWORD=turn diff --git a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html index bf816c9b..e9eb8de6 100644 --- a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html +++ b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html @@ -42,7 +42,7 @@ Allow Transcoding - + diff --git a/openvidu-testapp/src/app/components/video/video.component.html b/openvidu-testapp/src/app/components/video/video.component.html index 5e52c0e4..7ffe5117 100644 --- a/openvidu-testapp/src/app/components/video/video.component.html +++ b/openvidu-testapp/src/app/components/video/video.component.html @@ -5,6 +5,9 @@ +