¿Cómo evito que los objetos tengan bordes parpadeantes cuando codifico una grabación de pantalla en mp4 usando x264 y ffmpeg?

La meta

Estoy creando tutoriales cortos grabando mi pantalla (720p30fps). El contenido es solo una grabación del uso de algún software de escritorio, por lo que se mueve el mouse y se abren cuadros de diálogo, se escribe, se dibujan formas, etc. Después de la edición básica de la grabación de pantalla original, quiero codificar los videos en formatos webm y mp4 con exactamente la misma resolución y velocidad de fotogramas que grabé y quiero que la calidad de reproducción sea lo más cercana posible a la calidad de grabación original. Por lo tanto, no debería haber artefactos de compresión visibles después de la codificación final.

Respecto a la grabación original: La resolución es de 1280x720 píxeles. La velocidad de fotogramas es de 30 fotogramas por segundo. El software de grabación es Simple Screen Recorder. La codificación es H.264 con un factor de velocidad constante de 10 (lo suficientemente cerca como para no tener pérdidas) en un contenedor Matroska. La reproducción del original no muestra artefactos en absoluto.

Luego agrego algunos efectos muy básicos como fundidos cruzados y superposiciones de texto al MKV original usando Blender. Luego renderice en una secuencia de imágenes (formato PNG). Utilizo la exportación de imágenes de alta calidad desde Blender y, nuevamente, las imágenes de salida no muestran signos de artefactos de compresión.

El problema

El problema ocurre después de codificar la secuencia de imágenes en un video mp4 usando el codificador x264 con ffmpeg. A veces, algunas partes de algunos de los bordes duros/contornos de formas parpadean. Estos no son objetos en movimiento. Están inmóviles en el fondo en comparación con el lugar donde se mueve el mouse. Parece aleatorio. Por ejemplo, podría tener dos rectángulos amarillos uno al lado del otro, cada uno con un contorno negro y solo la parte superior de uno de los contornos del rectángulo parpadea durante algunos fotogramas y luego se detiene. Distrae un poco y es feo para lo que debería ser una codificación realmente limpia/fácil basada en una grabación de pantalla.

Generalmente veo esto por las líneas oscuras horizontales / bordes duros. De vez en cuando he visto similares para los bordes verticales.

Todavía no he notado este problema con los archivos webm de salida producidos por ffmpeg, solo con los archivos mp4 codificados x264. El comando ffmpeg que uso es así:

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -pix_fmt yuv420p -movflags +faststart -crf 22 -r 30 -g 300 -y myvideo.mp4

Para webm, uso el mismo comando básico y el mismo valor crf, pero obviamente un codificador diferente (libvpx-vp9). El archivo webm tiene aproximadamente 9 MB o más por 2 minutos de metraje. El mp4 es de unos 3,5 MB. Generalmente, la calidad entre los dos es imposible de distinguir, solo en aquellos momentos en los que el mp4 tiene algunos segmentos de líneas duras que parpadean.

He ilustrado aproximadamente el problema a continuación usando arte ASCII, donde estoy tratando de mostrar un rectángulo en cuatro marcos. Al principio está bien, luego para algunos marcos, parte del contorno del rectángulo se dibuja con un poco de grosor adicional en algunas partes, lo que hace que parezca parpadear o moverse un poco. Luego vuelve a ser perfecto de nuevo.

-----------------
|               |
-----------------


------=====------
|               |
-----------------


______=====______
|               |
-----------------


-----------------
|               |
-----------------

Ejemplos

Compara las siguientes imágenes. Observe en la segunda imagen que los 2 rectángulos inferiores tienen una línea gris adicional arriba o debajo de parte de su borde negro. Capturé estas imágenes mientras VLC estaba reproduciendo mi mp4 codificado x264 al 100% (sin escala). Este no es un ejemplo realmente dramático, pero es de esperar que sea suficiente para mostrar lo que está pasando. Debido a que las líneas adicionales aparecen y desaparecen, se crea una especie de efecto de parpadeo.

Notas:

  1. La grabación se capturó usando Simple Screen Recorder en una pantalla de 1280x720 con LibreOffice Draw maximizado. No puede ver artefactos en la grabación original, ni en la secuencia de imágenes PNG que exporté de Blender.
  2. El comando que usé para generar el video mp4 fue:ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -coder ac -pix_fmt yuv420p -movflags +faststart -crf 22 -bf 16 -b_strategy 2 -refs 16 -direct-pred spatial -me_method umh -me_range 16 -r 30 -g 300 -y myvideo.mp4

captura de pantalla cuando no se muestran artefactos al reproducir mp4 en VLC

captura de pantalla cuando aparecen artefactos mientras se reproduce el mismo mp4 en VLC

(Es más fácil notar los artefactos si alterna entre las dos imágenes en tamaño original)

Preguntas

¿Cómo se llama este tipo de artefacto de compresión?

¿Qué indicador puedo pasar ffmpeg para intentar suprimirlo?

Lo que he probado hasta ahora

Mucha experimentación con el CRF y los valores de velocidad. Para CRF he bajado a 18 y eso parece reducir un poco el parpadeo, pero no eliminarlo. Pero ese factor de velocidad parece demasiado bajo para obtener una codificación decente de contenido tan simple como una grabación de pantalla de acción baja en 720p.

También intenté establecer manualmente marcos b y marcos de referencia en valores entre 1 y 16, varí el tamaño de gop entre 30 y 300 y probé diferentes valores para la opción -me_method. Y probé un par de configuraciones de desentrelazado a través de la opción yadif, pero estoy bastante seguro de que eso no es relevante para este problema y solo parece empeorar la calidad del video de salida. Terminé con comandos ffmpeg que se parecen más a esto:

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -coder ac -pix_fmt yuv420p -movflags +faststart -crf 22 -bf 16 -b_strategy 2 -refs 16 -direct-pred spatial -me_method umh -me_range 16 -r 30 -g 300 -y myvideo.mp4

Pero hasta ahora, solo variar el valor CRF ha tenido un efecto notable. No quiero bajar mucho más, porque va a aumentar demasiado el tamaño del video.

Actualización: resultados de la prueba

Usé el ejemplo anterior con LibreOffice Draw y una pantalla de 1280x720p para grabar un clip de aproximadamente 47 segundos en un archivo MKV codificado en H.264. Importé esto a Blender y lo exporté directamente como una secuencia de imágenes PNG, sin efectos ni ediciones. Luego ejecuté la secuencia de imágenes a través de ffmpeg con el codificador x264 para producir el archivo de video mp4.

Empecé a variar mi comando ffmpeg original un parámetro a la vez para ver la diferencia. Me concentré principalmente en el valor CRF, porque esa era la única forma en que definitivamente podía ver una reducción en los artefactos de compresión. Incluso con el CRF establecido en 10, todavía podía ver el extraño parpadeo/bandas en los bordes en un par de lugares. Finalmente, bajé el valor a 5 y no noté ningún artefacto en absoluto. El valor ideal es posiblemente entre 5 y 10 para este contenido.

Eso me dejó con un video que es demasiado grande para lo que es. Después de jugar con los valores del marco B y del marco de referencia para lograr reducciones marginales en el tamaño del archivo, finalmente volví a variar el valor de gop y eso tuvo un impacto relativamente grande. Establecer el gop en 300 en lugar de 30, básicamente redujo a la mitad el tamaño del archivo. Así que mi último comando ffmpeg fue este:

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -crf 5 -bf 16 -refs 16 -r 30 -g 300 -me_method umh -me_range 16 -y myvideo.mp4

(Actualización: originalmente publiqué el comando anterior como -crf 10... ahora corregido a -crf 5)

Los principales parámetros que eliminé fueron:

  • -pix_fmt yuv420p(podría degradar la calidad)
  • -coder ac(No pude ver ningún impacto positivo con o sin esto)
  • -b_strategy 2(esto en realidad aumentó ligeramente el tamaño del archivo en algunos casos)
  • -direct-pred spatial(Absolutamente ningún impacto en la calidad/tamaño del archivo de salida que pude observar)

Si estuviera entregando el archivo de salida a YouTube, consideraría volver a colocar -pix_fmt yuv420pand -coder acy -flags cgop, solo porque los recomiendan.

Estos parámetros produjeron una mejora/disminución marginal en el tamaño del archivo (como en fracciones de 1 MB):

  • -bf 16
  • -refs 16
  • -me_method umh -me_range 16

Puede ver la variación en el tamaño del archivo en la pantalla a continuación, donde nombré cada archivo de salida según los parámetros de ffmpeg que varié. Cualquier cosa que no tenga crf05 en el nombre manifiesta el problema de los artefactos.

