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. * 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. * This allows browsers/clients 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>. * 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) { public SessionProperties.Builder forcedVideoCodec(VideoCodec forcedVideoCodec) {
this.forcedVideoCodec = forcedVideoCodec; this.forcedVideoCodec = forcedVideoCodec;
@ -156,15 +157,8 @@ public class SessionProperties {
} }
/** /**
* * Call this method to define if you want to allow transcoding in the media server or not
* Call this method to define if you want to allowTranscoding or not. If you define it as * when {@link #forcedVideoCodec(VideoCodec)} is not compatible with the browser/client.
* 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.
*
*/ */
public SessionProperties.Builder allowTranscoding(boolean allowTranscoding) { public SessionProperties.Builder allowTranscoding(boolean allowTranscoding) {
this.allowTranscoding = allowTranscoding; this.allowTranscoding = allowTranscoding;
@ -270,20 +264,15 @@ public class SessionProperties {
} }
/** /**
* * Defines which video codec is being forced to be used in the browser/client
* 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>
*/ */
public VideoCodec forcedVideoCodec() { public VideoCodec forcedVideoCodec() {
return this.forcedVideoCodec; return this.forcedVideoCodec;
} }
/** /**
* * Defines if transcoding is allowed or not when {@link #forcedVideoCodec}
* Defines if transcoding is allowed or not. If this method returns <code>false</code>, a video codec * is not a compatible codec with the browser/client.
* will be forcibly used for all browsers (See
* {@link io.openvidu.java.client.SessionProperties.Builder#forcedVideoCodec(VideoCodec)}).
*/ */
public boolean isTranscodingAllowed() { public boolean isTranscodingAllowed() {
return this.allowTranscoding; return this.allowTranscoding;

View File

@ -21,5 +21,5 @@ package io.openvidu.java.client;
* See {@link io.openvidu.java.client.SessionProperties.Builder#forcedVideoCodec(VideoCodec)} * See {@link io.openvidu.java.client.SessionProperties.Builder#forcedVideoCodec(VideoCodec)}
*/ */
public enum 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 { Recording } from './Recording';
import { RecordingLayout } from './RecordingLayout'; import { RecordingLayout } from './RecordingLayout';
import { RecordingMode } from './RecordingMode'; import { RecordingMode } from './RecordingMode';
import { VideoCodec } from './VideoCodec';
/** /**
* See [[OpenVidu.createSession]] * See [[OpenVidu.createSession]]
@ -65,23 +66,20 @@ export interface SessionProperties {
*/ */
customSessionId?: string; customSessionId?: string;
/** /**
* Call this method to define which video codec do you want to be forcibly used for this session. * It defines 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. * This allows browsers/clients 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>. * If the browser/client is not compatible with the specified codec and [[allowTranscoding]]
*/ * is <code>false</code> and exception will occur.
forcedVideoCodec?: string; *
* 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 * It defines if you want to allow transcoding in the media server or not
* as <code>false</code>, the default video codec VP8 will be used for all browsers, and the media * when [[forcedVideoCodec]] is not compatible with the browser/client.
* 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.
*
*/
allowTranscoding?: boolean; allowTranscoding?: boolean;
} }

View File

@ -5,6 +5,7 @@ export enum VideoCodec {
VP8 = 'VP8', VP8 = 'VP8',
VP9 = 'VP9', 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 # (property 'OPENVIDU_SESSIONS_GARBAGE_INTERVAL' to 0) this property is ignored
OPENVIDU_SESSIONS_GARBAGE_THRESHOLD=3600 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 # Call Detail Record enabled
# Whether to enable Call Detail Record or not # Whether to enable Call Detail Record or not
# Values: true | false # Values: true | false

View File

@ -178,6 +178,13 @@ OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000
# 0 means unconstrained # 0 means unconstrained
OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300 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 # true to enable OpenVidu Webhook service. false' otherwise
# Values: true | false # Values: true | false
OPENVIDU_WEBHOOK=false OPENVIDU_WEBHOOK=false

View File

@ -37,6 +37,10 @@ import java.util.Map;
import javax.annotation.PostConstruct; 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.commons.io.FilenameUtils;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.message.BasicHeader; 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.core.env.Environment;
import org.springframework.stereotype.Component; 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.OpenViduRole;
import io.openvidu.java.client.VideoCodec;
import io.openvidu.server.OpenViduServer; import io.openvidu.server.OpenViduServer;
import io.openvidu.server.cdr.CDREventName; import io.openvidu.server.cdr.CDREventName;
import io.openvidu.server.config.Dotenv.DotenvFormatException; import io.openvidu.server.config.Dotenv.DotenvFormatException;
@ -176,6 +177,10 @@ public class OpenviduConfig {
protected int openviduSessionsGarbageThreshold; protected int openviduSessionsGarbageThreshold;
private VideoCodec openviduForcedCodec;
private boolean openviduAllowTranscoding;
private String dotenvPath; private String dotenvPath;
// Derived properties // Derived properties
@ -190,6 +195,14 @@ public class OpenviduConfig {
return this.coturnRedisDbname; return this.coturnRedisDbname;
} }
public boolean isOpenviduAllowingTranscoding() {
return openviduAllowTranscoding;
}
public VideoCodec getOpenviduForcedCodec() {
return openviduForcedCodec;
}
public String getCoturnDatabasePassword() { public String getCoturnDatabasePassword() {
return this.coturnRedisPassword; return this.coturnRedisPassword;
} }
@ -335,20 +348,20 @@ public class OpenviduConfig {
public OpenViduRole[] getRolesFromRecordingNotification() { public OpenViduRole[] getRolesFromRecordingNotification() {
OpenViduRole[] roles; OpenViduRole[] roles;
switch (this.openviduRecordingNotification) { switch (this.openviduRecordingNotification) {
case none: case none:
roles = new OpenViduRole[0]; roles = new OpenViduRole[0];
break; break;
case moderator: case moderator:
roles = new OpenViduRole[] { OpenViduRole.MODERATOR }; roles = new OpenViduRole[] { OpenViduRole.MODERATOR };
break; break;
case publisher_moderator: case publisher_moderator:
roles = new OpenViduRole[] { OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR }; roles = new OpenViduRole[] { OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR };
break; break;
case all: case all:
roles = new OpenViduRole[] { OpenViduRole.SUBSCRIBER, OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR }; roles = new OpenViduRole[] { OpenViduRole.SUBSCRIBER, OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR };
break; break;
default: default:
roles = new OpenViduRole[] { OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR }; roles = new OpenViduRole[] { OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR };
} }
return roles; return roles;
} }
@ -500,6 +513,9 @@ public class OpenviduConfig {
openviduSessionsGarbageInterval = asNonNegativeInteger("OPENVIDU_SESSIONS_GARBAGE_INTERVAL"); openviduSessionsGarbageInterval = asNonNegativeInteger("OPENVIDU_SESSIONS_GARBAGE_INTERVAL");
openviduSessionsGarbageThreshold = asNonNegativeInteger("OPENVIDU_SESSIONS_GARBAGE_THRESHOLD"); openviduSessionsGarbageThreshold = asNonNegativeInteger("OPENVIDU_SESSIONS_GARBAGE_THRESHOLD");
openviduForcedCodec = asEnumValue("OPENVIDU_FORCED_CODEC", VideoCodec.class);
openviduAllowTranscoding = asBoolean("OPENVIDU_ALLOW_TRANSCODING");
kmsUrisList = checkKmsUris(); kmsUrisList = checkKmsUris();
checkCoturnIp(); checkCoturnIp();

View File

@ -371,20 +371,26 @@ public class KurentoSessionManager extends SessionManager {
KurentoMediaOptions kurentoOptions = (KurentoMediaOptions) mediaOptions; KurentoMediaOptions kurentoOptions = (KurentoMediaOptions) mediaOptions;
KurentoParticipant kParticipant = (KurentoParticipant) participant; KurentoParticipant kParticipant = (KurentoParticipant) participant;
KurentoSession kSession = kParticipant.getSession(); KurentoSession kSession = kParticipant.getSession();
boolean isTranscodingAllowed = kSession.getSessionProperties().isTranscodingAllowed();
// Modify sdp if transcoding is not allowed VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodec();
if(!kSession.getSessionProperties().isTranscodingAllowed()) {
VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodec(); // Modify sdp if forced codec is defined
if (forcedVideoCodec != VideoCodec.NONE) {
String sdpOffer = kurentoOptions.sdpOffer; String sdpOffer = kurentoOptions.sdpOffer;
try { 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) { } catch (OpenViduException e) {
String errorMessage = "Error forcing codec: ''" + forcedVideoCodec + "', for publisher on Session: '" + kSession.getSessionId() String errorMessage = "Error forcing codec: '" + forcedVideoCodec + "', for PARTICIPANT"
+ "'\nException: " + e.getMessage() + "\nSDP:\n" + sdpOffer; + participant.getParticipantPublicId() + "' publishing in Session: '"
throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage); + 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( log.debug(
@ -418,7 +424,7 @@ public class KurentoSessionManager extends SessionManager {
} }
sdpAnswer = kParticipant.publishToRoom(kurentoOptions.sdpOffer, kurentoOptions.doLoopback, false); sdpAnswer = kParticipant.publishToRoom(kurentoOptions.sdpOffer, kurentoOptions.doLoopback, false);
if (sdpAnswer == null) { if (sdpAnswer == null) {
OpenViduException e = new OpenViduException(Code.MEDIA_SDP_ERROR_CODE, OpenViduException e = new OpenViduException(Code.MEDIA_SDP_ERROR_CODE,
"Error generating SDP response for publishing user " + participant.getParticipantPublicId()); "Error generating SDP response for publishing user " + participant.getParticipantPublicId());
@ -582,16 +588,25 @@ public class KurentoSessionManager extends SessionManager {
KurentoParticipant kParticipant = (KurentoParticipant) participant; KurentoParticipant kParticipant = (KurentoParticipant) participant;
session = ((KurentoParticipant) participant).getSession(); session = ((KurentoParticipant) participant).getSession();
Participant senderParticipant = session.getParticipantByPublicId(senderName); Participant senderParticipant = session.getParticipantByPublicId(senderName);
boolean isTranscodingAllowed = session.getSessionProperties().isTranscodingAllowed();
VideoCodec forcedVideoCodec = session.getSessionProperties().forcedVideoCodec();
// Modify sdp if transcoding is not allowed // Modify sdp if forced codec is defined
if (!session.getSessionProperties().isTranscodingAllowed()) { if (forcedVideoCodec != VideoCodec.NONE) {
VideoCodec forcedVideoCodec = session.getSessionProperties().forcedVideoCodec();
try { 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) { } 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; + 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; KurentoParticipant kParticipant = (KurentoParticipant) participant;
KurentoSession kSession = kParticipant.getSession(); KurentoSession kSession = kParticipant.getSession();
boolean isPublisher = streamId.equals(participant.getPublisherStreamId()); boolean isPublisher = streamId.equals(participant.getPublisherStreamId());
boolean isTranscodingAllowed = kSession.getSessionProperties().isTranscodingAllowed();
VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodec();
// Modify sdp if transcoding is not allowed // Modify sdp if forced codec is defined
if (!kSession.getSessionProperties().isTranscodingAllowed()) { if (forcedVideoCodec != VideoCodec.NONE) {
VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodec();
try { 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) { } catch (OpenViduException e) {
String errorMessage = "Error on reconnecting and forcing codec: ''" + forcedVideoCodec + "', for " String errorMessage = "Error in reconnect and forcing codec: '" + forcedVideoCodec + "', for PARTICIPANT: '"
+ (isPublisher ? "publisher" : "subscriber") + " on Session: '" + kSession.getSessionId() + participant.getParticipantPublicId() + "' " + (isPublisher ? "publishing" : "subscribing")
+ "'\nException: " + e.getMessage() + "\nSDP:\n" + sdpString; + " in Session: '" + kSession.getSessionId() + "'\nException: "
throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, errorMessage); + 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) { if (isPublisher) {
@ -1243,14 +1265,4 @@ public class KurentoSessionManager extends SessionManager {
filter.removeEventListener(pub.removeListener(eventType)); 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) { if (forcedVideoCodec != null) {
builder = builder.forcedVideoCodec(VideoCodec.valueOf(forcedVideoCodec)); builder = builder.forcedVideoCodec(VideoCodec.valueOf(forcedVideoCodec));
} else { } else {
builder = builder.forcedVideoCodec(VideoCodec.VP8); builder = builder.forcedVideoCodec(openviduConfig.getOpenviduForcedCodec());
} }
if (allowTranscoding != null) { if (allowTranscoding != null) {
builder = builder.allowTranscoding(allowTranscoding); builder = builder.allowTranscoding(allowTranscoding);
} else { } else {
builder = builder.allowTranscoding(false); builder = builder.allowTranscoding(openviduConfig.isOpenviduAllowingTranscoding());
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {

View File

@ -42,6 +42,9 @@ OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300
OPENVIDU_SESSIONS_GARBAGE_INTERVAL=900 OPENVIDU_SESSIONS_GARBAGE_INTERVAL=900
OPENVIDU_SESSIONS_GARBAGE_THRESHOLD=3600 OPENVIDU_SESSIONS_GARBAGE_THRESHOLD=3600
OPENVIDU_FORCED_CODEC=VP8
OPENVIDU_ALLOW_TRANSCODING=false
COTURN_REDIS_IP=127.0.0.1 COTURN_REDIS_IP=127.0.0.1
COTURN_REDIS_DBNAME=0 COTURN_REDIS_DBNAME=0
COTURN_REDIS_PASSWORD=turn COTURN_REDIS_PASSWORD=turn

View File

@ -42,7 +42,7 @@
<mat-checkbox class="checkbox-form" [(ngModel)]="sessionProperties.allowTranscoding" <mat-checkbox class="checkbox-form" [(ngModel)]="sessionProperties.allowTranscoding"
id="allow-transcoding-checkbox">Allow Transcoding</mat-checkbox> id="allow-transcoding-checkbox">Allow Transcoding</mat-checkbox>
</div> </div>
<mat-form-field *ngIf="!sessionProperties.allowTranscoding"> <mat-form-field>
<mat-select placeholder="ForcedVideoCodec" [(ngModel)]="sessionProperties.forcedVideoCodec" <mat-select placeholder="ForcedVideoCodec" [(ngModel)]="sessionProperties.forcedVideoCodec"
id="forced-video-codec-select"> id="forced-video-codec-select">
<mat-option *ngFor="let enumerator of enumToArray(forceVideoCodec)" [value]="enumerator"> <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()"> <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> <mat-icon aria-label="Publisher events" class="mat-icon material-icons" role="img" aria-hidden="true">notifications</mat-icon>
</button> </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>
<div class="bottom-div"> <div class="bottom-div">
<button class="video-btn pub-btn" title="Publish/Unpublish" (click)="pubUnpub()"> <button class="video-btn pub-btn" title="Publish/Unpublish" (click)="pubUnpub()">

View File

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