devkunalnaik commited on
Commit
de1deba
Β·
1 Parent(s): 846860f

Fix: instant resume via FFmpeg seek, no motion blur in fast mode (write original frame)

Browse files
Files changed (1) hide show
  1. processors/video_processor.py +31 -6
processors/video_processor.py CHANGED
@@ -81,9 +81,28 @@ class VideoProcessor:
81
  cap.release()
82
  return None, "No face detected in source image."
83
 
84
- # Seek to start_frame for resume support
 
 
 
85
  if start_frame > 0:
86
- cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
  # Use AVI + XVID for the intermediate file β€” far more reliable than
89
  # mp4v on Linux (HF Spaces). FFmpeg converts it to H.264/mp4 after.
@@ -115,10 +134,11 @@ class VideoProcessor:
115
  f"(resume at {frame_idx} if interrupted)",
116
  )
117
 
118
- # Fast mode only safe for face swap β€” body swap needs every frame
119
- # (body position changes too much between frames to duplicate safely)
120
- if fast_mode and mode == "face" and (frame_idx - start_frame) % 2 == 1 and last_result is not None:
121
- writer.write(last_result)
 
122
  frame_idx += 1
123
  continue
124
 
@@ -151,6 +171,11 @@ class VideoProcessor:
151
  finally:
152
  cap.release()
153
  writer.release()
 
 
 
 
 
154
 
155
  frames_done = frame_idx - start_frame
156
  if frames_done == 0:
 
81
  cap.release()
82
  return None, "No face detected in source image."
83
 
84
+ # ── Seek to start_frame β€” use FFmpeg cut for instant seek ──────────────
85
+ # cap.set(POS_FRAMES) is slow: OpenCV decodes every frame up to the
86
+ # target. FFmpeg keyframe-seeks in milliseconds.
87
+ segment_path = None
88
  if start_frame > 0:
89
+ start_time = start_frame / fps
90
+ segment_path = tempfile.mktemp(suffix="_segment.mp4")
91
+ try:
92
+ import ffmpeg as _ffmpeg
93
+ (
94
+ _ffmpeg.input(video_path, ss=start_time)
95
+ .output(segment_path, c="copy", avoid_negative_ts="make_zero")
96
+ .overwrite_output()
97
+ .run(quiet=True)
98
+ )
99
+ cap.release()
100
+ cap = cv2.VideoCapture(segment_path)
101
+ print(f"[VideoProcessor] Resumed via FFmpeg cut at frame {start_frame} ({start_time:.2f}s)")
102
+ except Exception as e:
103
+ print(f"[VideoProcessor] FFmpeg seek failed ({e}), falling back to slow seek")
104
+ segment_path = None
105
+ cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
106
 
107
  # Use AVI + XVID for the intermediate file β€” far more reliable than
108
  # mp4v on Linux (HF Spaces). FFmpeg converts it to H.264/mp4 after.
 
134
  f"(resume at {frame_idx} if interrupted)",
135
  )
136
 
137
+ # Fast mode: skip odd frames β€” write the ORIGINAL frame (not a
138
+ # duplicate) so motion stays smooth with no stutter or blur.
139
+ # Only applies to face swap; body swap needs every frame.
140
+ if fast_mode and mode == "face" and (frame_idx - start_frame) % 2 == 1:
141
+ writer.write(frame) # original frame keeps motion fluid
142
  frame_idx += 1
143
  continue
144
 
 
171
  finally:
172
  cap.release()
173
  writer.release()
174
+ if segment_path:
175
+ try:
176
+ os.unlink(segment_path)
177
+ except OSError:
178
+ pass
179
 
180
  frames_done = frame_idx - start_frame
181
  if frames_done == 0: