Add jitter stat

This commit is contained in:
redphx 2024-10-10 21:35:36 +07:00
parent 411e43ceb0
commit 728abced45
7 changed files with 102 additions and 42 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -91,6 +91,7 @@ div[class^=StreamMenu-module__container] .bx-badges {
&[data-stats*="[batt]"] > .bx-stat-batt,
&[data-stats*="[fps]"] > .bx-stat-fps,
&[data-stats*="[ping]"] > .bx-stat-ping,
&[data-stats*="[jit]"] > .bx-stat-jit,
&[data-stats*="[btr]"] > .bx-stat-btr,
&[data-stats*="[dt]"] > .bx-stat-dt,
&[data-stats*="[pl]"] > .bx-stat-pl,
@ -106,6 +107,7 @@ div[class^=StreamMenu-module__container] .bx-badges {
&[data-stats$="[batt]"] > .bx-stat-batt,
&[data-stats$="[fps]"] > .bx-stat-fps,
&[data-stats$="[ping]"] > .bx-stat-ping,
&[data-stats$="[jit]"] > .bx-stat-jit,
&[data-stats$="[btr]"] > .bx-stat-btr,
&[data-stats$="[dt]"] > .bx-stat-dt,
&[data-stats$="[pl]"] > .bx-stat-pl,

View File

@ -37,6 +37,10 @@ export class StreamStats {
name: t('stat-ping'),
$element: CE('span'),
},
[StreamStat.JITTER]: {
name: t('jitter'),
$element: CE('span'),
},
[StreamStat.FPS]: {
name: t('stat-fps'),
$element: CE('span'),
@ -179,10 +183,8 @@ export class StreamStats {
$element.textContent = value.toString();
// Get stat's grade
if (PREF_STATS_CONDITIONAL_FORMATTING) {
if (statKey === StreamStat.PING || statKey === StreamStat.DECODE_TIME) {
grade = (value as any).calculateGrade();
}
if (PREF_STATS_CONDITIONAL_FORMATTING && 'grades' in value) {
grade = statsCollector.calculateGrade(value.current, value.grades);
}
if ($element.dataset.grade !== grade) {

View File

@ -714,6 +714,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
[StreamStat.PLAYTIME]: `${StreamStat.PLAYTIME.toUpperCase()}: ${t('playtime')}`,
[StreamStat.BATTERY]: `${StreamStat.BATTERY.toUpperCase()}: ${t('battery')}`,
[StreamStat.PING]: `${StreamStat.PING.toUpperCase()}: ${t('stat-ping')}`,
[StreamStat.JITTER]: `${StreamStat.JITTER.toUpperCase()}: ${t('jitter')}`,
[StreamStat.FPS]: `${StreamStat.FPS.toUpperCase()}: ${t('stat-fps')}`,
[StreamStat.BITRATE]: `${StreamStat.BITRATE.toUpperCase()}: ${t('stat-bitrate')}`,
[StreamStat.DECODE_TIME]: `${StreamStat.DECODE_TIME.toUpperCase()}: ${t('stat-decode-time')}`,

View File

@ -4,6 +4,7 @@ import { humanFileSize, secondsToHm } from "./html";
export enum StreamStat {
PING = 'ping',
JITTER = 'jit',
FPS = 'fps',
BITRATE = 'btr',
DECODE_TIME = 'dt',
@ -21,7 +22,13 @@ export type StreamStatGrade = '' | 'bad' | 'ok' | 'good';
type CurrentStats = {
[StreamStat.PING]: {
current: number;
calculateGrade: () => StreamStatGrade;
grades: [number, number, number];
toString: () => string;
};
[StreamStat.JITTER]: {
current: number;
grades: [number, number, number];
toString: () => string;
};
@ -50,7 +57,7 @@ type CurrentStats = {
[StreamStat.DECODE_TIME]: {
current: number;
total: number;
calculateGrade: () => StreamStatGrade;
grades: [number, number, number];
toString: () => string;
};
@ -96,17 +103,27 @@ export class StreamStatsCollector {
// Collect in background - 60 seconds
static readonly INTERVAL_BACKGROUND = 60 * 1000;
public calculateGrade(value: number, grades: [number, number, number]): StreamStatGrade {
return (value > grades[2]) ? 'bad' : (value > grades[1]) ? 'ok' : (value > grades[0]) ? 'good' : '';
}
private currentStats: CurrentStats = {
[StreamStat.PING]: {
current: -1,
calculateGrade() {
return (this.current >= 100) ? 'bad' : (this.current > 75) ? 'ok' : (this.current > 40) ? 'good' : '';
},
grades: [40, 75, 100],
toString() {
return this.current === -1 ? '???' : this.current.toString();
},
},
[StreamStat.JITTER]: {
current: 0,
grades: [30, 40, 60],
toString() {
return `${this.current.toFixed(2)}ms`;
},
},
[StreamStat.FPS]: {
current: 0,
toString() {
@ -142,9 +159,7 @@ export class StreamStatsCollector {
[StreamStat.DECODE_TIME]: {
current: 0,
total: 0,
calculateGrade() {
return (this.current > 12) ? 'bad' : (this.current > 9) ? 'ok' : (this.current > 6) ? 'good' : '';
},
grades: [6, 9, 12],
toString() {
return isNaN(this.current) ? '??ms' : `${this.current.toFixed(2)}ms`;
},
@ -200,12 +215,15 @@ export class StreamStatsCollector {
},
};
private lastVideoStat?: RTCBasicStat | null;
private lastVideoStat?: RTCInboundRtpStreamStats | null;
async collect() {
const stats = await STATES.currentStream.peerConnection?.getStats();
if (!stats) {
return;
}
stats?.forEach(stat => {
stats.forEach(stat => {
if (stat.type === 'inbound-rtp' && stat.kind === 'video') {
// FPS
const fps = this.currentStats[StreamStat.FPS];
@ -229,15 +247,23 @@ export class StreamStatsCollector {
const lastStat = this.lastVideoStat;
// Jitter
const jit = this.currentStats[StreamStat.JITTER];
const bufferDelayDiff = (stat as RTCInboundRtpStreamStats).jitterBufferDelay! - lastStat.jitterBufferDelay!;
const emittedCountDiff = (stat as RTCInboundRtpStreamStats).jitterBufferEmittedCount! - lastStat.jitterBufferEmittedCount!;
if (emittedCountDiff > 0) {
jit.current = bufferDelayDiff / emittedCountDiff * 1000;
}
// Bitrate
const btr = this.currentStats[StreamStat.BITRATE];
const timeDiff = stat.timestamp - lastStat.timestamp;
btr.current = 8 * (stat.bytesReceived - lastStat.bytesReceived) / timeDiff / 1000;
btr.current = 8 * (stat.bytesReceived - lastStat.bytesReceived!) / timeDiff / 1000;
// Decode time
const dt = this.currentStats[StreamStat.DECODE_TIME];
dt.total = stat.totalDecodeTime - lastStat.totalDecodeTime;
const framesDecodedDiff = stat.framesDecoded - lastStat.framesDecoded;
dt.total = stat.totalDecodeTime - lastStat.totalDecodeTime!;
const framesDecodedDiff = stat.framesDecoded - lastStat.framesDecoded!;
dt.current = dt.total / framesDecodedDiff * 1000;
this.lastVideoStat = stat;

View File

@ -131,6 +131,7 @@ const Texts = {
"increase": "Increase",
"install-android": "Better xCloud app for Android",
"japan": "Japan",
"jitter": "Jitter",
"keyboard-shortcuts": "Keyboard shortcuts",
"korea": "Korea",
"language": "Language",