Spaces:
Running
Running
Commit Β·
de1deba
1
Parent(s): 846860f
Fix: instant resume via FFmpeg seek, no motion blur in fast mode (write original frame)
Browse files
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
|
|
|
|
|
|
|
|
|
|
| 85 |
if start_frame > 0:
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 119 |
-
#
|
| 120 |
-
|
| 121 |
-
|
|
|
|
| 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:
|