Apuntes mientras aprendo sobre software y computadoras.

programación

Guía práctica para la edición audiovisual con FFmpeg

Introducción:

Con esta frase le doy comienzo a la “Guía práctica para la edición audiovisual con FFmpeg”.

Existen muchas opciones de software disponibles para la edición de audio y video.

Algunas de estas opciones son de software libre, otras no. Algunas de estas opciones son gratuitas, otras pueden comprarse y otras solo pueden usarse con un servicio pago de suscripción.

Pero existe una opción que nos permite realizar la totalidad del trabajo de ediciòn audiovisual directamente desde la terminal de nuestro sistema operativo.

Y es que noo se necesita de una interaz gráfica (Graphical User Interface) para utlizar FFmpeg.

En este sentido, este software se distingue por su versatilidad, su velocidad y la posibilidad que presenta para automatizar tareas.

Lo que en otros programas supone incontables pasos, aquí puede resolverse con la ejecución de un solo comando.

En el correr de estos párrafos voy a explicar algunas de las otras ventajas que supone comenzar a usar esta forma de trabajo.

Sin embargo, antes de continuar, creo que es bueno desarrollar tres conceptos administrativos a modo de introducción:

1- La idea central de esta página es la de aprender haciendo. No soy un experto en FFmpeg, simplemente intento aprender un poco mientras escribo.

Y no es la primera vez que hago una experiencia como esta, también escribí la:

2- ¿Qué significa automatizar tareas con FFmpeg? ¿Quiere decir que el programa puede editar un video por su cuenta mientras me relajo haciendo otras cosas?

No, no es para tanto. Bueno otra vez, no soy experto en el tema. Esta simplemente es una opinión impensada.

FFmpeg parece haber sido creado para conseguir muchas cosas. Es una herramienta integral para trabajar con audiovisuales. Pero tener muchas opciones no significa que todas resulten en una idea fácil de poner en práctica.

¿Es posible editar una película de 90 minutos usando solamente la terminal de comandos? En principio la respuesta es si, pero para algo tan complejo tal vez resulte más fácil directamente utilizar un programa como Blender o Kdenlive.

En resumen, cuanto más repetitiva parezca una tarea, más parece brillar FFmpeg en la resolución del problema.

3- Estoy utilizando todo este software en un sistema operativo Linux. Sin embargo tener Linux no es indispensable, se puede usar Ffmpeg en otros sistemas. Ejemplo, en Windows.

La principal diferencia va a estar en que alguno de los comandos si van a ser específicos para Linux.

Ya no quiero extender más la introducción. Por eso, voy a empezar primero averiguando…

¿Cómo instalar FFmpeg?

Bueno, eso depende del sistema operativo.

En Linux, por ejemplo de la vertiente Debian (o distribuciones que usen el comando apt para manejar paquetes), se puede hacer la instalación con:

sudo apt install ffmpeg

De ese modo es como yo lo instalé en Linux Mint.

De necesitar otras opciones, en este link oficial del sitio de FFmpeg se pueden encontrar los archivos ejecutables para descargar.

¿Qué es ffmpeg?

Bueno, tal vez el mejor lugar para empezar este camino sea averiguando qué es FFmpeg.

A partir de ahora voy a leer la versión del manual en Inglés. Voy a citar las partes que me parezcan más interesantes, acompañadas de mi propia traducción hogareña.

Puedo acceer a este mencionado manual desde Linux con el comando:

man ffmpeg

Y lo primero que encuentro al emepezar a leer es:

NAME
ffmpeg – ffmpeg video converter

Manual de ffmpeg

Bueno, bien, esto es: ffmpeg un convertidor de video.

La especialidad de este software es la de convertir video.

Luego sigue:

ffmpeg is a very fast video and audio converter that can also grab from a live audio/video source. It can also convert between arbitrary sample rates and resize video on the fly with a high quality polyphase filter.

manual de ffmpeg

