@100mslive/roomkit-react
Version:

80 lines (65 loc) • 3.12 kB
text/typescript
import { useRef } from 'react';
import { usePrevious } from 'react-use';
import { HMSTrackID, selectHMSStats, useHMSStatsStore } from '@100mslive/react-sdk';
interface UseQoEProps {
videoTrackID?: HMSTrackID;
audioTrackID?: HMSTrackID;
isLocal?: boolean;
}
const EXPECTED_RESOLUTION = 1280 * 720;
const clip = (value: number, min_value: number, max_value: number) => {
return Math.max(Math.min(value, max_value), min_value);
};
/**
* calculate QoE using 5 params:
* p1:freeze_duration_norm
* p2:resolution_norm
* p3:fps_norm
* p4:delay_norm
* p5:audio_concealed_norm
* the formula is 5*(p1)^3 * (p2)^0.3 * (p3)^0.5 * (p4)^1 * (p5)*2
*
* https://github.com/100mslive/webrtc-benchmark/blob/daily/sssd.py#L112
*/
export const useQoE = ({ videoTrackID, audioTrackID, isLocal = false }: UseQoEProps) => {
const audioTrackStats = useHMSStatsStore(selectHMSStats.trackStatsByID(audioTrackID));
const videoTrackStats = useHMSStatsStore(selectHMSStats.trackStatsByID(videoTrackID));
const prevVideoTrackStats = usePrevious(videoTrackStats);
const prevAudioTrackStats = usePrevious(audioTrackStats);
const prevJitterBufferDelayMs = useRef<number>(0);
if (isLocal || (videoTrackID && !videoTrackStats) || (audioTrackID && !audioTrackStats)) {
return;
}
const resolutionNorm =
((videoTrackStats?.frameWidth || 0) * (videoTrackStats?.frameHeight || 0)) / EXPECTED_RESOLUTION;
const framesDecodedInLastSec =
videoTrackStats?.framesDecoded && prevVideoTrackStats?.framesDecoded
? videoTrackStats.framesDecoded - prevVideoTrackStats.framesDecoded
: 0;
let freezeDurationNorm =
1 - ((videoTrackStats?.totalFreezesDuration || 0) - (prevVideoTrackStats?.totalFreezesDuration || 0));
freezeDurationNorm = freezeDurationNorm < 0 ? 0.5 : freezeDurationNorm;
freezeDurationNorm = framesDecodedInLastSec === 0 ? 0 : freezeDurationNorm;
const fpsNorm = framesDecodedInLastSec / 30;
const prevJBDelay = prevVideoTrackStats?.jitterBufferDelay || 0;
const prevJBEmittedCount = prevVideoTrackStats?.jitterBufferEmittedCount || 0;
const currentJBDelay = (videoTrackStats?.jitterBufferDelay || 0) - prevJBDelay;
const currentJBEmittedCount = (videoTrackStats?.jitterBufferEmittedCount || 0) - prevJBEmittedCount;
const jitterBufferDelayMs =
currentJBEmittedCount > 0 ? (currentJBDelay * 1000) / currentJBEmittedCount : prevJitterBufferDelayMs.current;
prevJitterBufferDelayMs.current = jitterBufferDelayMs;
const delayNorm = 1 - Math.min(1, jitterBufferDelayMs / 2000);
const prevConcealedSamples =
(prevAudioTrackStats?.concealedSamples || 0) - (prevAudioTrackStats?.silentConcealedSamples || 0);
const currentConcealedSamples =
(audioTrackStats?.concealedSamples || 0) - (audioTrackStats?.silentConcealedSamples || 0) - prevConcealedSamples;
const audioConcealedNorm = 1 - currentConcealedSamples / 48000;
return (
5 *
clip(freezeDurationNorm, 0, 1) ** 3 *
clip(resolutionNorm, 0, 1) ** 0.3 *
clip(fpsNorm, 0, 1) ** 0.2 *
clip(delayNorm, 0, 1) ** 0.5 *
clip(audioConcealedNorm, 0, 1) ** 2
).toFixed(2);
};