mirror of https://github.com/OpenVidu/openvidu.git
openvidu-browser: Refactored webRtcStats
parent
a5e022e440
commit
e545fd1630
|
@ -34,16 +34,45 @@ interface WebrtcStatsConfig {
|
||||||
httpEndpoint: string
|
httpEndpoint: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface JSONStats {
|
interface JSONStatsResponse {
|
||||||
'@timestamp': string,
|
'@timestamp': string,
|
||||||
participant_id: string,
|
participant_id: string,
|
||||||
session_id: string,
|
session_id: string,
|
||||||
platform: string,
|
platform: string,
|
||||||
platform_description: string,
|
platform_description: string,
|
||||||
stream: string,
|
stream: string,
|
||||||
webrtc_stats: RTCStatsReport
|
webrtc_stats: IWebrtcStats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IWebrtcStats {
|
||||||
|
inbound: {
|
||||||
|
audio: {
|
||||||
|
bytesReceived: number,
|
||||||
|
packetsReceived: number,
|
||||||
|
packetsLost: number
|
||||||
|
} | {},
|
||||||
|
video: {
|
||||||
|
bytesReceived: number,
|
||||||
|
packetsReceived: number,
|
||||||
|
packetsLost: number,
|
||||||
|
framesDecoded: number,
|
||||||
|
nackCount: number
|
||||||
|
} | {}
|
||||||
|
} | {},
|
||||||
|
outbound: {
|
||||||
|
audio: {
|
||||||
|
bytesSent: number,
|
||||||
|
packetsSent: number,
|
||||||
|
} | {},
|
||||||
|
video: {
|
||||||
|
bytesSent: number,
|
||||||
|
packetsSent: number,
|
||||||
|
framesEncoded: number,
|
||||||
|
nackCount: number
|
||||||
|
} | {}
|
||||||
|
} | {}
|
||||||
|
};
|
||||||
|
|
||||||
export class WebRtcStats {
|
export class WebRtcStats {
|
||||||
|
|
||||||
private readonly STATS_ITEM_NAME = 'webrtc-stats-config';
|
private readonly STATS_ITEM_NAME = 'webrtc-stats-config';
|
||||||
|
@ -66,16 +95,15 @@ export class WebRtcStats {
|
||||||
const webrtcObj = localStorage.getItem(this.STATS_ITEM_NAME);
|
const webrtcObj = localStorage.getItem(this.STATS_ITEM_NAME);
|
||||||
|
|
||||||
if (!!webrtcObj) {
|
if (!!webrtcObj) {
|
||||||
|
|
||||||
this.webRtcStatsEnabled = true;
|
this.webRtcStatsEnabled = true;
|
||||||
const webrtcStatsConfig: WebrtcStatsConfig = JSON.parse(webrtcObj);
|
const webrtcStatsConfig: WebrtcStatsConfig = JSON.parse(webrtcObj);
|
||||||
this.POST_URL = webrtcStatsConfig.httpEndpoint;
|
|
||||||
this.statsInterval = webrtcStatsConfig.interval; // Interval in seconds
|
|
||||||
|
|
||||||
// webrtc object found in local storage
|
// webrtc object found in local storage
|
||||||
logger.warn('WebRtc stats enabled for stream ' + this.stream.streamId + ' of connection ' + this.stream.connection.connectionId);
|
logger.warn('WebRtc stats enabled for stream ' + this.stream.streamId + ' of connection ' + this.stream.connection.connectionId);
|
||||||
logger.warn('localStorage item: ' + JSON.stringify(webrtcStatsConfig));
|
logger.warn('localStorage item: ' + JSON.stringify(webrtcStatsConfig));
|
||||||
|
|
||||||
|
this.POST_URL = webrtcStatsConfig.httpEndpoint;
|
||||||
|
this.statsInterval = webrtcStatsConfig.interval; // Interval in seconds
|
||||||
|
|
||||||
this.webRtcStatsIntervalId = setInterval(async () => {
|
this.webRtcStatsIntervalId = setInterval(async () => {
|
||||||
await this.sendStatsToHttpEndpoint();
|
await this.sendStatsToHttpEndpoint();
|
||||||
}, this.statsInterval * 1000);
|
}, this.statsInterval * 1000);
|
||||||
|
@ -155,13 +183,13 @@ export class WebRtcStats {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sendStats(url: string, json: JSONStats): Promise<void> {
|
private async sendStats(url: string, response: JSONStatsResponse): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const configuration: RequestInit = {
|
const configuration: RequestInit = {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-type': 'application/json'
|
'Content-type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify(json),
|
body: JSON.stringify(response),
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
};
|
};
|
||||||
await fetch(url, configuration);
|
await fetch(url, configuration);
|
||||||
|
@ -173,34 +201,88 @@ export class WebRtcStats {
|
||||||
|
|
||||||
private async sendStatsToHttpEndpoint(): Promise<void> {
|
private async sendStatsToHttpEndpoint(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const stats: RTCStatsReport = await this.getStats();
|
const webrtcStats: IWebrtcStats = await this.getStats();
|
||||||
const json = this.generateJSONStats(stats);
|
const response = this.generateJSONStatsResponse(webrtcStats);
|
||||||
// this.parseAndSendStats(stats);
|
await this.sendStats(this.POST_URL, response);
|
||||||
await this.sendStats(this.POST_URL, json);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log(error);
|
logger.log(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getStats(): Promise<any> {
|
private async getStats(): Promise<IWebrtcStats> {
|
||||||
|
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) {
|
if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) {
|
||||||
|
|
||||||
const pc: any = this.stream.getRTCPeerConnection();
|
const pc: any = this.stream.getRTCPeerConnection();
|
||||||
pc.getStats((statsReport) => {
|
pc.getStats((statsReport) => {
|
||||||
resolve(this.standardizeReport(statsReport));
|
const stats = statsReport.result().filter((stat) => stat.type === 'ssrc');
|
||||||
|
const response = this.initWebRtcStatsResponse();
|
||||||
|
|
||||||
|
stats.forEach(stat => {
|
||||||
|
const valueNames: string[] = stat.names();
|
||||||
|
const mediaType = stat.stat("mediaType");
|
||||||
|
const isAudio = mediaType === 'audio' && valueNames.includes('audioOutputLevel');
|
||||||
|
const isVideo = mediaType === 'video' && valueNames.includes('qpSum');
|
||||||
|
const isIndoundRtp = valueNames.includes('bytesReceived') && (isAudio || isVideo);
|
||||||
|
const isOutboundRtp = valueNames.includes('bytesSent');
|
||||||
|
|
||||||
|
if(isIndoundRtp){
|
||||||
|
response.inbound[mediaType].bytesReceived = stat.stat('bytesReceived');
|
||||||
|
response.inbound[mediaType].packetsReceived = stat.stat('packetsReceived');
|
||||||
|
response.inbound[mediaType].packetsLost = stat.stat('packetsLost');
|
||||||
|
if(mediaType === 'video'){
|
||||||
|
response.inbound['video'].framesDecoded = stat.stat('framesDecoded');
|
||||||
|
response.inbound['video'].nackCount = stat.stat('nackCount');
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(isOutboundRtp) {
|
||||||
|
response.outbound[mediaType].bytesSent = stat.stat('bytesSent');
|
||||||
|
response.outbound[mediaType].packetsSent = stat.stat('packetsSent');
|
||||||
|
if(mediaType === 'video'){
|
||||||
|
response.outbound['video'].framesEncoded = stat.stat('framesEncoded');
|
||||||
|
response.outbound['video'].nackCount = stat.stat('nackCount');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const statsReport = await this.stream.getRTCPeerConnection().getStats();
|
const statsReport:any = await this.stream.getRTCPeerConnection().getStats(null);
|
||||||
resolve(this.standardizeReport(statsReport));
|
const response = this.initWebRtcStatsResponse();
|
||||||
|
statsReport.forEach((stat: any) => {
|
||||||
|
|
||||||
|
const mediaType = stat.mediaType;
|
||||||
|
// isRemote property has been deprecated from Firefox 66 https://blog.mozilla.org/webrtc/getstats-isremote-66/
|
||||||
|
switch (stat.type) {
|
||||||
|
case "outbound-rtp":
|
||||||
|
response.outbound[mediaType].bytesSent = stat.bytesSent;
|
||||||
|
response.outbound[mediaType].packetsSent = stat.packetsSent;
|
||||||
|
if(mediaType === 'video'){
|
||||||
|
response.outbound[mediaType].framesEncoded = stat.framesEncoded;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "inbound-rtp":
|
||||||
|
response.inbound[mediaType].bytesReceived = stat.bytesReceived;
|
||||||
|
response.inbound[mediaType].packetsReceived = stat.packetsReceived;
|
||||||
|
response.inbound[mediaType].packetsLost = stat.packetsLost;
|
||||||
|
if (mediaType === 'video') {
|
||||||
|
response.inbound[mediaType].framesDecoded = stat.framesDecoded;
|
||||||
|
response.inbound[mediaType].nackCount = stat.nackCount;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return resolve(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateJSONStats(stats: RTCStatsReport): JSONStats {
|
private generateJSONStatsResponse(stats: IWebrtcStats): JSONStatsResponse {
|
||||||
return {
|
return {
|
||||||
'@timestamp': new Date().toISOString(),
|
'@timestamp': new Date().toISOString(),
|
||||||
participant_id: this.stream.connection.data,
|
participant_id: this.stream.connection.data,
|
||||||
|
@ -212,34 +294,19 @@ export class WebRtcStats {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private standardizeReport(response: RTCStatsReport | any) {
|
private initWebRtcStatsResponse(): IWebrtcStats {
|
||||||
let standardReport = {};
|
|
||||||
|
|
||||||
if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) {
|
return {
|
||||||
response.result().forEach(report => {
|
inbound: {
|
||||||
const standardStats = {
|
audio: {},
|
||||||
id: report.id,
|
video: {}
|
||||||
timestamp: report.timestamp,
|
},
|
||||||
type: report.type
|
outbound: {
|
||||||
|
audio: {},
|
||||||
|
video: {}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
report.names().forEach((name) => {
|
|
||||||
standardStats[name] = report.stat(name);
|
|
||||||
});
|
|
||||||
standardReport[standardStats.id] = standardStats;
|
|
||||||
});
|
|
||||||
|
|
||||||
return standardReport;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Others platforms
|
|
||||||
response.forEach((values) => {
|
|
||||||
let standardStats: any = {};
|
|
||||||
Object.keys(values).forEach((value: any) => {
|
|
||||||
standardStats[value] = values[value];
|
|
||||||
});
|
|
||||||
standardReport[standardStats.id] = standardStats
|
|
||||||
});
|
|
||||||
|
|
||||||
return standardReport;
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue