Live video on the AT Protocol
1import { useEffect, useRef, useState } from "react";
2import { useLivestreamStore } from "../livestream-store";
3
4export type ConnectionQuality = "good" | "degraded" | "poor";
5
6function getLiveConnectionQuality(
7 timeBetweenSegments: number | null,
8 range: number | null,
9 numOfSegments: number = 1,
10): ConnectionQuality {
11 if (timeBetweenSegments === null || range === null) return "poor";
12
13 if (timeBetweenSegments <= 1500 && range <= (1500 * 60) / numOfSegments) {
14 return "good";
15 }
16 if (timeBetweenSegments <= 3000 && range <= (3000 * 60) / numOfSegments) {
17 return "degraded";
18 }
19 return "poor";
20}
21
22export function useSegmentTiming() {
23 const latestSegment = useLivestreamStore((x) => x.segment);
24 const [segmentDeltas, setSegmentDeltas] = useState<number[]>([]);
25 const prevSegmentRef = useRef<any>();
26 const prevTimestampRef = useRef<number | null>(null);
27
28 // Dummy state to force update every second
29 const [, setNow] = useState(Date.now());
30
31 useEffect(() => {
32 const interval = setInterval(() => {
33 setNow(Date.now());
34 }, 1000);
35 return () => clearInterval(interval);
36 }, []);
37
38 useEffect(() => {
39 if (latestSegment && prevSegmentRef.current !== latestSegment) {
40 const now = Date.now();
41 if (prevTimestampRef.current !== null) {
42 const delta = now - prevTimestampRef.current;
43 // Only store the last 25 deltas
44 setSegmentDeltas((prev) => [...prev, delta].slice(-25));
45 }
46 prevTimestampRef.current = now;
47 prevSegmentRef.current = latestSegment;
48 }
49 }, [latestSegment]);
50
51 // The most recent time between segments
52 const timeBetweenSegments =
53 segmentDeltas.length > 0
54 ? segmentDeltas[segmentDeltas.length - 1]
55 : prevTimestampRef.current
56 ? Date.now() - prevTimestampRef.current
57 : null;
58
59 // Calculate mean and range of deltas
60 const mean =
61 segmentDeltas.length > 0
62 ? Math.round(
63 segmentDeltas.reduce((acc, curr) => acc + curr, 0) /
64 segmentDeltas.length,
65 )
66 : null;
67
68 const range =
69 segmentDeltas.length > 0
70 ? Math.max(...segmentDeltas) - Math.min(...segmentDeltas)
71 : null;
72
73 let to_ret = {
74 segmentDeltas,
75 timeBetweenSegments,
76 mean,
77 range,
78 connectionQuality: "poor",
79 };
80
81 to_ret.connectionQuality = getLiveConnectionQuality(
82 timeBetweenSegments,
83 range,
84 segmentDeltas.length,
85 );
86
87 return to_ret;
88}