De esto creo que lo más importante es:

  • Si, ffmpeg es un software para convertir audio y video.
  • Puede trabajar también con audio y video de una fuente “en vivo”. Por ejemplo, se puede hacer live streaming de video usando ffmpeg.
  • Se puede convertir (re-codificar) los archivos determinando diferentes sample rates (tasas de muestro) y dimensiones de video.

Algo sobre la sintaxis de comandos en FFmpeg

Lo que sigue a continuación considero que es muy importante.

El manual comienza a decir en qué forma tenemos que construir la sintaxis de los comandos:

ffmpeg reads from an arbitrary number of input “files” (which can be regular files, pipes, network streams, grabbing devices, etc.), specified by the “-i” option, and writes to an arbitrary number of output “files”, which are specified by a plain output url. Anything found on the command line which cannot be interpreted as an option is considered to be an output url.

manual de ffmpeg

Y de esto entiendo lo siguiente:

  • ffmpeg entiende de “archivos de entrada” (input) y “archivos de salida” (output). Yo le doy uno o más inputs y me devuelve uno o más outputs.
  • Estos archivos pueden responder a múltiples fuentes. Ejemplo, archivos regulares o de video en directo.
  • Los archivos de entrada se especifican primero, acompañados de la opción “-i” al escribir el comando.
  • Los archivos de salida solamente se especifican por el nombre que vamos a darles
  • Cualquier cosa que no pueda ser interpretada como una opción, va a ser considerada como un nombre de salida.

Luego el manual continua entregando más información:

Each input or output url can, in principle, contain any number of streams of different types (video/audio/subtitle/attachment/data). The allowed number and/or types of streams may be limited by the container format. Selecting which streams from which inputs will go into which output is either done automatically or with the “-map” option.

manual de ffmpeg

De esto entiendo que:

  • En principio cada entrada o salida puede venir de múltiples fuentes (video, audio, subtítulos, documentos adjuntos o datos).
  • El número o el tipo de esas fuentes puede estar limitado por el formato del contenedor.
  • La selección de que entrada va hacia que salida se hace automáticamente. Aunque es posible dar nuevas direcciones agregando “-map” al momento de ejecutar el comando.

Ahora el siguiente párrafo me dice que los archivos de entrada son numerados por un sistema de índice:

To refer to input files in options, you must use their indices (0-based). E.g. the first input file is 0, the second is 1, etc. (…)

manual de gimp

Esto también lo encontré estudiando lenguajes de programación, por ejemplo Python,

De este modo, el primer input tiene el indice 0, el segundo input tiene el indice 1 y asi sucesivamente. Lo más importante para recordar: el indice comienza desde 0.

Luego sigue:

As a general rule, options are applied to the next specified file. Therefore, order is important, and you can have the same option on the command line multiple times. Each occurrence is then applied to the next input or output file. Exceptions from this rule are the global options (e.g. verbosity level), which should be specified first.

Manual de gimp

Este párrafo vuelve a mencionar la importancia en el orden en el que presentamos primero los archivos de entrada, luego los de salida.

A mi entender, lo que tengo que sacar como información valiosa es lo siguiente:

  • El orden es importante porque las opciones se aplican al siguiente archivo especificado. Primero va la opción, luego va el nombre del archivo de entrada o de salida al que se aplica esa opción.
  • Siendo que la sintaxis es de esta forma, puedo poner la misma opción múltiples veces dentro de un mismo comando. Cada opción va a afectar a un archivo diferente.
  • La excepción a lo anterior son las opciones globales, que afectan a todos los archivos, y tienen que ir al principio del comando.

El párrafo siguiente termina de aclarar estos conceptos:

Do not mix input and output files — first specify all input files, then all output files. Also do not mix options which belong to different files. All options apply ONLY to the next input or output file and are reset between files.

manual de gimp

