H022329's picture
Upload folder using huggingface_hub
10a30d9 verified
import os
import time
import shutil
import subprocess
import numpy as np
from tqdm import tqdm
from moviepy.video import fx as vfx
from moviepy.video.io.VideoFileClip import VideoFileClip
import logging
import multiprocessing
logger = logging.getLogger(__name__)
def preprocess_video(
video_path,
target_width=640,
target_height=360,
target_fps=24,
video_output_format='mp4',
):
"""将输入视频调整为固定分辨率和帧率,存储到视频文件上级目录的同级 processed 目录下。
如果目标文件已存在则直接复用,返回预处理后的视频路径。
"""
video_name = os.path.basename(video_path).split('.')[0]
processed_dir = os.path.join(os.path.dirname(os.path.dirname(video_path)), 'processed')
os.makedirs(processed_dir, exist_ok=True)
output_path = os.path.join(processed_dir, f"{video_name}.{video_output_format}")
if os.path.exists(output_path):
logger.info(f"Preprocessed video already exists: {output_path}")
return output_path
logger.info(f"Preprocessing video {video_name}: {target_width}x{target_height} @ {target_fps}fps -> {output_path}")
cmd = [
"ffmpeg", "-y",
"-i", video_path,
"-vf", f"scale={target_width}:{target_height}",
"-r", str(target_fps),
"-c:v", "libx264",
"-c:a", "aac", # 确保音频被编码
"-b:a", "128k",
"-loglevel", "error",
output_path,
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
raise RuntimeError(f"ffmpeg preprocessing failed for {video_path}:\n{result.stderr}")
logger.info(f"Preprocessed video saved to {output_path}")
return output_path
def split_video(
video_path,
working_dir,
segment_length,
num_frames_per_segment,
audio_output_format='mp3',
):
unique_timestamp = str(int(time.time() * 1000))
video_name = os.path.basename(video_path).split('.')[0]
video_segment_cache_path = os.path.join(working_dir, '_cache', video_name)
if os.path.exists(video_segment_cache_path):
shutil.rmtree(video_segment_cache_path)
os.makedirs(video_segment_cache_path, exist_ok=False)
segment_index = 0
segment_index2name, segment_times_info = {}, {}
with VideoFileClip(video_path) as video:
total_video_length = int(video.duration)
start_times = list(range(0, total_video_length, segment_length))
# if the last segment is shorter than 5 seconds, we merged it to the last segment
if len(start_times) > 1 and (total_video_length - start_times[-1]) < 5:
start_times = start_times[:-1]
for start in tqdm(start_times, desc=f"Spliting Video {video_name}"):
if start != start_times[-1]:
end = min(start + segment_length, total_video_length)
else:
end = total_video_length
subvideo = video.subclip(start, end)
subvideo_length = subvideo.duration
frame_times = np.linspace(0, subvideo_length, num_frames_per_segment, endpoint=False)
frame_times += start
segment_index2name[f"{segment_index}"] = f"{unique_timestamp}-{segment_index}-{start}-{end}"
segment_times_info[f"{segment_index}"] = {"frame_times": frame_times, "timestamp": (start, end)}
# save audio
audio_file_base_name = segment_index2name[f"{segment_index}"]
audio_file = f'{audio_file_base_name}.{audio_output_format}'
try:
subaudio = subvideo.audio
subaudio.write_audiofile(os.path.join(video_segment_cache_path, audio_file), codec='mp3', verbose=False, logger=None)
except Exception as e:
logger.warning(f"Warning: Failed to extract audio for video {video_name} ({start}-{end}). Probably due to lack of audio track.")
segment_index += 1
return segment_index2name, segment_times_info
def saving_video_segments(
video_name,
video_path,
working_dir,
segment_index2name,
segment_times_info,
error_queue,
video_output_format='mp4',
):
try:
with VideoFileClip(video_path) as video:
video_segment_cache_path = os.path.join(working_dir, '_cache', video_name)
for index in tqdm(segment_index2name, desc=f"Saving Video Segments {video_name}"):
start, end = segment_times_info[index]["timestamp"][0], segment_times_info[index]["timestamp"][1]
video_file = f'{segment_index2name[index]}.{video_output_format}'
# print("video_file ", video_file )
subvideo = video.subclip(start, end)
subvideo.write_videofile(os.path.join(video_segment_cache_path, video_file), codec='libx264', verbose=False, logger=None)
except Exception as e:
error_queue.put(f"Error in saving_video_segments:\n {str(e)}")
raise RuntimeError