import { h } from 'preact'; import { baseUrl } from '../api/baseUrl'; import { useEffect } from 'preact/hooks'; export default function MsePlayer({ camera, width, height }) { const url = `${baseUrl.replace(/^http/, 'ws')}live/mse/api/ws?src=${camera}`; useEffect(() => { const video = document.querySelector('#video'); // support api_path const ws = new WebSocket(url); ws.binaryType = 'arraybuffer'; let mediaSource; ws.onopen = () => { // https://web.dev/i18n/en/fast-playback-with-preload/#manual_buffering // https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API mediaSource = new MediaSource(); video.src = URL.createObjectURL(mediaSource); mediaSource.onsourceopen = () => { mediaSource.onsourceopen = null; URL.revokeObjectURL(video.src); ws.send(JSON.stringify({ type: 'mse' })); }; }; let sourceBuffer, queueBuffer = []; ws.onmessage = (ev) => { if (typeof ev.data === 'string') { const data = JSON.parse(ev.data); if (data.type === 'mse') { sourceBuffer = mediaSource.addSourceBuffer(data.value); // important: segments supports TrackFragDecodeTime // sequence supports only TrackFragRunEntry Duration sourceBuffer.mode = 'segments'; sourceBuffer.onupdateend = () => { if (!sourceBuffer.updating && queueBuffer.length > 0) { sourceBuffer.appendBuffer(queueBuffer.shift()); } }; } } else if (sourceBuffer.updating) { queueBuffer.push(ev.data); } else { sourceBuffer.appendBuffer(ev.data); } }; let offsetTime = 1, noWaiting = 0; setInterval(() => { if (video.paused || video.seekable.length === 0) return; if (noWaiting < 0) { offsetTime = Math.min(offsetTime * 1.1, 5); } else if (noWaiting >= 30) { noWaiting = 0; offsetTime = Math.max(offsetTime * 0.9, 0.5); } noWaiting += 1; const endTime = video.seekable.end(video.seekable.length - 1); let playbackRate = (endTime - video.currentTime) / offsetTime; if (playbackRate < 0.1) { // video.currentTime = endTime - offsetTime; playbackRate = 0.1; } else if (playbackRate > 10) { // video.currentTime = endTime - offsetTime; playbackRate = 10; } // https://github.com/GoogleChrome/developer.chrome.com/issues/135 video.playbackRate = playbackRate; }, 1000); video.onwaiting = () => { const endTime = video.seekable.end(video.seekable.length - 1); video.currentTime = endTime - offsetTime; noWaiting = -1; }; }, [url]); return (
); }