Archivos de salida creados al variar los parámetros ffmpeg y x264

Como comparación, ejecuté un comando similar para producir un archivo webm, pero con un valor CRF mucho peor/más alto. Con un valor CRF de 22, el archivo mp4 resultante tenía 1,4 MB sin artefactos de compresión perceptibles en absoluto. El comando ffmpeg que usé fue:

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libvpx-vp9 -b:v 0 -tile-columns 2 -crf 22 -r 30 -g 300 -y myvideo.webm

Actualización: más resultados de pruebas

Siguiendo las sugerencias de stib y Mulvya, probé las opciones y y varí el -tunevalor entre 5 y 10. Hice lo mismo para mis propios experimentos con las opciones y . La salida mp4, basada en mi video mkv de entrada, tenía artefactos de compresión visibles y parpadeantes en el mismo lugar hasta que bajé el crf a 6 cuando usaba , y a 5 con las opciones o cuando usaba-trelliscrf-me_method-bf-trellis-bf -refs-tune. Aquí hay capturas de pantalla que muestran la parte final de los artefactos que estoy tratando de eliminar mientras uso valores de CRF más altos. La primera pantalla es el video mkv de entrada, la segunda es un video mp4 codificado de muestra creado con la opción -trellis 0. Puede ver los artefactos en el borde superior de uno de los rectángulos. (También hay una banda gris gruesa en el fondo negro; sin embargo, eso no es un artefacto de compresión, creo que es un diálogo en proceso de desaparecer después de hacer clic en cancelar en mi grabación de demostración / muestra).

parte del cuadro en el mkv vido original sin artefactos de compresión

casi la misma parte del video mp4 codificado, mostrando artefactos de compresión

Aquí están los comandos y los tamaños de archivo resultantes

-tune animación:

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -crf 5 -tune animation -r 30 -g 300 -y myvideo.mp4
Tamaño: 2.757.370
Artefactos visibles: No

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -crf 6 -tune animation -r 30 -g 300 -y myvideo.mp4
Tamaño: 2,593,711
Artefactos visibles:

-enrejado 0:

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -crf 6 -pix_fmt yuv420p -x264opts trellis=0 -r 30 -g 300 -y myvideo.mp4
Tamaño: 2.214.534 bytes
Artefactos visibles: No

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -crf 7 -pix_fmt yuv420p -x264opts trellis=0 -r 30 -g 300 -y myvideo.mp4
Tamaño: 2 074 544 bytes
Artefactos visibles: (aunque una cantidad minúscula, literalmente, unos pocos píxeles en una fracción de segundo)

-fast_skip 0:

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -crf 7 -pix_fmt yuv420p -x264opts fast_pskip=0 -r 30 -g 300 -y myvideo.mp4
Tamaño: 2.012.596 bytes
Artefactos visibles: No

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -crf 8 -pix_fmt yuv420p -x264opts fast_pskip=0 -r 30 -g 300 -y myvideo.mp4
Tamaño: 1.883.919 bytes
Artefactos visibles: (cantidad muy pequeña/breve)

-psy 0 -fast_pskip 0:

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -crf 7 -pix_fmt yuv420p -x264opts psy=0:fast_pskip=0 -r 30 -g 300 -y myvideo.mp4
Tamaño: 1 886 919 bytes
Artefactos visibles: No

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -crf 8 -pix_fmt yuv420p -x264opts psy=0:fast_pskip=0 -r 30 -g 300 -y myvideo.mp4
Tamaño: 1.764.771 bytes
Artefactos visibles:

-bf -refs:

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -crf 5 -bf 16 -refs 16 -r 30 -g 300 -me_method umh -me_range 16 -y myvideo.mp4
Tamaño: 2.257.420 bytes
Artefactos visibles: No

ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -crf 6 -bf 16 -refs 16 -r 30 -g 300 -me_method umh -me_range 16 -y myvideo.mp4
Tamaño: 2 106 439 bytes
Artefactos visibles: (aunque una cantidad minúscula)

-desbloquear:
ffmpeg -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -preset veryslow -crf 14 -pix_fmt yuv420p -x264opts fast_pskip=0:psy=0:deblock=-3,-3 -r 30 -g 300 -y myvideo.mp4
Tamaño: 982.549 bytes
Artefactos visibles: No

