Frame-Player mit S3-Bildern
Posted: 13 Jan 2025, 19:12
Ich erstelle eine React-Anwendung, die mehrere Frame-Player gleichzeitig rendern muss (bis zu 25 Player). Jeder Player zeigt eine in S3 gespeicherte Bildfolge (10–100 Bilder) an und spielt sie mit 2 Bildern pro Sekunde ab.
Aktuelle Implementierung:
Beim Rendern mehrerer Player mit hohem Frame zählt gleichzeitig, ein Frame-Player bleibt hängen. Auf der Registerkarte „Netzwerk“ werden zahlreiche abgebrochene Anfragen angezeigt.
Aktuelle Frame Player-Implementierung:
Fragen:
Was ist der beste Ansatz, um mehrere Bildsequenz-Player effizient zu handhaben?
Wie kann ich die Vorladestrategie optimieren, um Anforderungsstornierungen zu verhindern?
Gibt es bessere Alternativen, um mehrere gleichzeitige Intervalle zu verwalten?
Irgendwelche Vorschläge Für Leistungsoptimierungen oder alternative Ansätze wären wir sehr dankbar.
Anbei Netzwerk-Screenshot Screenshot der Registerkarte „Netzwerk“
Aktuelle Implementierung:
- Mehrere Frame-Player in einem Rasterlayout
- Jeder Player lädt und spielt Bildsequenzen aus S3
- Implementierte Vorladestrategie für bessere Leistung
- Ziel-Framerate: 2 FPS
- React (neueste Version)
- Auf S3 gehostete Bilder
- Jeder Frame-Player ist unabhängig
- Anzahl der Spieler: bis zu 25
- Frames pro Spieler: 10-100
Beim Rendern mehrerer Player mit hohem Frame zählt gleichzeitig, ein Frame-Player bleibt hängen. Auf der Registerkarte „Netzwerk“ werden zahlreiche abgebrochene Anfragen angezeigt.
Aktuelle Frame Player-Implementierung:
Code: Select all
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { formatTime } from '../utils';
export interface FrameData {
id: string;
frameUrl: string;
createdTime: number;
}
interface Props {
frames: Array;
currentFrameIndex: number;
fps: number;
timeZone: string;
loop?: boolean;
onComplete?: () => void;
width?: number | string;
height?: number | string;
className?: string;
onFrameChange: (idx: number) => void;
elaborateFrames?: boolean;
preloadCount?: number;
}
interface FrameStatus {
loaded: boolean;
error: boolean;
}
export function FramePlayer({
frames,
currentFrameIndex,
timeZone,
fps,
loop = true,
onComplete,
width = '100%',
height = '100%',
className = '',
onFrameChange,
elaborateFrames,
preloadCount = 8,
}: Props) {
const [isPlaying, setIsPlaying] = useState(true);
const frameStatusRef = useRef({});
const requestRef = useRef();
const previousTimeRef = useRef();
const preloadingRef = useRef(new Set());
const frameDuration = 1000 / fps;
// Preload frames around current index
useEffect(() => {
const preloadFrames = async () => {
const startIdx = Math.max(0, currentFrameIndex);
const endIdx = Math.min(frames.length, currentFrameIndex + preloadCount);
// const frameStatus = frameStatusRef.current;
for (let i = startIdx; i < endIdx; i++) {
const frame = frames[i];
const frameKey = frame.frameUrl;
// Skip if already loaded or currently preloading
if (
frameStatusRef.current[frameKey]?.loaded ||
// frameStatus[frameKey]?.error ||
preloadingRef.current.has(frameKey)
) {
continue;
}
preloadingRef.current.add(frameKey);
const img = new Image();
img.src = frame.frameUrl;
img.onload = () => {
frameStatusRef.current = {
...frameStatusRef.current,
[frameKey]: { loaded: true, error: false },
};
preloadingRef.current.delete(frameKey);
};
img.onerror = () => {
frameStatusRef.current = {
...frameStatusRef.current,
[frameKey]: { loaded: false, error: true },
};
preloadingRef.current.delete(frameKey);
};
}
};
preloadFrames();
}, [currentFrameIndex, frames, preloadCount]);
// Check if current frame is loaded before advancing
const shouldAdvanceFrame = useCallback(() => {
const frameStatus = frameStatusRef.current;
const currentFrame = frames[currentFrameIndex];
return currentFrame ? frameStatus[currentFrame.frameUrl]?.loaded : false;
}, [currentFrameIndex, frames]);
const animate = useCallback(
(time: number) => {
if (previousTimeRef.current === undefined) {
previousTimeRef.current = time;
requestRef.current = requestAnimationFrame(animate);
return;
}
const deltaTime = time - previousTimeRef.current;
if (deltaTime >= frameDuration && shouldAdvanceFrame()) {
let nextFrame = currentFrameIndex + 1;
if (nextFrame >= frames.length) {
if (loop) {
nextFrame = 0;
} else {
setIsPlaying(false);
onComplete?.();
nextFrame = currentFrameIndex;
}
}
onFrameChange(nextFrame);
previousTimeRef.current = time;
}
requestRef.current = requestAnimationFrame(animate);
},
[
currentFrameIndex,
frameDuration,
frames,
loop,
onComplete,
onFrameChange,
shouldAdvanceFrame,
]
);
useEffect(() => {
if (isPlaying) {
requestRef.current = requestAnimationFrame(animate);
} else if (requestRef.current) {
cancelAnimationFrame(requestRef.current);
requestRef.current = undefined;
previousTimeRef.current = undefined;
}
return () => {
if (requestRef.current) {
cancelAnimationFrame(requestRef.current);
}
};
}, [isPlaying, animate]);
const frame = useMemo(
() => (frames.length > 0 ? frames[currentFrameIndex] : undefined),
[currentFrameIndex, frames]
);
const handleImageLoad = (frameKey: string) => () => {
if (!frameStatusRef.current[frameKey]?.loaded) {
frameStatusRef.current = {
...frameStatusRef.current,
[frameKey]: { loaded: true, error: false },
};
}
};
return (
className={`frame-player relative flex items-center justify-center ${className}`}
style={{ width, height }}
>
{frame ? (
{elaborateFrames && (
{formatTime(new Date(frame.createdTime), timeZone, true)}
)}
[img]{frame.frameUrl}
style={{ objectFit: 'contain', height: '100%' }}
onLoad={handleImageLoad(frame.frameUrl)}
/>
) : (
Events loading...
)}
);
}
export default FramePlayer;
Was ist der beste Ansatz, um mehrere Bildsequenz-Player effizient zu handhaben?
Wie kann ich die Vorladestrategie optimieren, um Anforderungsstornierungen zu verhindern?
Gibt es bessere Alternativen, um mehrere gleichzeitige Intervalle zu verwalten?
Irgendwelche Vorschläge Für Leistungsoptimierungen oder alternative Ansätze wären wir sehr dankbar.
Anbei Netzwerk-Screenshot Screenshot der Registerkarte „Netzwerk“