Determinar automáticamente el desfase de audio en un fichero MKV

En Cómo corregir la desincronización de audio en un fichero Matroska explico cómo generar un fichero Matroska con los audios sincronizados, pero requiere determinar la desincronización precisa para poder compensarla. Este proceso suele ser bastante sencillo, pero requiere dedicarle unos minutos. La inmensa mayoría de las películas no tienen problemas así que la tentación es no comprobarlo de forma sistemática y, como no puede ser de otra manera, nos saltaremos justo la película que está mal.

Las causas de la desincronización son varias. En contenedores anticuados como AVI, usar sonido VBR o, incluso ABR, es una receta para el desastre. Afortunadamente esos contenedores están despareciendo (poco a poco, eso sí) y Matroska, el nuevo estándar, soporta audio VBR sin problemas merced al uso de códigos de tiempo en las pistas. Lo que está muy bien, porque Opus genera VBR por defecto.

No obstante, ocasionalmente, tenemos problemas de desincronización de audio y vídeo cuando generamos un fichero Matroska a partir de otro. ¿Por qué?.

En mi experiencia, la causa fundamental de generar un MKV desincronizado es que el audio original dura menos tiempo que el vídeo y empieza "retrasado" respecto al vídeo. ¿Cómo puede ser eso?. Posiblemente porque se ha mezclado un vídeo con un audio de una fuente diferente. Por ejemplo, una película en inglés a la que se le une un audio en castellano tomado de una fuente alternativa con una duración distinta. El "ripper" original sincronizó las diferentes pistas para tener en cuenta este hecho, pero cuando yo recodifico la película pierdo esos ajustes y mi audio queda desincronizado.

¿Y si pudiese extraer el ajuste de desfase de los audios de la película original y aplicarlo tal cual en mi versión recodificada?

Una búsqueda por internet muestra poco o nada utilizable. Sorpresa. Me lo tengo que currar yo.

Tras leer y experimentar mucho, llego a este comando:

$ ffmpeg -i ORIGEN.mkv -af "ashowinfo" -map 0:1 -y -f alaw -frames:a 1 /dev/null

Este comando emplea el filtro de audio ashowinfo para mostrar información de cada frame de sonido. Con frames:a 1 indicamos que nos interesa solo información del primero, no de toda la película. Uso /dev/null porque no me interesa el resultado de la transcodificación falsa que hemos solicitado. El -y es para no tener un error debido a que /dev/null ya existe. Uso el códec alaw porque es muy rápido (no tiene compresión) y, como he dicho, no nos interesa esa transcodificación de un único frame de sonido.

Un parámetro crítico es -map 0:1, que indica qué canal de audio nos interesa.

Tenemos que ejecutar este comando por cada canal de audio de la película original, ajustando el parámetro -map convenientemente. El resultado se aplica como expliqué en el artículo Cómo corregir la desincronización de audio en un fichero Matroska.

Veamos un ejemplo real:

$ ffmpeg -i FUENTE.mkv -af "ashowinfo" -map 0:1 -y -f alaw -frames:a 1 /dev/null 2>&1 | grep Parsed_ashowinfo
[Parsed_ashowinfo_0 @ 0x2791020] n:0 pts:23664 pts_time:0.493 pos:2514526 fmt:fltp channels:1 chlayout:mono rate:48000 nb_samples:960 checksum:DC647747 plane_checksums: [ DC647747 ]
$ ffmpeg -i FUENTE.mkv -af "ashowinfo" -map 0:2 -y -f alaw -frames:a 1 /dev/null 2>&1 | grep Parsed_ashowinfo
[Parsed_ashowinfo_0 @ 0xad3020] n:0 pts:71664 pts_time:1.493 pos:2628216 fmt:fltp channels:1 chlayout:mono rate:48000 nb_samples:960 checksum:4B86407B plane_checksums: [ 4B86407B ]

En esta película en concreto, el primer canal de audio tiene un desfase de 0.493 segundos y el segundo canal de sonido tiene un desfase de 1.493 segundos. Observa los campos pts_time.

Estos valores se meten en Cómo corregir la desincronización de audio en un fichero Matroska o se aplican directamente cuando se mezclan todas las pistas de audio, vídeo y subtítulos tras la transcodificación de la película.

Problema solucionado.

Note

Observa que los tiempos que tenemos aquí vienen expresados en segundos, pero en Cómo corregir la desincronización de audio en un fichero Matroska tenemos que usar tiempos en milisegundos. Supongo que no tengo que explicar cómo se hace la conversión, ¿no? :-)