Y lo importante es:

  • No mezclar los archivos de entrada y de salida. Primero van todos los inputs, luego los outputs.
  • Cada opción solo aplica al siguiente archivo que lo acompaña. El orden es: opción + entrada o salida.
  • Las opciones se reinician en cada entrada/salida que acompañan. Por eso puedo usar la misma opción múltiples veces para archivos distintos.

Luego el manual da algunos ejemplos de como escribir los comandos. Es bastante explicativo, y contiene mucha información valiosa.

Por lo general no me gusta copiar fragmentos enteros de la documentación, pero en este caso creo que ayuda mucho a comprender en que forma funciona el programa.

De todos modos, siendo que la mayoría de las veces la mejor forma de aprender algo es haciendo algo, voy a continuar con…

Cómo conocer la información de un video en ffmpeg

En este momento, la terminal se encuentra trabajando en el mismo directorio donde tengo los videos. Por lo tanto, los resultados obtenidos van a guardarse dentro de la misma carpeta.

Según entiendo por mi lectura de la documentación, ffmpeg entiende de archivos de entrada y archivos de salida. Utilizo la opción “-i” para indicar cuales son los archivos de entrada, los archivos que voy a intervenir, transformar o lo que sea.

Y como archivo de salida (output) se cuenta cualquier otra cosa que no se encuentre acompañada de opciones.

Por esto el orden en el que se escribe el comando es muy importante, porque de eso depende el resultado. Indico cuales son los archivos de entrada (input), luego el resto de las opciones que voy a aplicar y al final señalo los archivos de salida (output) que voy a obtener.

Puedo entender todo esto con un primer comando, que me va a servir para conocer la información de un archivo. Voy a usar de ejemplo un video en mi directorio, un video que se llama “nubes.mp4” y de este modo podré conocer su duración, el bitrate y muchas cosas más.

El comando es:

ffmpeg -i nubes.avi

Solo necesito eso, utilizar la opción “-i” para indicar el input que ffmpeg va leer. Pero como no estoy indicando la creación de un archivo de salida, voy a ver la información de respuesta reflejada en la terminal.

Y esto me da mucha información del video. Parte de la información es la siguiente:

Input #0, avi, from 'nubes.avi':
Metadata:
encoder : Lavf52.64.2
Duration: 00:00:25.01, start: 0.000000, bitrate: 2356 kb/s
Stream #0:0: Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 1920x1080, 2299 kb/s, 30 fps, 30 tbr, 30 tbn, 60 tbc
Stream #0:1: Audio: mp2 (P[0][0][0] / 0x0050), 44100 Hz, stereo, s16p, 64 kb/s
At least one output file must be specified

Voy a intentar hacer una revisión más o menos rápida de algunas partes de esos datos. O al menos las que entiendo en este momento:

  • Input #0, avi, from ‘nubes.avi’: Si recuerdo de antes, cada input sigue un índice que comienza desde cero. Siendo que en este caso estoy presentando un único archivo, su lugar en el índice es el 0.
  • Metadata: incluye información particular del archivo, que responde preguntas sobre su origen. Ejemplo, el año en que fue creado
  • Duration: 00:00:25.01, start: 0.000000: Si, eso mismo, la duración del video.
  • bitrate: 2356 kb/s : tasa de datos por segundo. Básicamente la cantidad de información del video, cuanto mayor el número mayor la calidad.
  • Video: h264 (High): el codec (codificador-decodificador) del video. Aunque el contenedor del video es “.avi”, el codec es h264. Es lo que especifica la forma en que se comprime la información.
  • 1920×1080: la resolución en pixeles del video.
  • 30 fps: la cantidad de cuadros por segundo (frames per second) del video. Un segundo del video contiene 30 fotogramas individuales. En teoría, cuantos más fotogramas más fluido se ve el movimiento de las imágenes.
  • Audio: mp2 (P[0][0][0] / 0x0050), 44100 Hz, stereo, s16p, 64 kb/s: Algunos detalles del audio.

