fix playback on load

This commit is contained in:
Siddharth
2025-11-09 12:07:36 -07:00
parent 0e946fb260
commit e3b38c00f1
5 changed files with 87 additions and 16 deletions
+6 -5
View File
@@ -57,14 +57,15 @@ export default function VideoEditor() {
}, []);
function togglePlayPause() {
const video = videoPlaybackRef.current?.video;
const playback = videoPlaybackRef.current;
const video = playback?.video;
console.log('🎮 Toggle play/pause:', { hasVideo: !!video, isPlaying, action: isPlaying ? 'pause' : 'play' });
if (!video) return;
if (!playback || !video) return;
if (isPlaying) {
video.pause();
playback.pause();
} else {
video.play().catch(err => console.error('❌ Video play failed:', err));
playback.play().catch(err => console.error('❌ Video play failed:', err));
}
}
@@ -33,6 +33,8 @@ export interface VideoPlaybackRef {
app: PIXI.Application | null;
videoSprite: PIXI.Sprite | null;
videoContainer: PIXI.Container | null;
play: () => Promise<void>;
pause: () => void;
}
const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
@@ -75,6 +77,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
const maskGraphicsRef = useRef<PIXI.Graphics | null>(null);
const isPlayingRef = useRef(isPlaying);
const isSeekingRef = useRef(false);
const allowPlaybackRef = useRef(false);
const clampFocusToStage = useCallback((focus: ZoomFocus, depth: ZoomDepth) => {
return clampFocusToStageUtil(focus, depth, stageSizeRef.current);
@@ -152,6 +155,28 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
app: appRef.current,
videoSprite: videoSpriteRef.current,
videoContainer: videoContainerRef.current,
play: async () => {
const video = videoRef.current;
if (!video) {
allowPlaybackRef.current = false;
return;
}
allowPlaybackRef.current = true;
try {
await video.play();
} catch (error) {
allowPlaybackRef.current = false;
throw error;
}
},
pause: () => {
const video = videoRef.current;
allowPlaybackRef.current = false;
if (!video) {
return;
}
video.pause();
},
}));
const updateFocusFromClientPoint = (clientX: number, clientY: number) => {
@@ -330,6 +355,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
if (!video) return;
video.pause();
video.currentTime = 0;
allowPlaybackRef.current = false;
}, [videoPath]);
useEffect(() => {
@@ -343,6 +369,12 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
if (video.videoWidth === 0 || video.videoHeight === 0) return;
const source = PIXI.VideoSource.from(video);
if ('autoPlay' in source) {
(source as { autoPlay?: boolean }).autoPlay = false;
}
if ('autoUpdate' in source) {
(source as { autoUpdate?: boolean }).autoUpdate = true;
}
const videoTexture = PIXI.Texture.from(source);
const videoSprite = new PIXI.Sprite(videoTexture);
@@ -374,6 +406,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
video,
isSeekingRef,
isPlayingRef,
allowPlaybackRef,
currentTimeRef,
timeUpdateAnimationRef,
onPlayStateChange,
@@ -524,6 +557,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(({
onDurationChange(video.duration);
video.currentTime = 0;
video.pause();
allowPlaybackRef.current = false;
currentTimeRef.current = 0;
setVideoReady(true);
};
@@ -17,6 +17,7 @@ interface LayoutResult {
baseScale: number;
baseOffset: { x: number; y: number };
maskRect: { x: number; y: number; width: number; height: number };
cropOffset: { x: number; y: number };
}
export function layoutVideoContent(params: LayoutParams): LayoutResult | null {
@@ -90,10 +91,14 @@ export function layoutVideoContent(params: LayoutParams): LayoutResult | null {
maskGraphics.fill({ color: 0xffffff });
return {
stageSize: { width, height },
stageSize: { width: croppedDisplayWidth, height: croppedDisplayHeight },
videoSize: { width: croppedVideoWidth, height: croppedVideoHeight },
baseScale: scale,
baseOffset: { x: spriteX, y: spriteY },
maskRect: { x: maskX, y: maskY, width: croppedDisplayWidth, height: croppedDisplayHeight },
cropOffset: {
x: crop.x * videoWidth,
y: crop.y * videoHeight,
},
};
}
@@ -2,6 +2,7 @@ interface VideoEventHandlersParams {
video: HTMLVideoElement;
isSeekingRef: React.MutableRefObject<boolean>;
isPlayingRef: React.MutableRefObject<boolean>;
allowPlaybackRef: React.MutableRefObject<boolean>;
currentTimeRef: React.MutableRefObject<number>;
timeUpdateAnimationRef: React.MutableRefObject<number | null>;
onPlayStateChange: (playing: boolean) => void;
@@ -13,6 +14,7 @@ export function createVideoEventHandlers(params: VideoEventHandlersParams) {
video,
isSeekingRef,
isPlayingRef,
allowPlaybackRef,
currentTimeRef,
timeUpdateAnimationRef,
onPlayStateChange,
@@ -38,12 +40,20 @@ export function createVideoEventHandlers(params: VideoEventHandlersParams) {
video.pause();
return;
}
if (!allowPlaybackRef.current) {
video.pause();
return;
}
allowPlaybackRef.current = false;
isPlayingRef.current = true;
onPlayStateChange(true);
updateTime();
};
const handlePause = () => {
allowPlaybackRef.current = false;
isPlayingRef.current = false;
if (timeUpdateAnimationRef.current) {
cancelAnimationFrame(timeUpdateAnimationRef.current);
@@ -9,6 +9,7 @@ interface TransformParams {
baseScale: number;
baseOffset: { x: number; y: number };
baseMask: { x: number; y: number; width: number; height: number };
cropOffset: { x: number; y: number };
zoomScale: number;
focusX: number;
focusY: number;
@@ -26,6 +27,7 @@ export function applyZoomTransform(params: TransformParams) {
baseScale,
baseOffset,
baseMask,
cropOffset,
zoomScale,
focusX,
focusY,
@@ -45,10 +47,10 @@ export function applyZoomTransform(params: TransformParams) {
return;
}
const focusStagePxX = focusX * stageSize.width;
const focusStagePxY = focusY * stageSize.height;
const stageCenterX = stageSize.width / 2;
const stageCenterY = stageSize.height / 2;
const focusStagePxX = baseMask.x + focusX * stageSize.width;
const focusStagePxY = baseMask.y + focusY * stageSize.height;
const stageCenterX = baseMask.x + stageSize.width / 2;
const stageCenterY = baseMask.y + stageSize.height / 2;
const actualScale = baseScale * zoomScale;
videoSprite.scale.set(actualScale);
@@ -62,8 +64,28 @@ export function applyZoomTransform(params: TransformParams) {
const focusInVideoSpaceY = focusStagePxY - baseVideoY;
// Position formula: stageCenterX - focusInVideoSpace * zoomScale
const newVideoX = stageCenterX - focusInVideoSpaceX * zoomScale;
const newVideoY = stageCenterY - focusInVideoSpaceY * zoomScale;
let newVideoX = stageCenterX - focusInVideoSpaceX * zoomScale;
let newVideoY = stageCenterY - focusInVideoSpaceY * zoomScale;
const cropStartX = cropOffset.x;
const cropStartY = cropOffset.y;
const cropEndX = cropOffset.x + videoSize.width;
const cropEndY = cropOffset.y + videoSize.height;
const maskWidth = baseMask.width;
const maskHeight = baseMask.height;
const minVideoX = baseMaskX + maskWidth - cropEndX * actualScale;
const maxVideoX = baseMaskX - cropStartX * actualScale;
const minVideoY = baseMaskY + maskHeight - cropEndY * actualScale;
const maxVideoY = baseMaskY - cropStartY * actualScale;
if (minVideoX <= maxVideoX) {
newVideoX = Math.max(minVideoX, Math.min(maxVideoX, newVideoX));
}
if (minVideoY <= maxVideoY) {
newVideoY = Math.max(minVideoY, Math.min(maxVideoY, newVideoY));
}
videoSprite.position.set(newVideoX, newVideoY);
@@ -73,11 +95,10 @@ export function applyZoomTransform(params: TransformParams) {
blurFilter.blur = motionBlur;
}
const maskWidth = baseMask.width * zoomScale;
const maskHeight = baseMask.height * zoomScale;
const maskX = baseMaskX + (newVideoX - baseVideoX);
const maskY = baseMaskY + (newVideoY - baseVideoY);
const maskX = baseMaskX;
const maskY = baseMaskY;
const radius = Math.min(maskWidth, maskHeight) * 0.02;
maskGraphics.clear();
maskGraphics.roundRect(maskX, maskY, maskWidth, maskHeight, radius);
maskGraphics.fill({ color: 0xffffff });