ffmpeg -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -preset veryslow -crf 14 -pix_fmt yuv420p -x264opts fast_pskip=0:psy=0:deblock=-3,-3 -bf 16 -refs 16 -me_method umh -me_range 64 -r 30 -g 300 -y myvideo.mp4
Tamaño: 961.460 bytes
Artefactos visibles: No

Conclusión

O el codificador vp9 es mucho mejor para manejar este tipo de contenido de video, o hay algún parámetro x264 que no conozco que mejoraría las cosas. ¿Alguien tiene alguna idea sobre una opción que pueda darle a ffmpeg para obtener un mejor resultado con x264/mp4? (Y no estoy tratando de iniciar una guerra de códecs, ¡necesito publicar en ambos formatos!)

Los comentarios no son para una discusión extensa; esta conversación se ha movido a chat .

Respuestas (2)

El parámetro que parece tener el mayor impacto en la eliminación de este artefacto (por el tipo de contenido en la publicación original), al tiempo que permite valores de CRF más altos, es deblock. El uso -deblock -3,-3permitió un valor CRF de 14 con los artefactos completamente eliminados. El uso de valores CRF de 15 y 16 vuelve a introducir el artefacto, aunque en pequeñas cantidades y no se nota mucho a menos que lo esté buscando específicamente. También los valores de desbloqueo de -3 -1 y -3 -0 funcionaron razonablemente bien, pero no eliminaron el artefacto por completo en CRF 14.

El comando final fue:

ffmpeg -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -preset veryslow -crf 14 -pix_fmt yuv420p -x264opts fast_pskip=0:psy=0:deblock=-3,-3 -r 30 -g 300 -y myvideo.mp4

Para exprimir un poco más de kb de compresión, puede leer bf refs me_method umhy agregar los me_rangeparámetros. Pero descubrí que necesitaba el me_rangeconjunto en 64. Los valores menores reintroducirían el artefacto de compresión.

Nota: Tuve cierto éxito con la variación aq-modey aq-strength, pero eso no fue tan bueno como el desbloqueo para eliminar los artefactos.

Se agregaron los resultados del uso de este enfoque a la publicación original, para compararlos con los otros métodos.
Gracias por trabajar en esto. Creo que es probable que el codificador no pueda decidir hacerlo con el negro y, debido a la aleatoriedad según la necesidad del codificador, termina siendo aleatorio en esa área. Creo que puede ser útil limpiar el OP ahora, para que otros no tengan que leerlo, puede ayudar a obtener alguna respuesta. He estado allí. Se necesita mucha moderación cuando se hace una buena pregunta. No es fácil con algo tan complicado. ¿Podría tal vez incluso combinar respuestas, si están relacionadas? solo un pensamiento.

Varíe el valor de CRF hacia abajo hasta que no vea artefactos de compresión. Vaya ridículamente bajo si es necesario, como CRF = 5.

Varíe el valor de GOP para compensar el mayor tamaño del archivo de salida. Por ejemplo, para video a 30 cuadros por segundo, intente gop=300

Su comando ffmpeg podría verse así:ffmpeg -speed 1 -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -crf 5 -bf 16 -refs 16 -r 30 -g 300 -me_method umh -me_range 16 -y myvideo.mp4

Editar: (Usando la opción de velocidad correcta para x264, agregando pix_fmt y otras opciones x264)

ffmpeg -framerate 30 -f image2 -i frames/%04d.png -c:v libx264 -movflags +faststart -preset veryslow -crf 5 -pix_fmt yuv420p -x264opts fast_pskip=0:psy=0 -bf 16 -refs 16 -me_method umh -me_range 16 -r 30 -g 300 -y myvideo.mp4

Básicamente estoy renunciando a resolver este problema de una manera sensata. Esta es una respuesta mal informada y probablemente pobre a mi propia pregunta. Con suerte, alguien más informado puede responder con parámetros ffmpeg/x264 más sensibles para este tipo de contenido, en lugar de simplemente variar el valor CRF a algo extremo.
He estado usando un enfoque similar al publicado durante algunos meses y funciona bien. Por lo general, no necesito un valor CRF tan bajo como 5... pero 7 suele ser un punto de partida razonable. También con versiones posteriores de ffmpeg tuve que cambiar de -speed 1 a -preset veryslow. (Parece que -speed es en realidad para libvpx, no para libx264). También uso estas opciones ahora: -pix_fmt yuv420p -x264opts fast_pskip=0:psy=0Respuesta actualizada en consecuencia.