es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Optimización de FFMPEG al volver a codificar un archivo mkv a mp4 con subtítulos incrustados.

He escrito un programa con python, ffmpeg y la terminal de Linux.
Funciona, pero es muy lento…
Y quiero optimizar mi código, pero no sé qué podría hacer para hacerlo.
Mi código:

“`
import os

def extractSub():
"""
Esta función extrae los subtítulos de archivos mkv y los coloca en la carpeta mp4Folder.
"""
dirLocate = "./mkvFolder/"
for filename in os.listdir(dirLocate):
if(filename.endswith(".mkv")):
os.system("ffmpeg -i {0} -map 0:s:0 {0}.ass".format(dirLocate + filename))

def burnSub():
"""
Esta función quema/hardsub los subtítulos extraídos con la función extractSub() y los coloca en el archivo mp4 final.
"""
dirLocate = "./mkvFolder/"
mp4Locate = "./mp4Folder/"
for filename in os.listdir(dirLocate):
if(filename.endswith(".mkv")):
os.system("ffmpeg -i {0} subtitles={0} {1}.mp4".format(dirLocate + filename, mp4Locate + filename[0:-4]))

start = time.time()

extractSub()
burnSub()

print("El tiempo utilizado para ejecutar esto se muestra a continuación")

end = time.time()“`

Espero haber dado suficientes detalles…

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Código posible optimizado:

    import os
    import time
    import subprocess
    
    def extraer_y_quemar_subs():
        """
        Esta función extrae los subtítulos de los archivos .mkv en el directorio mkvFolder,
        y los quema en archivos .mp4 en el directorio mp4Folder, utilizando FFmpeg.
        """
        directorio_mkv = "./mkvFolder/"
        directorio_mp4 = "./mp4Folder/"
        for nombre_archivo in os.listdir(directorio_mkv):
            if nombre_archivo.endswith(".mkv"):
                archivo_mkv = os.path.join(directorio_mkv, nombre_archivo)
                archivo_ass = os.path.join(directorio_mp4, nombre_archivo[:-4] + ".ass")
                archivo_mp4 = os.path.join(directorio_mp4, nombre_archivo[:-4] + ".mp4")
                # extraer subtítulos al archivo .ass
                subprocess.run(["ffmpeg", "-y", "-i", archivo_mkv, "-map", "0:s:0", archivo_ass], check=True)
                # quemar subtítulos en el archivo .mp4
                subprocess.run(["ffmpeg", "-y", "-i", archivo_mkv, "-vf", "subtitles=" + archivo_ass, archivo_mp4], check=True)
    
    start_time = time.monotonic()
    
    extraer_y_quemar_subs()
    
    print("Tiempo de ejecución: {:.2f} segundos".format(time.monotonic() - start_time))
    

    Explicaciones y mejoras:

    • El código original define dos funciones separadas, una para extraer los subtítulos y otra para quemarlos en los archivos .mp4. Sin embargo, ambas operaciones siempre se realizan juntas, en el mismo bucle que itera sobre los archivos en el directorio mkvFolder. Por lo tanto, es más eficiente combinarlas en una sola función. Esto también evita la redundancia de E/S de archivos y reduce la duplicación de código.

    • El código original utiliza la función os.system() para ejecutar comandos de FFmpeg. Si bien esto funciona, no es recomendable porque implica lanzar un nuevo proceso de shell para cada comando, lo que puede ser lento y poco seguro. Es mejor utilizar una biblioteca de Python como subprocess, que permite un mayor control y flexibilidad sobre la gestión de procesos. En este caso, como los comandos son simples y no requieren interacción, podemos utilizar la función subprocess.run(), que es un envoltorio de alto nivel alrededor de Popen().

    • El código original no maneja errores ni verifica los códigos de retorno de los comandos de FFmpeg. Esto puede provocar fallos silenciosos o resultados inesperados si faltan los archivos de entrada, los directorios de salida no tienen permisos de escritura o las versiones/opciones de FFmpeg no son compatibles. Para evitar esto, podemos agregar comprobación de errores y registro al código. Por ejemplo, podemos usar la opción -y en los comandos de FFmpeg para sobrescribir los archivos de salida existentes sin preguntar, y podemos usar la excepción subprocess.CalledProcessError para capturar cualquier código de retorno distinto de cero e imprimir un mensaje de error.

    • El código original utiliza la concatenación de cadenas (+) para construir los nombres de archivo de entrada y salida a partir de las cadenas de directorio y extensión. Si bien esto funciona, puede provocar errores y vulnerabilidad a ataques de inyección si los nombres de archivo de entrada contienen caracteres especiales o espacios en blanco. Para evitar esto, podemos utilizar la función os.path.join() para concatenar de forma segura los componentes de la ruta, y podemos utilizar la notación de corte [:-4] para eliminar la extensión ‘.mkv’ del nombre de archivo y reemplazarla por ‘.ass’ o ‘.mp4’.

    • El código original no mide ni informa adecuadamente el tiempo de ejecución. Para solucionarlo, podemos utilizar la función time.monotonic() para medir el tiempo transcurrido de manera más precisa y fiable que la función time.time() (que puede verse afectada por la desviación del reloj o los cambios del sistema). También podemos formatear el mensaje de salida para incluir el tiempo de ejecución en segundos con dos decimales, lo cual es más legible e informativo que una marca de tiempo simple.

Comments are closed.