Fixed forceVideoCodec and allowTranscoding properties. Add debug logs for SDP

pull/600/head
cruizba 2020-09-04 17:33:12 +02:00
parent b399e686ed
commit 0f958baf39
13 changed files with 137 additions and 94 deletions

View File

@ -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 <code>false</code>.
* 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 <code>false</code> 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 <code>false</code>, 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 <code>true</code>, 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 <code>false</code>
* 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 <code>false</code>, 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;

View File

@ -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
}

View File

@ -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 <code>false</code>.
*/
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 <code>false</code> 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 <code>false</code>, 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 <code>true</code>, 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;
}

View File

@ -5,6 +5,7 @@ export enum VideoCodec {
VP8 = 'VP8',
VP9 = 'VP9',
H264 = 'H264'
H264 = 'H264',
NONE = 'NONE'
}

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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

View File

@ -42,7 +42,7 @@
<mat-checkbox class="checkbox-form" [(ngModel)]="sessionProperties.allowTranscoding"
id="allow-transcoding-checkbox">Allow Transcoding</mat-checkbox>
</div>
<mat-form-field *ngIf="!sessionProperties.allowTranscoding">
<mat-form-field>
<mat-select placeholder="ForcedVideoCodec" [(ngModel)]="sessionProperties.forcedVideoCodec"
id="forced-video-codec-select">
<mat-option *ngFor="let enumerator of enumToArray(forceVideoCodec)" [value]="enumerator">

View File

@ -5,6 +5,9 @@
<button class="video-btn events-btn bottom-left-rounded" title="Publisher events" (click)="openPublisherEventsDialog()">
<mat-icon aria-label="Publisher events" class="mat-icon material-icons" role="img" aria-hidden="true">notifications</mat-icon>
</button>
<button class="video-btn events-btn bottom-left-rounded" title="Peer Connection Stats" (click)="showStats()">
<mat-icon aria-label="Peer Connection Stats" class="mat-icon material-icons" role="img" aria-hidden="true">info</mat-icon>
</button>
</div>
<div class="bottom-div">
<button class="video-btn pub-btn" title="Publish/Unpublish" (click)="pubUnpub()">

View File

@ -726,5 +726,12 @@ export class VideoComponent implements OnInit, OnDestroy {
event
});
}
async showStats() {
let stats = await this.streamManager.stream.getWebRtcPeer().pc.getStats(null);
stats.forEach(report => {
console.log(report);
})
}
}