Con todo, para no detenerme demasiado en esto, quiero repasar especialmente un detalle muy importante.

Canales y más canales:

Voy a detenerme en la siguiente pieza de datos:

Stream #0:0: Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 1920x1080, 2299 kb/s, 30 fps, 30 tbr, 30 tbn, 60 tbc
Stream #0:1: Audio: mp2 (P[0][0][0] / 0x0050), 44100 Hz, stereo, s16p, 64 kb/s

Aquí encuentro:

Stream #0:0: Video:
Stream #0:1: Audio:

Tengo que prestar atención que un mismo archivo “nubes.avi” (en este caso de índice #0) tengo dos streams separados. Como si fueran dos canales que se bifurcan de una misma fuente.

Esto es, dentro de “nubes.avi” tengo una primera canal de video que también tiene un índice #0. Por eso esta denominada con “#0:0:”.

Y luego tengo un segundo canal de audio, que tiene un índice #1. Esta viene denominada con el código “#0:1:”.

El manual dice:

(…) streams within a file are referred to by their indices. E.g. “2:3” refers to the fourth stream in the third input file (…)

manual de ffmpeg

Esto es, los streams dentro de un archivo tienen sus propios índices. Por ejemplo si encuentro el dato “2:3”, esto se refiere se refiere al cuarto stream (índice #3) en el tercer input (de índice #2).

Para intentar reducir mi propia confusión, voy a traducir el termino “streams” por la palabra “canales”.

Si estuviera usando un editor gráfico como Adobe Premiere o Kdenlive para estudiar “nubes.avi”, podría ver en la linea de tiempo del editor dos canales distintos: uno para el video y otro para el audio. Esto es básicamente lo mismo, son canales o pistas de información dentro de un archivo.

Volviendo al ejemplo original, podría agregar un tercer canal a mi input “nubes.avi”. Digamos que es un nuevo canal de audio. El indice de este tercer canal va a ser “0:2”.

¿Por qué importa todo esto? Bueno, esto es solo una suposición. Pero creo que si escribo un comando lo bastante complejo, voy a poder intervenir distintos canales de una sola pasada.

Por ejemplo, podría subir el volumen de un canal de audio y bajar el de otro al mismo tiempo refiriéndome a cada uno por su propio índice.

Cómo convertir formatos de video en FFmpeg

En el ejemplo del comando anterior, al final de la devolución de la consola, el texto me dice:

At least one output file must be specified

Esto quiere decir: Al menos un archivo de salida tiene que ser especificado.

Siendo que ffmpeg no encuentra un archivo de salida para “resolver” la entrada, en este caso me devuelve los datos directamente en la terminal.

Para empezar a resolver esto, voy a agregar un nombre de salida. Mi objetivo es convertir un video, cambiando de alguna forma alguna de sus características.

Cambiar formato de video con mismo codec en FFmpeg:

En este caso, mi objetivo es convertir un video en formato “.avi” a otro en formato “.mp4” usando solamente la terminal.

Para no confundirme, voy a empezar a usar los comando sin opciones y luego voy a agregar nuevas variables gradualmente.

Entonces, tengo mi archivo original “nubes.avi” (aunque me pregunto si alguien realmente sigue usando ese tipo de formato “.avi”… no importa, sirve para el ejemplo).

Voy a simplemente escribir el nombre del archivo de entrada, acompañado de la opcion “-i”, seguido del nombre del nuevo archivo de salida: “nuevas_nubes.mp4”.

No recuerdo si lo mencioné antes, pero la “i” de la opcion “-i” quiere decir “input” o “entrada”. Es por eso que es la opción que acompaña al nombre del archivo de entrada para señalarlo.

Como es de esperar, mi objetivo es tener un nuevo video, pero ahora con .mp4 por formato contenedor.

Me pregunto que puedo ocurrir…

ffmpeg -i nubes.avi nuevas_nubes.mp4

Bueno, para empezar la terminal se queda trabajando unos minutos en resolver el pedido. A la vez, nos dice cuanto va a llevar el asunto, la cantidad de frames que repasa y otros datos del tema.

El resultado es que ahora tengo dos archivos en mi directorio. Uno de ellos es el original “nubes.avi” y el otro es “nuevas_nubes.mp4”.

Voy a revisar la información de cada uno individualmente, copiando la parte que considero importante:

ffmpeg -i nubes.avi
Input #0, avi, from 'nubes.avi':
encoder : Lavf52.64.2

Stream #0:0: Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 1920x1080, 2299 kb/s, 30 fps, 30 tbr, 30 tbn, 60 tbc
Stream #0:1: Audio: mp2 (P[0][0][0] / 0x0050), 44100 Hz, stereo, s16p, 64 kb/s

Y el nuevo archivo:

ffmpeg -i nuevas_nubes.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'nuevas_nubes.mp4':
encoder : Lavf58.29.100

Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080, 3761 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default)
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default)

