Плеер кадров с изображениями s3Javascript

Форум по Javascript
Ответить
Anonymous
 Плеер кадров с изображениями s3

Сообщение Anonymous »

Я создаю приложение React, которое должно одновременно отображать несколько игроков кадров (до 25 игроков). Каждый проигрыватель отображает последовательность изображений (10–100 кадров), хранящихся в S3, со скоростью 2 кадра в секунду.
Текущая реализация:
  • Несколько проигрывателей кадров в виде сетки
  • Каждый проигрыватель загружает и воспроизводит последовательности изображений из S3
  • Реализована стратегия предварительной загрузки для повышения производительности.
  • Целевая частота кадров: 2 кадра в секунду
Технические характеристики:
  • React (последняя версия)
  • Изображения, размещенные на S3
  • Каждый проигрыватель кадров независим
  • Количество игроков: до 25
  • Кадров на игрока: 10–100
Проблема:
При рендеринге нескольких игроков при одновременном большом количестве кадров некоторые проигрыватели кадров застревают. На вкладке «Сеть» отображаются многочисленные отмененные запросы.
Текущая реализация проигрывателя кадров:

Код: Выделить всё

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;
Вопросы:
Каков наилучший подход для эффективной работы с несколькими проигрывателями последовательностей изображений?
Как оптимизировать стратегию предварительной загрузки, чтобы предотвратить отмену запросов?
Есть ли лучшие альтернативы для управления несколькими одновременными интервалами?
Есть предложения для оптимизации производительности или альтернативных подходов будем очень признательны.
Прикрепленный скриншот сети. Скриншот вкладки «Сеть».

Подробнее здесь: https://stackoverflow.com/questions/793 ... -s3-images
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»