@@ -90,3 +90,92 @@ def get_video_specs(video_path: str):
9090 "duration" : float (data .get ("format" , {}).get ("duration" , 0 )),
9191 "streams" : streams ,
9292 }
93+
94+
95+ def compress_video (
96+ input_path : str ,
97+ output_path : str ,
98+ crf : int = 23 ,
99+ preset : str = "superfast" ,
100+ overwrite : bool = True ,
101+ ):
102+ """Compress video using H.264 codec with specified quality settings.
103+
104+ Parameters
105+ ----------
106+ input_path : str
107+ Path to the input video file.
108+
109+ output_path : str
110+ Path where the compressed video file will be saved.
111+
112+ crf : int, optional
113+ Constant Rate Factor determining the quality and bitrate.
114+ Lower values yield higher quality and larger file sizes
115+ (range 0-51, typical 18-28).
116+ Default is 23.
117+
118+ preset : str, optional
119+ The encoding speed preset. Faster presets result in larger files
120+ but quicker encoding. Options include 'ultrafast', 'superfast',
121+ 'veryfast', 'faster', 'fast', 'medium', 'slow', 'slower',
122+ 'veryslow'.
123+ Default is 'superfast'.
124+
125+ overwrite : bool, optional
126+ If True, overwrite the output file if it already exists.
127+ Default is True.
128+
129+ Returns
130+ -------
131+ bool
132+ True if successful, False if an error occurred
133+
134+ Raises
135+ ------
136+ FileNotFoundError if the video file does not exist.
137+
138+ Example:
139+ --------
140+ >>> from ethology.io.video_utils import compress_video
141+ >>> compress_video("input.mp4", "output.mp4")
142+ True
143+ >>> compress_video("input.mp4", "output.mp4", crf=20, preset="medium")
144+ True
145+
146+ """
147+ path = Path (input_path )
148+ if not path .exists ():
149+ raise FileNotFoundError (f"Video file not found: { input_path } " )
150+
151+ cmd = [
152+ "ffmpeg" ,
153+ "-y" if overwrite else "" ,
154+ "-i" ,
155+ str (input_path ),
156+ "-c:v" ,
157+ "libx264" ,
158+ "-pix_fmt" ,
159+ "yuv420p" ,
160+ "-preset" ,
161+ preset ,
162+ "-crf" ,
163+ str (crf ),
164+ "-progress" ,
165+ "pipe:1" ,
166+ str (output_path ),
167+ ]
168+
169+ cmd = [
170+ arg for arg in cmd if arg
171+ ] # Filter out empty args, say in case of overwrite=False
172+
173+ try :
174+ subprocess .run (cmd , capture_output = True , text = True , check = True )
175+ print ("File compressed successfully!" )
176+ return True
177+
178+ except subprocess .CalledProcessError as e :
179+ print (f"FFmpeg error: { e .stderr } " )
180+ print ("File compression failed." )
181+ return False
0 commit comments