fix concat

This commit is contained in:
2025-07-26 16:29:59 -07:00
parent 4958c503dd
commit fa945b9c3e

View File

@@ -401,15 +401,19 @@ class VideoProcessor:
if not chunk_files: if not chunk_files:
raise ValueError("No chunk files to merge") raise ValueError("No chunk files to merge")
print(f"🎬 Streaming merge: {len(chunk_files)} chunks → {output_path}") print(f"🎬 TRUE Streaming merge: {len(chunk_files)} chunks → {output_path}")
# Use simple concatenation approach - load and merge all frames sequentially # Create temporary directory for frame images
all_frames = [] import tempfile
temp_frames_dir = Path(tempfile.mkdtemp(prefix="merge_frames_"))
frame_counter = 0
try: try:
# Process each chunk without accumulation print(f"📁 Using temp frames dir: {temp_frames_dir}")
# Process each chunk and save frames directly to disk
for i, chunk_file in enumerate(chunk_files): for i, chunk_file in enumerate(chunk_files):
print(f"📼 Loading chunk {i+1}/{len(chunk_files)}: {chunk_file.name}") print(f"📼 Processing chunk {i+1}/{len(chunk_files)}: {chunk_file.name}")
# Load chunk (this is the only copy in memory) # Load chunk (this is the only copy in memory)
chunk_data = np.load(str(chunk_file)) chunk_data = np.load(str(chunk_file))
@@ -422,9 +426,16 @@ class VideoProcessor:
frames = frames[overlap_frames:] frames = frames[overlap_frames:]
print(f" ✂️ Skipped {overlap_frames} overlapping frames") print(f" ✂️ Skipped {overlap_frames} overlapping frames")
# Add frames to final sequence # Save frames directly to disk (no accumulation in memory)
all_frames.extend(frames) for frame in frames:
print(f" ✅ Added {len(frames)} frames (total: {len(all_frames)})") frame_path = temp_frames_dir / f"frame_{frame_counter:06d}.jpg"
# Use high quality JPEG to minimize compression artifacts
success = cv2.imwrite(str(frame_path), frame, [cv2.IMWRITE_JPEG_QUALITY, 95])
if not success:
raise RuntimeError(f"Failed to save frame {frame_counter}")
frame_counter += 1
print(f" ✅ Saved {len(frames)} frames to disk (total: {frame_counter})")
# Immediately free chunk memory # Immediately free chunk memory
del frames, chunk_data del frames, chunk_data
@@ -439,9 +450,9 @@ class VideoProcessor:
# Aggressive cleanup every chunk # Aggressive cleanup every chunk
self._aggressive_memory_cleanup(f"After processing chunk {i}") self._aggressive_memory_cleanup(f"After processing chunk {i}")
# Save final video using existing method # Create final video directly from frame images using ffmpeg
print(f"📹 Saving final video with {len(all_frames)} frames...") print(f"📹 Creating final video from {frame_counter} frames...")
self.save_video(all_frames, output_path) self._create_video_from_frames(temp_frames_dir, Path(output_path), frame_counter)
# Add audio if provided # Add audio if provided
if audio_source: if audio_source:
@@ -449,19 +460,72 @@ class VideoProcessor:
except Exception as e: except Exception as e:
print(f"❌ Streaming merge failed: {e}") print(f"❌ Streaming merge failed: {e}")
# Cleanup
if 'all_frames' in locals():
del all_frames
gc.collect()
raise raise
finally: finally:
# Cleanup # Cleanup temporary frames directory
if 'all_frames' in locals(): try:
del all_frames if temp_frames_dir.exists():
import shutil
shutil.rmtree(temp_frames_dir)
print(f"🗑️ Cleaned up temp frames dir: {temp_frames_dir}")
except Exception as e:
print(f"⚠️ Could not cleanup temp frames dir: {e}")
# Memory cleanup
gc.collect() gc.collect()
print(f"✅ Streaming merge complete: {output_path}") print(f" TRUE Streaming merge complete: {output_path}")
def _create_video_from_frames(self, frames_dir: Path, output_path: Path, frame_count: int):
"""Create video directly from frame images using ffmpeg (memory efficient)"""
import subprocess
frame_pattern = str(frames_dir / "frame_%06d.jpg")
fps = self.video_info['fps'] if hasattr(self, 'video_info') and self.video_info else 30.0
print(f"🎬 Creating video with ffmpeg: {frame_count} frames at {fps} fps")
# Use GPU encoding if available, fallback to CPU
gpu_cmd = [
'ffmpeg', '-y', # -y to overwrite output file
'-framerate', str(fps),
'-i', frame_pattern,
'-c:v', 'h264_nvenc', # NVIDIA GPU encoder
'-preset', 'fast',
'-cq', '18', # Quality for GPU encoding
'-pix_fmt', 'yuv420p',
str(output_path)
]
cpu_cmd = [
'ffmpeg', '-y', # -y to overwrite output file
'-framerate', str(fps),
'-i', frame_pattern,
'-c:v', 'libx264', # CPU encoder
'-preset', 'medium',
'-crf', '18', # Quality for CPU encoding
'-pix_fmt', 'yuv420p',
str(output_path)
]
# Try GPU first
print(f"🚀 Trying GPU encoding...")
result = subprocess.run(gpu_cmd, capture_output=True, text=True)
if result.returncode != 0:
print("⚠️ GPU encoding failed, using CPU...")
print(f"🔄 CPU encoding...")
result = subprocess.run(cpu_cmd, capture_output=True, text=True)
else:
print("✅ GPU encoding successful!")
if result.returncode != 0:
print(f"❌ FFmpeg stdout: {result.stdout}")
print(f"❌ FFmpeg stderr: {result.stderr}")
raise RuntimeError(f"FFmpeg failed with return code {result.returncode}")
print(f"✅ Video created successfully: {output_path}")
def _add_audio_to_video(self, video_path: str, audio_source: str): def _add_audio_to_video(self, video_path: str, audio_source: str):
"""Add audio to video using ffmpeg""" """Add audio to video using ffmpeg"""