Bueno, puedo comprender que el contenedor efectivamente es distinto. Ahora es parte de la familia de los .mp4 en el nuevo caso.

Pero si reviso lado a lado la información del codec, cantidad de cuadros por segundo y resolusion parecen ser compartir muchos detalles:

Video original:

Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 1920x1080, 2299 kb/s, 30 fps, 30 tbr, 30 tbn, 60 tbc

Video convertido:

Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080, 3761 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default)

Así que creo todo esto es muy bueno para empezar. Ya conseguí empezar a usar ffmpeg, y pude convertir un archivo de entrada en un archivo de salida con un contenedor diferente.

Pero me gustaría empezar con algunas cosas más arriesgadas… ¿Qué más puedo hacer con ffmpeg?

Cómo cortar un fragmento de un video en FFmpeg

Hasta ahora estuve trabajando con un video de dimensiones 1920×1080. Por otra parte, la duración del video es de más de un minuto y su peso en el disco es de 14 megabytes.

La conversión a .mp4 hizo que el video pasara a ocupar 21 mb. La duración y sus dimensiones se mantuvieron iguales, ya que no agregué ninguna opción para modificarlas. Un proyecto audiovisual puede llegar a ocupar cientos de gigas de memoria… unos simples 20 megas difícilmente vayan a significar algún tipo de problema.

Ahora quiro cortar un fragmento de ese video. Digamos, un fragmento de diez segundos. En resumidas cuentas, la tarea de editar un trabajo audiovisual se ocupa de tomar múltiples fragmentos pequeños para crear una obra más grande.

Para hacer esto, lo principal es conocer que momento del video quiero. Y eso lo expreso con el timecode o “código de tiempo”, básicamente el momento exacto expresado en horas, minutos, segundos y centésimas de segundo.

Quiero quedarme con los primeros diez segundos del video. Esto siguiendo el time code significa ir desde el segundo cero (00:00:00.000) hasta el segundo diez (00:00:10.000). Siendo que solamente necesito diez segundos, no tengo que agregar nada en los minutos o las horas.

El comando queda entonces de este modo:

ffmpeg -ss 00:00:00.000 -t 00:00:10.000 -i nuevas_nubes.mp4 -c copy corte_nubes.mp4

El inicio lo señalo con la opción “-ss” (start time), que significa literalmente “momento de comienzo”. Esta opción tiene que ir antes del -i, y por tanto antes del nombre del archivo de entrada.

La opción “-t” (duration) marca la duración del video.

En mi caso, la duración del corte es de diez segundos empezando desde el segundo cero. Pero digamos, puedo cortar empezando en el segundo 26 y hacer que el corte dure 14segundos.

Ahora el comando queda de este modo:

ffmpeg -ss 00:00:26.000 -t 00:00:14.000 -i nuevas_nubes.mp4 -c copy corte_nubes.mp4

