Sticker View: Fix several video emojis causing extreme CPU usage

This commit is contained in:
Alexander Zinchuk 2023-10-11 16:44:53 +02:00
parent faec0d1f57
commit 2e3c059333
3 changed files with 15 additions and 5 deletions

View File

@ -81,7 +81,8 @@ const StickerView: FC<OwnProps> = ({
const { const {
id, isLottie, stickerSetInfo, emoji, id, isLottie, stickerSetInfo, emoji,
} = sticker; } = sticker;
const isUnsupportedVideo = sticker.isVideo && !IS_WEBM_SUPPORTED; const [isVideoBroken, markVideoBroken] = useFlag();
const isUnsupportedVideo = sticker.isVideo && (!IS_WEBM_SUPPORTED || isVideoBroken);
const isVideo = sticker.isVideo && !isUnsupportedVideo; const isVideo = sticker.isVideo && !isUnsupportedVideo;
const isStatic = !isLottie && !isVideo; const isStatic = !isLottie && !isVideo;
const previewMediaHash = getStickerPreviewHash(sticker.id); const previewMediaHash = getStickerPreviewHash(sticker.id);
@ -184,6 +185,7 @@ const StickerView: FC<OwnProps> = ({
isPriority={forceAlways} isPriority={forceAlways}
disablePictureInPicture disablePictureInPicture
onReady={markPlayerReady} onReady={markPlayerReady}
onBroken={markVideoBroken}
onEnded={onVideoEnded} onEnded={onVideoEnded}
style={filterStyle} style={filterStyle}
/> />

View File

@ -15,6 +15,7 @@ type OwnProps =
canPlay: boolean; canPlay: boolean;
children?: React.ReactNode; children?: React.ReactNode;
onReady?: NoneToVoidFunction; onReady?: NoneToVoidFunction;
onBroken?: NoneToVoidFunction;
} }
& VideoProps; & VideoProps;
@ -24,6 +25,7 @@ function OptimizedVideo({
canPlay, canPlay,
children, children,
onReady, onReady,
onBroken,
onTimeUpdate, onTimeUpdate,
...restProps ...restProps
}: OwnProps) { }: OwnProps) {
@ -45,7 +47,7 @@ function OptimizedVideo({
}); });
// This is only needed for browsers not allowing autoplay // This is only needed for browsers not allowing autoplay
const { isBuffered, bufferingHandlers } = useBuffering(true, onTimeUpdate); const { isBuffered, bufferingHandlers } = useBuffering(true, onTimeUpdate, onBroken);
const { onPlaying: handlePlayingForBuffering, ...otherBufferingHandlers } = bufferingHandlers; const { onPlaying: handlePlayingForBuffering, ...otherBufferingHandlers } = bufferingHandlers;
useSyncEffect(([prevIsBuffered]) => { useSyncEffect(([prevIsBuffered]) => {
if (prevIsBuffered === undefined) { if (prevIsBuffered === undefined) {

View File

@ -11,13 +11,14 @@ type BufferingEvent = (e: Event | React.SyntheticEvent<HTMLMediaElement>) => voi
const MIN_READY_STATE = 3; const MIN_READY_STATE = 3;
// Avoid flickering when re-mounting previously buffered video // Avoid flickering when re-mounting previously buffered video
const DEBOUNCE = 200; const DEBOUNCE = 200;
const MIN_ALLOWED_MEDIA_DURATION = 0.1; // Some video emojis have weird duration of 0.04 causing extreme amount of events
/** /**
* Time range relative to the duration [0, 1] * Time range relative to the duration [0, 1]
*/ */
export type BufferedRange = { start: number; end: number }; export type BufferedRange = { start: number; end: number };
const useBuffering = (noInitiallyBuffered = false, onTimeUpdate?: AnyToVoidFunction) => { const useBuffering = (noInitiallyBuffered = false, onTimeUpdate?: AnyToVoidFunction, onBroken?: AnyToVoidFunction) => {
const [isBuffered, setIsBuffered] = useState(!noInitiallyBuffered); const [isBuffered, setIsBuffered] = useState(!noInitiallyBuffered);
const [isReady, setIsReady] = useState(false); const [isReady, setIsReady] = useState(false);
const [bufferedProgress, setBufferedProgress] = useState(0); const [bufferedProgress, setBufferedProgress] = useState(0);
@ -28,12 +29,17 @@ const useBuffering = (noInitiallyBuffered = false, onTimeUpdate?: AnyToVoidFunct
}, []); }, []);
const handleBuffering = useLastCallback<BufferingEvent>((e) => { const handleBuffering = useLastCallback<BufferingEvent>((e) => {
const media = e.currentTarget as HTMLMediaElement;
if (media.duration < MIN_ALLOWED_MEDIA_DURATION) {
onBroken?.();
return;
}
if (e.type === 'timeupdate') { if (e.type === 'timeupdate') {
onTimeUpdate?.(e); onTimeUpdate?.(e);
} }
const media = e.currentTarget as HTMLMediaElement;
if (!isSafariPatchInProgress(media)) { if (!isSafariPatchInProgress(media)) {
if (media.buffered.length) { if (media.buffered.length) {
const ranges = getTimeRanges(media.buffered, media.duration); const ranges = getTimeRanges(media.buffered, media.duration);