Zulko / moviepy

Video editing with Python
https://zulko.github.io/moviepy/
MIT License
12.49k stars 1.56k forks source link

Ver Very very Slow #2231

Open sysmaya opened 3 days ago

sysmaya commented 3 days ago

What an absurdly slow and exasperating thing. 2 hours to make a 5 minute video?? There are faster alternatives: FFmpeg only VidGear

Expected Behavior

You expect a 5-minute video to take less than 10 minutes to complete.

Actual Behavior

2 long hours waiting to build the video

Steps to Reproduce the Problem

Get started by downloading MoviePY Upload a 5 minute wav audio place some image clips and 50 text subtitles

steinathan commented 2 days ago

Very slow indeed, found an alternative yet?

sysmaya commented 1 day ago

Very slow indeed, found an alternative yet?

I'm writing my own functions to use ffmpeg, they are 10 times faster. But it's tedious.

def ffmpeg_image_overlay( start_time, duration, x='(W-w)/2', y='(H-h)/2'):
    """
    Generates an overlay filter for FFmpeg.
    Args:
        image_file (str): Path to the image file.
        start_time (float): Start time in seconds to display the image.
        duration (float): Duration in seconds to display the image.
        x (str): Expression for the horizontal position of the image.
        y (str): Expression for the vertical position of the image.
    Returns:
        str: Overlay filter for FFmpeg.
    """
    return f"[0:v][1:v] overlay={x}:{y}:enable='between(t,{start_time},{start_time + duration})'"
def ffmpeg_run_command(video_input, video_output, filtro, imageFile=''):
    """
    Run the FFmpeg command with the provided filter.    
    Args:
        video_input (str): Path of the input video file.
        video_output (str): Path to the output video file.
        filter (str): Command or FFmpeg filter to apply to the video.    
    Returns:
        bool: True if the command succeeds, False if it fails.
    """
    inicioExec = time.time()
    filtro = re.sub(r'\s+', ' ', filtro)
    lentxt = len(filtro)

    caracteres = string.ascii_uppercase + string.digits
    nombreTMP = ''.join(random.choice(caracteres) for _ in range(12))
    nombreTMP = nombreTMP + '.mp4'

    ffmpeg_path = r'C:\\ffmpeg\\bin\\ffmpeg.exe'
    directorio_actual = os.path.dirname(os.path.abspath(__file__))
    video_input = os.path.join(directorio_actual, video_input)

    if imageFile == '' :
        comando = [
            ffmpeg_path,
            '-i', video_input,              # Input del video
            '-vf', filtro,                  # Filtro de video para superponer el texto
            '-codec:a', 'copy',             # Copiar el audio sin modificar
            '-stats',                       # Muestra el banner de avance del proceso
            nombreTMP                       # Archivo de salida
        ]
    else :
        comando = [
            ffmpeg_path,
            '-i', video_input,              # Input del video
            '-i', imageFile,                # Input de la imagen
            '-filter_complex', filtro,      # Filtro de video para superponer la imagen
            '-pix_fmt', 'yuv420p',          # Formato de píxeles
            '-codec:a', 'copy',             # Copiar el audio sin modificar
            '-v', 'quiet',                  # Oculta todos los mensajes excepto el banner de progreso
            '-stats',                       # Muestra el banner de avance del proceso
            nombreTMP                       # Archivo de salida
        ]

    try:
        subprocess.run(comando, check=True)
        duracion_ejecucion = time.time() - inicioExec
        minutos = int(duracion_ejecucion // 60)
        segundos = int(duracion_ejecucion % 60)
        milisegundos = int((duracion_ejecucion - int(duracion_ejecucion)) * 1000)
        print(f"run_ffmpeg() OK: {minutos}:{segundos}:{milisegundos}")

        if os.path.exists(video_output): os.remove(video_output)   
        os.rename(nombreTMP, video_output)
        return True
    except subprocess.CalledProcessError as e:
        print(f"run_ffmpeg() Error: {e}")
        return False