refactor: used hls system instead

This commit is contained in:
JohnMark Sill 2022-02-10 14:39:47 -06:00
parent 46020693cd
commit 7401789b3b
2 changed files with 132 additions and 56 deletions

View File

@ -14,13 +14,14 @@ const getLast24Hours = () => {
};
export default function HistoryViewer({ camera }) {
console.log('history', { camera });
const apiHost = useApiHost();
const videoRef = useRef();
const { searchString } = useSearchString(200, `camera=${camera}&after=${getLast24Hours()}`);
const { searchString } = useSearchString(500, `camera=${camera}&after=${getLast24Hours()}`);
const { data: events } = useEvents(searchString);
const [timelineEvents, setTimelineEvents] = useState();
const [hasPlayed, setHasPlayed] = useState();
const [isPlaying, setIsPlaying] = useState(false);
const [currentEvent, setCurrentEvent] = useState();
const [currentEventIndex, setCurrentEventIndex] = useState();
const [timelineOffset, setTimelineOffset] = useState(0);
@ -28,11 +29,18 @@ export default function HistoryViewer({ camera }) {
useEffect(() => {
if (events) {
setTimelineEvents([...events].reverse().filter((e) => e.end_time !== undefined));
const filteredEvents = [...events].reverse().filter((e) => e.end_time !== undefined);
setTimelineEvents(filteredEvents);
setCurrentEventIndex(filteredEvents.length - 1);
}
}, [events]);
const handleTimeUpdate = () => {
const videoContainer = videoRef.current;
if (videoContainer.paused) {
return;
}
const timestamp = Math.round(videoRef.current.currentTime);
const offset = Math.round(timestamp);
const triggerStateChange = offset !== timelineOffset;
@ -41,18 +49,50 @@ export default function HistoryViewer({ camera }) {
}
};
const handleTimelineChange = (event) => {
if (event !== undefined) {
setCurrentEvent(event);
const handleTimelineChange = (timelineChangedEvent) => {
if (timelineChangedEvent.seekComplete) {
const currentEventExists = currentEvent !== undefined;
if (!currentEventExists || currentEvent.id !== timelineChangedEvent.event.id) {
setCurrentEvent(timelineChangedEvent.event);
}
}
const videoContainer = videoRef.current;
if (videoContainer) {
if (!videoContainer.paused) {
videoContainer.pause();
setHasPlayed(true);
}
const videoHasPermissionToPlay = hasPlayed !== undefined;
if (videoHasPermissionToPlay && timelineChangedEvent.seekComplete) {
const markerTime = Math.abs(timelineChangedEvent.time - timelineChangedEvent.event.startTime) / 1000;
videoContainer.currentTime = markerTime;
if (hasPlayed) {
videoContainer.play();
setHasPlayed(false);
}
}
}
};
const handlePlay = function () {
videoRef.current.play();
const videoContainer = videoRef.current;
if (videoContainer) {
if (videoContainer.paused) {
videoContainer.play();
} else {
videoContainer.pause();
}
}
};
const handlePlayed = () => {
setIsPlaying(true);
};
const handlePaused = () => {
setTimelineOffset(undefined);
setIsPlaying(false);
};
const handlePrevious = function () {
@ -64,35 +104,36 @@ export default function HistoryViewer({ camera }) {
};
const handleMetadataLoad = () => {
if (videoRef.current) {
setMinHeight(videoRef.current.clientHeight);
const videoContainer = videoRef.current;
if (videoContainer) {
setMinHeight(videoContainer.clientHeight);
}
};
const RenderVideo = useCallback(() => {
return (
<video
ref={videoRef}
onTimeUpdate={handleTimeUpdate}
onPause={handlePaused}
poster={`${apiHost}/api/events/${currentEvent.id}/snapshot.jpg`}
onLoadedMetadata={handleMetadataLoad}
preload='metadata'
style={
minHeight
? {
minHeight: `${minHeight}px`,
}
: {}
}
playsInline
controls
>
<source
src={`${apiHost}/api/${camera}/start/${currentEvent.start_time}/end/${currentEvent.end_time}/clip.mp4`}
/>
</video>
);
if (currentEvent) {
return (
<video
ref={videoRef}
onTimeUpdate={handleTimeUpdate}
onPause={handlePaused}
onPlay={handlePlayed}
poster={`${apiHost}/api/events/${currentEvent.id}/snapshot.jpg`}
onLoadedMetadata={handleMetadataLoad}
preload='metadata'
style={
minHeight
? {
minHeight: `${minHeight}px`,
}
: {}
}
playsInline
>
<source type='application/vnd.apple.mpegurl' src={`${apiHost}/vod/event/${currentEvent.id}/index.m3u8`} />
</video>
);
}
}, [currentEvent, apiHost, camera, videoRef]);
return (
@ -115,6 +156,7 @@ export default function HistoryViewer({ camera }) {
events={timelineEvents}
offset={timelineOffset}
currentIndex={currentEventIndex}
disabled={isPlaying}
onChange={handleTimelineChange}
/>

View File

@ -2,7 +2,7 @@ import { h } from 'preact';
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import { longToDate } from '../utils/dateUtil';
export default function Timeline({ events, offset, currentIndex, onChange }) {
export default function Timeline({ events, offset, currentIndex, disabled, onChange }) {
const timelineContainerRef = useRef(undefined);
const [timeline, setTimeline] = useState([]);
@ -11,6 +11,7 @@ export default function Timeline({ events, offset, currentIndex, onChange }) {
const [currentEvent, setCurrentEvent] = useState();
const [scrollTimeout, setScrollTimeout] = useState();
const [scrollActive, setScrollActive] = useState(true);
const [eventsEnabled, setEventsEnable] = useState(true);
useEffect(() => {
if (events && events.length > 0 && timelineOffset) {
@ -40,8 +41,8 @@ export default function Timeline({ events, offset, currentIndex, onChange }) {
...firstTimelineEvent,
id: firstTimelineEvent.id,
index: 0,
startTime: firstTimelineEvent.start_time,
endTime: firstTimelineEvent.end_time,
startTime: longToDate(firstTimelineEvent.start_time),
endTime: longToDate(firstTimelineEvent.end_time),
});
setTimeline(timelineEvents);
}
@ -54,25 +55,22 @@ export default function Timeline({ events, offset, currentIndex, onChange }) {
useEffect(() => {
const cEvent = getCurrentEvent();
if (cEvent && offset >= 0) {
setScrollActive(false);
timelineContainerRef.current.scroll({
left: cEvent.positionX + offset - timelineOffset,
behavior: 'smooth',
});
} else {
setScrollActive(true);
}
}, [offset, timelineContainerRef]);
useEffect(() => {
if (currentIndex !== undefined) {
if (timeline.length > 0 && currentIndex !== undefined) {
const event = timeline[currentIndex];
setCurrentEvent({
...event,
id: event.id,
index: currentIndex,
startTime: event.start_time,
endTime: event.end_time,
startTime: longToDate(event.start_time),
endTime: longToDate(event.end_time),
});
timelineContainerRef.current.scroll({ left: event.positionX - timelineOffset, behavior: 'smooth' });
}
@ -88,32 +86,53 @@ export default function Timeline({ events, offset, currentIndex, onChange }) {
const foundIndex = timeline.findIndex((event) => event.startTime <= markerTime && markerTime <= event.endTime);
if (foundIndex > -1) {
const found = timeline[foundIndex];
setCurrentEvent({
...found,
id: found.id,
index: foundIndex,
startTime: found.start_time,
endTime: found.end_time,
});
if (found !== currentEvent && found.id !== currentEvent.id) {
setCurrentEvent({
...found,
id: found.id,
index: foundIndex,
startTime: longToDate(found.start_time),
endTime: longToDate(found.end_time),
});
return found;
}
}
}
};
const handleScroll = (event) => {
const scrollLogic = () => {
clearTimeout(scrollTimeout);
const scrollPosition = event.target.scrollLeft;
const scrollPosition = timelineContainerRef.current.scrollLeft;
const startTime = longToDate(timeline[0].start_time);
const markerTime = new Date(startTime.getTime() + scrollPosition * 1000);
setMarkerTime(markerTime);
handleChange(currentEvent, markerTime, false);
setScrollTimeout(
setTimeout(() => {
checkMarkerForEvent(markerTime);
const foundEvent = checkMarkerForEvent(markerTime);
handleChange(foundEvent ? foundEvent : currentEvent, markerTime, true);
}, 250)
);
};
const handleWheel = (event) => {
if (!disabled) {
return;
}
scrollLogic(event);
};
const handleScroll = (event) => {
if (disabled) {
return;
}
scrollLogic(event);
};
useEffect(() => {
if (timelineContainerRef) {
const timelineContainerWidth = timelineContainerRef.current.offsetWidth;
@ -122,9 +141,18 @@ export default function Timeline({ events, offset, currentIndex, onChange }) {
}
}, [timelineContainerRef]);
useEffect(() => {
onChange && onChange(currentEvent);
}, [onChange, currentEvent]);
const handleChange = useCallback(
(event, time, seekComplete) => {
if (onChange !== undefined) {
onChange({
event,
time,
seekComplete,
});
}
},
[onChange]
);
const RenderTimeline = useCallback(() => {
if (timeline && timeline.length > 0) {
@ -175,7 +203,13 @@ export default function Timeline({ events, offset, currentIndex, onChange }) {
></div>
</div>
</div>
<div ref={timelineContainerRef} className='overflow-x-auto hide-scroll' onScroll={handleScroll}>
<div
ref={timelineContainerRef}
onWheel={handleWheel}
onTouchMove={handleWheel}
onScroll={handleScroll}
className='overflow-x-auto hide-scroll'
>
<RenderTimeline />
</div>
</div>