darkmedia-x-api / engine /renderers /ffmpeg_compiler.py
cybermedia's picture
Upload folder using huggingface_hub
343eed9 verified
import os
import subprocess
from pathlib import Path
import tempfile
class FFmpegCompiler:
"""
Orchestrateur FFmpeg pour DarkMedia-X.
Remplace les opérations lentes de MoviePy (Mixage Audio, Concatenation, VFX)
par des appels C/C++ ultra-rapides accélérés matériellement.
"""
@staticmethod
def mix_audio_fast(voice_path: str, music_path: str, output_path: str, music_volume: float = 0.08):
"""
Mixe la voix et la musique quasi-instantanément via FFmpeg 'amix'.
"""
print(f"⚡ [FFmpeg] Mixage Audio Rapide: Voix + Musique...")
# Filtre complexe: on baisse le volume de la musique et on mixe
# amix: inputs=2 (voix, musique), duration=first (s'arrête quand la voix finit)
filter_complex = f"[1:a]volume={music_volume}[music];[0:a][music]amix=inputs=2:duration=first:dropout_transition=2[outa]"
cmd = [
"ffmpeg", "-y", "-hide_banner", "-loglevel", "error",
"-i", str(voice_path),
"-i", str(music_path),
"-filter_complex", filter_complex,
"-map", "[outa]",
"-acodec", "aac", "-b:a", "192k",
str(output_path)
]
try:
subprocess.run(cmd, check=True)
return True
except subprocess.CalledProcessError as e:
print(f"❌ [FFmpeg] Erreur de mixage audio: {e}")
return False
@staticmethod
def apply_vfx_and_compress(input_video: str, output_path: str, vfx_type: str = "vhs"):
"""
Applique un effet visuel (VFX) via les filtres natifs de FFmpeg.
Utilise l'encodeur x264 pour une grande compatibilité.
"""
print(f"⚡ [FFmpeg] Application du VFX Matériel : {vfx_type.upper()}...")
filters = []
if vfx_type == "vhs":
# Effet VHS: décalage des canaux couleurs (chromatic aberration) et bruit
filters.append("colorchannelmixer=rr=1:rb=0.2:gg=1:bb=1:br=0.2")
filters.append("noise=alls=15:allf=t+u")
filters.append("curves=r='0/0.1 1/0.9':g='0/0.1 1/0.9':b='0/0.2 1/1'")
filters.append("eq=contrast=1.3:brightness=-0.05:saturation=0.8")
elif vfx_type == "cctv":
# Effet caméra de surveillance: Noir et blanc, contraste élevé, scanlines
filters.append("hue=s=0") # Désaturation
filters.append("eq=contrast=1.6:brightness=-0.1")
filters.append("noise=alls=10:allf=t")
elif vfx_type == "blood_lust":
# Effet rouge sang
filters.append("colorchannelmixer=rr=1.5:gg=0.5:bb=0.5")
filters.append("eq=saturation=1.2:contrast=1.4:brightness=-0.05")
else:
# Base Horror Style (Remplace apply_horror_style de MoviePy)
filters.append("eq=contrast=1.3:brightness=-0.05:saturation=0.85")
# Filtre final (si vide, copie juste la vidéo)
vf_string = ",".join(filters) if filters else "copy"
cmd = [
"ffmpeg", "-y", "-hide_banner", "-loglevel", "error",
"-i", str(input_video)
]
if filters:
cmd.extend(["-vf", vf_string])
cmd.extend([
"-c:v", "libx264", "-preset", "fast", "-crf", "22",
"-c:a", "copy", # On ne retouche pas l'audio
str(output_path)
])
try:
subprocess.run(cmd, check=True)
return True
except subprocess.CalledProcessError as e:
print(f"❌ [FFmpeg] Erreur d'application VFX: {e}")
return False
@staticmethod
def concat_videos_fast(video_paths: list, output_path: str):
"""
Concatène une liste de vidéos MP4 en utilisant le concat demuxer (ultra rapide, pas de ré-encodage complet).
/!\ Nécessite que toutes les vidéos aient la même résolution, framerate et codecs.
"""
if not video_paths:
return False
print(f"⚡ [FFmpeg] Concaténation Rapide de {len(video_paths)} clips...")
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as f:
for vp in video_paths:
# Échapper les chemins pour FFmpeg
safe_path = str(vp).replace('\\', '/').replace("'", r"'\''")
f.write(f"file '{safe_path}'\n")
list_path = f.name
cmd = [
"ffmpeg", "-y", "-hide_banner", "-loglevel", "error",
"-f", "concat",
"-safe", "0",
"-i", list_path,
"-c", "copy",
str(output_path)
]
try:
subprocess.run(cmd, check=True)
os.unlink(list_path)
return True
except subprocess.CalledProcessError as e:
print(f"❌ [FFmpeg] Erreur de concaténation: {e}")
os.unlink(list_path)
return False
if __name__ == "__main__":
# Tests unitaires basiques
pass