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 {
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<OwnProps> = ({
isPriority={forceAlways}
disablePictureInPicture
onReady={markPlayerReady}
onBroken={markVideoBroken}
onEnded={onVideoEnded}
style={filterStyle}
/>

View File

@ -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) {

View File

@ -11,13 +11,14 @@ type BufferingEvent = (e: Event | React.SyntheticEvent<HTMLMediaElement>) => 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<BufferingEvent>((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);