Guest User

Untitled

a guest
Oct 5th, 2025
17
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.30 KB | None | 0 0
  1. import os
  2. import tempfile
  3.  
  4. # ====================== 修复权限问题:设置自定义临时目录 ======================
  5. # 创建项目目录下的temp文件夹
  6. custom_temp = os.path.join(os.getcwd(), "temp")
  7. os.makedirs(custom_temp, exist_ok=True)
  8. tempfile.tempdir = custom_temp
  9. # ==============================================================================
  10.  
  11. import numpy as np
  12. from moviepy.editor import ImageSequenceClip, AudioFileClip
  13. from scipy.io import wavfile
  14. from typing import Optional
  15. import logging
  16.  
  17. logger = logging.getLogger(__name__)
  18.  
  19. def save_video(
  20. output_path: str,
  21. video_numpy: np.ndarray,
  22. audio_numpy: Optional[np.ndarray] = None,
  23. sample_rate: int = 16000,
  24. fps: int = 24,
  25. ) -> str:
  26. """
  27. Combine a sequence of video frames with an optional audio track and save as an MP4.
  28.  
  29. Args:
  30. output_path (str): Path to the output MP4 file.
  31. video_numpy (np.ndarray): Numpy array of frames. Shape (C, F, H, W).
  32. Values can be in range [-1, 1] or [0, 255].
  33. audio_numpy (Optional[np.ndarray]): 1D or 2D numpy array of audio samples, range [-1, 1].
  34. sample_rate (int): Sample rate of the audio in Hz. Defaults to 16000.
  35. fps (int): Frames per second for the video. Defaults to 24.
  36.  
  37. Returns:
  38. str: Path to the saved MP4 file.
  39. """
  40.  
  41. # Validate inputs
  42. assert isinstance(video_numpy, np.ndarray), "video_numpy must be a numpy array"
  43. assert video_numpy.ndim == 4, "video_numpy must have shape (C, F, H, W)"
  44. assert video_numpy.shape[0] in {1, 3}, "video_numpy must have 1 or 3 channels"
  45.  
  46. if audio_numpy is not None:
  47. assert isinstance(audio_numpy, np.ndarray), "audio_numpy must be a numpy array"
  48. assert np.abs(audio_numpy).max() <= 1.0, "audio_numpy values must be in range [-1, 1]"
  49.  
  50. # Reorder dimensions: (C, F, H, W) → (F, H, W, C)
  51. video_numpy = video_numpy.transpose(1, 2, 3, 0)
  52.  
  53. # Normalize frames if values are in [-1, 1]
  54. if video_numpy.max() <= 1.0:
  55. video_numpy = np.clip(video_numpy, -1, 1)
  56. video_numpy = ((video_numpy + 1) / 2 * 255).astype(np.uint8)
  57. else:
  58. video_numpy = video_numpy.astype(np.uint8)
  59.  
  60. # Convert numpy array to a list of frames
  61. frames = list(video_numpy)
  62.  
  63. # Create video clip
  64. clip = ImageSequenceClip(frames, fps=fps)
  65.  
  66. # Add audio if provided
  67. if audio_numpy is not None:
  68. # 关键修复:使用二进制模式('wb')和delete=False
  69. with tempfile.NamedTemporaryFile(suffix=".wav", mode='wb', delete=False) as temp_audio_file:
  70. wavfile.write(
  71. temp_audio_file.name,
  72. sample_rate,
  73. (audio_numpy * 32767).astype(np.int16),
  74. )
  75. # 添加调试日志(可选,用于验证)
  76. logger.info(f"Audio written to: {temp_audio_file.name}, size: {os.path.getsize(temp_audio_file.name)} bytes")
  77. audio_clip = AudioFileClip(temp_audio_file.name)
  78. final_clip = clip.set_audio(audio_clip)
  79. else:
  80. final_clip = clip
  81.  
  82. # Write final video to disk
  83. final_clip.write_videofile(
  84. output_path, codec="libx264", audio_codec="aac", fps=fps, verbose=False, logger=None
  85. )
  86. final_clip.close()
  87.  
  88. return output_path
Advertisement
Add Comment
Please, Sign In to add comment