Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions domains/video/components/preview/previewCanvasRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { MutableRefObject, RefObject } from "react";
import { applyPixelPreviewScalePolicy, drawScaledImage, resizeCanvasForDpr } from "@/shared/utils";
import { getCanvasColorsSync } from "@/shared/hooks";
import { PLAYBACK } from "../../constants";

import { Clip, VideoClip, VideoProject, VideoTrack, getClipPlaybackSpeed, getClipScaleX, getClipScaleY, getSourceTime } from "../../types";
import { resolveClipPositionAtTimelineTime } from "../../utils/clipTransformKeyframes";
import { drawCropOverlay, drawMaskRegionOverlay, drawTransformBoundsOverlay } from "./previewCanvasOverlayDrawing";
Expand Down Expand Up @@ -272,13 +272,13 @@ export function renderPreviewCanvasFrame(params: PreviewCanvasRenderParams) {
continue;
}
const sourceTime = getSourceTime(clip, renderTime);
if (params.playback.isPlaying && Math.abs(videoElement.currentTime - sourceTime) > PLAYBACK.SEEK_DRIFT_THRESHOLD * 1.25) {
baseFrameReady = false;
continue;
}
if (!params.playback.isPlaying && Math.abs(videoElement.currentTime - sourceTime) > 0.05) {
videoElement.currentTime = sourceTime;
}
// During playback, always draw whatever frame the video element has.
// The video is playing at the correct playbackRate, so the current
// frame is the best available — skipping it causes frozen display
// (the "1 FPS" symptom). Only mark not-ready when truly no data.
sourceEl = videoElement;
} else if (clip.type === "image") {
let img = params.imageCacheRef.current.get(clip.sourceUrl);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,14 @@ export function usePreviewMediaPlaybackSync(options: UsePreviewMediaPlaybackSync
}

const sourceTime = getSourceTime(clip, ct);
// During playback, use relaxed threshold to avoid re-seek storms that
// stall the decoder and cause frozen frames. The video element is
// already playing at the correct playbackRate — let it catch up
// naturally instead of constantly interrupting with seeks.
const videoSeekThreshold =
desiredState.isAudible && !isWebAudioReadyRef.current(clip.sourceUrl)
? Math.max(PLAYBACK.SEEK_DRIFT_THRESHOLD, 0.45)
: PLAYBACK.SEEK_DRIFT_THRESHOLD;
? Math.max(PLAYBACK.PLAYBACK_SEEK_DRIFT_THRESHOLD, 0.8)
: PLAYBACK.PLAYBACK_SEEK_DRIFT_THRESHOLD;
Comment on lines 160 to +163

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 재생 중 0.8초 미만 seek를 즉시 재동기화하세요

재생 중 타임라인을 짧게 seek(예: ±0.2~0.7초)하면 videoSeekThreshold가 0.8초로 완화되어 video.currentTime 보정이 발생하지 않습니다. 이 경로에서 seek()는 재생을 멈추지 않고 타임라인 시간만 점프시키기 때문에, 비디오 엘리먼트와 타임라인이 같은 속도로 계속 진행되며 생긴 오프셋이 사라지지 않아 영구적인 A/V·플레이헤드 불일치가 됩니다(이전 임계값 0.15/0.45에서는 빠르게 복구되던 동작).

Useful? React with 👍 / 👎.

if (
Number.isFinite(sourceTime) &&
hasFiniteMediaDuration(video) &&
Expand Down
6 changes: 5 additions & 1 deletion domains/video/constants/videoConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ export const PLAYBACK = {
DEFAULT_RATE: 1,
FRAME_STEP: 1 / 30, // 30fps
SYNC_INTERVAL_MS: 50, // media sync interval during playback
SEEK_DRIFT_THRESHOLD: 0.15, // seconds: visual media re-seek threshold
SEEK_DRIFT_THRESHOLD: 0.15, // seconds: visual media re-seek threshold (scrub/pause)
/** Relaxed drift threshold during active playback — avoids re-seek storms that
* stall the decoder. The video element plays at the correct playbackRate,
* so small drift is acceptable for smooth preview. */
PLAYBACK_SEEK_DRIFT_THRESHOLD: 0.8, // seconds: re-seek only when very far behind
AUDIO_SEEK_DRIFT_THRESHOLD: 0.35, // seconds: fallback HTMLAudio seek threshold
TIME_DISPLAY_THROTTLE_MS: 100, // throttle for time display updates
} as const;
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.tsbuildinfo

Large diffs are not rendered by default.