File size: 3,546 Bytes
604fcb2
 
 
 
 
 
 
 
6e20182
604fcb2
6e20182
 
604fcb2
 
 
 
 
 
6e20182
604fcb2
 
 
6e20182
604fcb2
6e20182
604fcb2
9864ea4
 
 
604fcb2
 
9864ea4
604fcb2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6e20182
604fcb2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6e20182
 
604fcb2
 
 
 
 
 
 
 
 
 
 
6e20182
 
 
 
604fcb2
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Live Stream</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        html, body { width: 100%; height: 100%; overflow: hidden; background: #000; }
        video {
            width: 100vw;
            height: 100vh;
            object-fit: contain;
            background: #000;
        }
    </style>
</head>
<body>
    <video id="video" autoplay muted playsinline></video>

    <script>
        const video = document.getElementById('video');
        let ws, mediaSource, sourceBuffer;
        let bufferQueue = [];
        let reconnectTimer;

        // Use ?src= from URL hash, default to "test" for demo, "live" for real stream
        const streamSrc = location.hash.replace('#', '') || 'test';

        function connect() {
            const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
            ws = new WebSocket(`${proto}//${location.host}/api/ws?src=${streamSrc}`);
            ws.binaryType = 'arraybuffer';

            ws.onopen = () => {
                mediaSource = new MediaSource();
                video.src = URL.createObjectURL(mediaSource);
            };

            ws.onmessage = (event) => {
                if (typeof event.data === 'string') {
                    const msg = JSON.parse(event.data);
                    if (msg.type === 'mse') {
                        try {
                            sourceBuffer = mediaSource.addSourceBuffer(msg.value);
                            sourceBuffer.mode = 'segments';
                            sourceBuffer.addEventListener('updateend', () => {
                                if (bufferQueue.length > 0 && !sourceBuffer.updating) {
                                    sourceBuffer.appendBuffer(bufferQueue.shift());
                                }
                                if (video.buffered.length > 0 && video.buffered.end(0) - video.buffered.start(0) > 30) {
                                    sourceBuffer.remove(0, video.buffered.end(0) - 10);
                                }
                            });
                            video.muted = false;
                            video.play().catch(() => {});
                        } catch (e) {
                            console.error('Source buffer error:', e);
                        }
                    }
                } else {
                    if (sourceBuffer && !sourceBuffer.updating) {
                        try {
                            sourceBuffer.appendBuffer(event.data);
                        } catch (e) {
                            bufferQueue.push(event.data);
                        }
                    } else {
                        bufferQueue.push(event.data);
                    }
                }
            };

            ws.onclose = () => { cleanup(); scheduleReconnect(); };
            ws.onerror = () => { cleanup(); scheduleReconnect(); };
        }

        function cleanup() {
            bufferQueue = [];
            if (mediaSource && mediaSource.readyState === 'open') {
                try { mediaSource.endOfStream(); } catch(e) {}
            }
            sourceBuffer = null;
            mediaSource = null;
        }

        function scheduleReconnect() {
            clearTimeout(reconnectTimer);
            reconnectTimer = setTimeout(connect, 3000);
        }

        connect();
    </script>
</body>
</html>