Download VideoAgent/_videoutil/split.py from AXERA-TECH/VideoAgent-AX650N: direct link, hf CLI and curl.
- Browser
- Download file 5.37 kB
-
https://huggingface.co/AXERA-TECH/VideoAgent-AX650N/resolve/main/VideoAgent/_videoutil/split.py
- Command line
-
hf download hf://AXERA-TECH/VideoAgent-AX650N/VideoAgent/_videoutil/split.py
-
curl -L -o split.py https://huggingface.co/AXERA-TECH/VideoAgent-AX650N/resolve/main/VideoAgent/_videoutil/split.py
5.37 kB
| 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 | |
| def _get_ffmpeg_bin(): | |
| """返回支持 libx264 的 ffmpeg 路径""" | |
| env_bin = os.getenv("FFMPEG_BIN") | |
| if env_bin: | |
| return env_bin | |
| try: | |
| import imageio_ffmpeg | |
| return imageio_ffmpeg.get_ffmpeg_exe() | |
| except ImportError: | |
| return "ffmpeg" | |
| _FFMPEG_BIN = _get_ffmpeg_bin() | |
| 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_BIN, "-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='pcm_s16le', fps=16000, nbytes=2, 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 | |