Y el resultado es un corte de 8 segundos de video. Eso es, tengo un plano que va entre el segundo 20 y el segundo 40 del video original.

Tengo que recordar que si uso un nombre para un archivo de salida que es igual al nombre de un archivo ya existente en el directorio, ffmpeg me va a preguntar si quiero sobre escribirlo.

Por último tengo la opción “-c” que signfica copy o copia. Según el manual de instrucciones de ffmpeg, esta opción quiere decir:

special value “copy” (output only) to indicate that the stream is not to be re-encoded.

manual de ffmpeg

Esto es que, el valor especial copy indica que los canales no tienen que ser re codificados.

La idea es que de este modo ffmpeg copia los canales del archivo, en este caso un canal de audio y otro de video y los retiene en el archivo de salida sin volver a codificarlos.

El resultado es un nuevo archivo, un corte del video original con sus nuevas características que ya puedo usar para otras cosas.

Cómo crear un loop de video

En el manual puedo encontrar lo siguiente:

-stream_loop number (input)
Set number of times input stream shall be looped. Loop 0 means no loop, loop -1 means infinite loop.manual de ffmpeg

Según mi propia traducción, esto quiere decir que necesito acompañar mi comando con la opción “-stream_loop” seguida de un número, que indica la cantidad de repeticiones.

El número 0 indica cero repeticiones, -1 indica infinitas repeticiones y otro número entero positivo para esa cantidad determinada de repeticiones.

La opción de infinitas repeticiones puede servir por ejemplo para rotar por los videos de una lista, comúnmente conocida como una “playlist”.

En su versión más sencilla, el comando tiene que quedarme de esta forma:

ffmpeg -stream_loop 2 -i ejemplo01.mp4 -c copy resultado.mp4

Lo que quiere decir todo esto, leído de izquierda a derecha, es que:

  • Primero voy a realizar un loop de 2 vueltas para el video “ejemplo01.mp4”.
  • Luego con eso voy a crear un nuevo video, sin modificar los codecs del original (sin re-encoding), con el nombre de “resultado.mp4”.
  • Al final el video va a reproducirse tres veces. Digamos que el video original dura 5 segundos, el final va a durar 15. La duración del original más las dos repeticiones.

Proyectos prácticos de edición audiovisual con FFmpeg

En esta sección práctica voy a dedicarme a revisar algunos proyectos prácticos, usando FFmpeg para automatizar algunas tareas de la edición audiovisual.

Editar toda una lista de videos con FFmpeg

Se puede leer sobre este proyecto siguiendo este enlace: Cortar y editar videos de forma automática con FFmpeg

La idea central es utilizar diferentes herramientas para conseguir editar todo un conjunto de videos utilizando solamente la terminal de Linux.

Para esto voy a necesitar los vidos que quiero cortar, algunos archivos de texto y unos pocos comandos de Bash.

Cortar la claqueta de un video con Python y FFmpeg

Se puede leer sobre este proyecto aquí: Remover claqueta de un video de manera automática.

Mi objetivo con ese proyecto es encontrar el momento en el que la claqueta hace “click” al cerrarse por medio del sonido. De esa forma es posible identificar el momento en el que el plano comienza, para descartar todo lo que queda por delante.

Creo que es una idea interesante, lamentablemente no funciona muy bien. Pero se puede mejorar, y sirve para aprender un poco más del tema.

Conclusión

Este es el cierre de la Guía práctica para la edición audiovisual con FFmpeg. Aunque seguramente voy a ir ampliándo el texto poco a poco, porque todavía hay muchos temas para cubrir.

Me gustaría en alguna oportunidad utilizar FFmpeg para editar algún tipo de proyecto grande con esta herramienta, para poder contar la experiencia.

Mientras tanto, por favor avisame si encontrás algún error en el material para que pueda resolverlo.

La seguimos en el próximo apunte.

1 Comment

  1. José Luis

    Excelente Aportación !

    Muchas Gracias !!!

Leave a Reply