From 2e3c0593331893d035d82863931311c79e8a536e Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Wed, 11 Oct 2023 16:44:53 +0200 Subject: [PATCH] Sticker View: Fix several video emojis causing extreme CPU usage --- src/components/common/StickerView.tsx | 4 +++- src/components/ui/OptimizedVideo.tsx | 4 +++- src/hooks/useBuffering.ts | 12 +++++++++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/components/common/StickerView.tsx b/src/components/common/StickerView.tsx index d923c96a7..a7fdfea69 100644 --- a/src/components/common/StickerView.tsx +++ b/src/components/common/StickerView.tsx @@ -81,7 +81,8 @@ const StickerView: FC = ({ const { id, isLottie, stickerSetInfo, emoji, } = 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 isStatic = !isLottie && !isVideo; const previewMediaHash = getStickerPreviewHash(sticker.id); @@ -184,6 +185,7 @@ const StickerView: FC = ({ isPriority={forceAlways} disablePictureInPicture onReady={markPlayerReady} + onBroken={markVideoBroken} onEnded={onVideoEnded} style={filterStyle} /> diff --git a/src/components/ui/OptimizedVideo.tsx b/src/components/ui/OptimizedVideo.tsx index 51a77b4e4..cb912bf91 100644 --- a/src/components/ui/OptimizedVideo.tsx +++ b/src/components/ui/OptimizedVideo.tsx @@ -15,6 +15,7 @@ type OwnProps = canPlay: boolean; children?: React.ReactNode; onReady?: NoneToVoidFunction; + onBroken?: NoneToVoidFunction; } & VideoProps; @@ -24,6 +25,7 @@ function OptimizedVideo({ canPlay, children, onReady, + onBroken, onTimeUpdate, ...restProps }: OwnProps) { @@ -45,7 +47,7 @@ function OptimizedVideo({ }); // 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; useSyncEffect(([prevIsBuffered]) => { if (prevIsBuffered === undefined) { diff --git a/src/hooks/useBuffering.ts b/src/hooks/useBuffering.ts index 38a4be09c..d5ef6da43 100644 --- a/src/hooks/useBuffering.ts +++ b/src/hooks/useBuffering.ts @@ -11,13 +11,14 @@ type BufferingEvent = (e: Event | React.SyntheticEvent) => voi const MIN_READY_STATE = 3; // Avoid flickering when re-mounting previously buffered video 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] */ 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 [isReady, setIsReady] = useState(false); const [bufferedProgress, setBufferedProgress] = useState(0); @@ -28,12 +29,17 @@ const useBuffering = (noInitiallyBuffered = false, onTimeUpdate?: AnyToVoidFunct }, []); const handleBuffering = useLastCallback((e) => { + const media = e.currentTarget as HTMLMediaElement; + + if (media.duration < MIN_ALLOWED_MEDIA_DURATION) { + onBroken?.(); + return; + } + if (e.type === 'timeupdate') { onTimeUpdate?.(e); } - const media = e.currentTarget as HTMLMediaElement; - if (!isSafariPatchInProgress(media)) { if (media.buffered.length) { const ranges = getTimeRanges(media.buffered, media.duration);