
Al acondicionar una transmisión para la reproducción DASH, los puntos de acceso aleatorio deben estar exactamente en el mismo tiempo de transmisión de origen en todas las transmisiones. La forma habitual de hacer esto es forzar una velocidad de fotogramas fija y una longitud de GOP fija (es decir, un fotograma clave cada N fotogramas).
En FFmpeg, la velocidad de cuadros fija es fácil (-r NÚMERO).
Pero para ubicaciones de fotogramas clave fijas (longitud GOP), existen tres métodos... ¿cuál es "correcto"? La documentación de FFmpeg es frustrantemente vaga al respecto.
Método 1: jugar con los argumentos de libx264
-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1
Parece haber cierto debate sobre si el corte de escena debe desactivarse o no, ya que no está claro si el "contador" de fotogramas clave se reinicia cuando ocurre un corte de escena.
Método 2: establecer un tamaño de GOP fijo:
-g GOP_LEN_IN_FRAMES
Desafortunadamente, esto sólo está documentado de pasada en la documentación de FFMPEG y, por lo tanto, el efecto de este argumento es muy confuso.
Método 3: inserte un fotograma clave cada N segundos (¿Tal vez?):
-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
Esteesdocumentado explícitamente. Pero todavía no está claro de inmediato si el "contador de tiempo" se reinicia después de cada fotograma clave. Por ejemplo, en un GOP esperado de 5 segundos, si scenecut
libx264 inyecta un fotograma clave 3 segundos después, ¿el siguiente fotograma clave sería 5 segundos o 2 segundos más tarde?
De hecho, la documentación de FFmpeg diferencia entre esta y la -g
opción, pero en realidad no dice en qué se diferencian en lo más mínimo estas dos opciones anteriores (obviamente, -g
requerirá una velocidad de fotogramas fija).
¿Lo cual está bien?
Parecería que -force_key_frames
sería superior., ya que no requeriría una velocidad de fotogramas fija.Sin embargo, esto requiere que
- Cumple con las especificaciones GOP en H.264 (Si alguna)
- GARANTIZA que habrá un fotograma clave con cadencia fija, independientemente de
scenecut
los fotogramas clave libx264.
También parecería que -g
no podría funcionar sin forzar una velocidad de fotogramas fija ( -r
), ya que no hay garantía de que varias ejecuciones ffmpeg
con diferentes argumentos de códec proporcionen la misma velocidad de fotogramas instantánea en cada resolución. Las velocidades de fotogramas fijas pueden reducir el rendimiento de la compresión (¡IMPORTANTE en un escenario DASH!).
Finalmente,el keyint
método parece un truco. Espero contra toda esperanza que esta no sea la respuesta correcta.
Referencias:
Un ejemplo usando el -force_key_frames
método.
Respuesta1
TL;DR
Yo recomendaría lo siguiente:
libx264
: (y opcionalmente agregar )-g X -keyint_min X
-force_key_frames "expr:gte(t,n_forced*N)"
libx265
:-x265-params "keyint=X:min-keyint=X"
libvpx-vp9
:-g X
donde X
es el intervalo en fotogramas y N
es el intervalo en segundos. Por ejemplo, para un intervalo de 2 segundos con un vídeo de 30 fps, X
= 60 y N
= 2.
Una nota sobre los diferentes tipos de marcos
Para explicar adecuadamente este tema, primero tenemos que definir los dos tipos de I-frames/keyframes:
- Tramas Instantaneous Decoder Refresh (IDR): Permiten la decodificación independiente de las siguientes tramas, sin acceso a las tramas anteriores a la trama IDR.
- Tramas que no son IDR: requieren una trama IDR previa para que funcione la decodificación. Los fotogramas que no son IDR se pueden utilizar para cortes de escenas en medio de un GOP (grupo de imágenes).
¿Qué se recomienda para streaming?
Para el caso de la transmisión, desea:
- Asegúrese de que todos los fotogramas IDR estén en posiciones regulares (por ejemplo, a 2, 4, 6,… segundos) para que el vídeo pueda dividirse en segmentos de igual duración.
- Habilite la detección de cortes de escena para mejorar la eficiencia/calidad de la codificación. Esto significa permitir que se coloquen marcos I entre marcos IDR. Aún puedes trabajar con la detección de cortes de escena desactivada (y esto todavía forma parte de muchas guías), pero no es necesario.
¿Qué hacen los parámetros?
Para configurar el codificador, debemos comprender qué hacen los parámetros del fotograma clave. Hice algunas pruebas y descubrí lo siguiente, para los tres codificadores libx264
y libx265
en libvpx-vp9
FFmpeg:
libx264
:-g
establece el intervalo de fotogramas clave.-keyint_min
establece el intervalo mínimo de fotogramas clave.-x264-params "keyint=x:min-keyint=y"
es lo mismo que-g x -keyint_min y
.Nota:Al establecer ambos en el mismo valor, el mínimo se establece internamente enmedioel intervalo máximo más uno, como se ve en el
x264
código:h->param.i_keyint_min = x264_clip3( h->param.i_keyint_min, 1, h->param.i_keyint_max/2+1 );
libx265
:-g
no está implementado.-x265-params "keyint=x:min-keyint=y"
obras.
libvpx-vp9
:-g
establece el intervalo de fotogramas clave.-keyint_min
establece el intervalo mínimo de fotogramas claveNota:Debido a cómo funciona FFmpeg,
-keyint_min
solo se reenvía al codificador cuando es igual que-g
. En el código delibvpxenc.c
FFmpeg podemos encontrar:if (avctx->keyint_min >= 0 && avctx->keyint_min == avctx->gop_size) enccfg.kf_min_dist = avctx->keyint_min; if (avctx->gop_size >= 0) enccfg.kf_max_dist = avctx->gop_size;
Esto podría ser un error (¿o falta de característica?), ya que
libvpx
definitivamente admite establecer un valor diferente parakf_min_dist
.
¿Deberías usar -force_key_frames
?
La -force_key_frames
opción inserta a la fuerza fotogramas clave en el intervalo dado (expresión). Esto funciona para todos los codificadores, pero podría alterar el mecanismo de control de velocidad. Especialmente para VP9, he notado fluctuaciones severas en la calidad, por lo que no puedo recomendar su uso en este caso.
Respuesta2
Aquí están mis cincuenta centavos por el caso.
Método 1:
jugando con los argumentos de libx264
-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1
Genere iframes solo en los intervalos deseados.
Ejemplo 1:
ffmpeg -i test.mp4 -codec:v libx264 \
-r 23.976 \
-x264opts "keyint=48:min-keyint=48:no-scenecut" \
-c:a copy \
-y test_keyint_48.mp4
Genere iframes como se esperaba de esta manera:
Iframes Seconds
1 0
49 2
97 4
145 6
193 8
241 10
289 12
337 14
385 16
433 18
481 20
529 22
577 24
625 26
673 28
721 30
769 32
817 34
865 36
913 38
961 40
1009 42
1057 44
1105 46
1153 48
1201 50
1249 52
1297 54
1345 56
1393 58
El método 2 se deprecia. Omitido.
Método 3:
inserte un fotograma clave cada N segundos (QUIZÁS):
-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
Ejemplo 2
ffmpeg -i test.mp4 -codec:v libx264 \
-r 23.976 \
-force_key_frames "expr:gte(t,n_forced*2)"
-c:a copy \
-y test_fkf_2.mp4
Genere un iframes de una manera ligeramente diferente:
Iframes Seconds
1 0
49 2
97 4
145 6
193 8
241 10
289 12
337 14
385 16
433 18
481 20
519 21.58333333
529 22
577 24
625 26
673 28
721 30
769 32
817 34
865 36
913 38
931 38.75
941 39.16666667
961 40
1008 42
1056 44
1104 46
1152 48
1200 50
1248 52
1296 54
1305 54.375
1344 56
1367 56.95833333
1392 58
1430 59.58333333
1440 60
1475 61.45833333
1488 62
1536 64
1544 64.33333333
1584 66
1591 66.29166667
1632 68
1680 70
1728 72
1765 73.54166667
1776 74
1811 75.45833333
1824 75.95833333
1853 77.16666667
1872 77.95833333
1896 78.95833333
1920 79.95833333
1939 80.75
1968 81.95833333
Como puede ver, coloca iframes cada 2 segundos Y en el corte de escena (segundos con parte flotante), lo cual, en mi opinión, es importante para la complejidad de la transmisión de video.
Los tamaños de archivos generados son bastante similares. Es muy extraño que incluso con más fotogramas clave enMétodo 3A veces genera menos archivos que el algoritmo de biblioteca x264 estándar.
Para generar múltiples archivos de velocidad de bits para la transmisión HLS, elegimos el método tres. Está perfectamente alineado con 2 segundos entre fragmentos, tienen iframe al comienzo de cada fragmento y tienen iframes adicionales en escenas complejas, lo que brinda una mejor experiencia para los usuarios que tienen dispositivos obsoletos y no pueden reproducir perfiles altos x264.
Espero que ayude a alguien.
Respuesta3
Quería agregar algo de información aquí ya que mi búsqueda en Google sacó a relucir esta discusión en mi búsqueda para encontrar información sobre cómo tratar de encontrar una manera de segmentar mi codificación DASH de la manera que quería, y ninguna de la información que encontré era totalmente correcta.
Primero, varios conceptos erróneos de los que hay que deshacerse:
No todos los I-frames son iguales. Hay marcos con "I" grandes y marcos con "I" pequeños. O para utilizar la terminología correcta, IDR I-Frames y no IDR I-Frames. Los fotogramas IDR (a veces llamados "fotogramas clave") crearán un nuevo GOP. Las tramas que no son IDR no lo harán. Es útil tenerlos dentro de un Partido Republicano donde hay un cambio de escena.
-x264opts keyint=GOPSIZE:min-keyint=GOPSIZE
← Esto no hace lo que crees que hace. Esto me tomó un poco de tiempo darme cuenta. Resulta quemin-keyint
está limitado en el código. No se permite que sea mayor que(keyint / 2) + 1
. Entonces, asignar el mismo valor a estas dos variables da como resultado que el valormin-keyint
se reduzca a la mitad al codificar.
Aquí está la cuestión: el corte de escena es realmente genial, especialmente en videos que tienen cortes rápidos y duros. Lo mantiene agradable y nítido, por lo que no quiero desactivarlo, pero al mismo tiempo no pude obtener un tamaño de GOP fijo mientras estuviera habilitado. Quería habilitar el corte de escena, pero solo usar fotogramas I que no sean IDR. Pero no estaba funcionando. Hasta que descubrí (tras muchas lecturas) el concepto erróneo número 2.
Resulta que necesitaba configurarlo keyint
para duplicar el tamaño de GOP que deseaba. Esto significa que min-keyint
se puede configurar en el tamaño de GOP que desee (sin que el código interno lo corte por la mitad), lo que evita que la detección de corte de escena utilice fotogramas IDR dentro del tamaño de GOP porque el recuento de fotogramas desde el último fotograma IDR es siempre menos que min-keyinit
.
Y finalmente configurar la force_key_frame
opción anula el tamaño doble keyint
. Así que esto es lo que funciona:
Prefiero segmentos en fragmentos de 2 segundos, por lo que mi GOPSIZE = Framerate * 2
ffmpeg <other_options> -force_key_frames "expr:eq(mod(n,<GOPSIZE>),0)" -x264opts rc-lookahead=<GOPSIZE>:keyint=<GOPSIZE * 2>:min-keyint=<GOPSIZE> <other_options>
Puedes verificar usando ffprobe:
ffprobe <SRC_FLE> -select_streams v -show_frames -of csv -show_entries frame=coded_picture_number,key_frame,pict_type > frames.csv
En el archivo CSV generado, cada línea le indicará frame, [is_an_IDR_?], [frame_type], [frame_number]
:
frame,1,I,60 <-- frame 60, is I frame, 1 means is an IDR I-frame (aka KeyFrame)
frame,0,I,71 <-- frame 71, is I frame, 0 means not an IDR I_frame
El resultado es que solo debería ver fotogramas I IDR a GOPSIZE
intervalos fijos, mientras que todos los demás fotogramas I no son fotogramas IDR insertados según sea necesario para la detección de corte de escena.
Respuesta4
Twitch tiene una publicación sobre esto. Explican que decidieron utilizar su propio programa por varios motivos; uno de ellos fue que ffmpeg no le permite ejecutar diferentes instancias x264 en diferentes subprocesos, sino que dedica todos los subprocesos especificados a un fotograma en una salida antes de pasar a la siguiente salida.
Si no estás haciendo streaming en tiempo real, tienes más lujo. La forma "correcta" probablemente sea codificar en una resolución con solo el tamaño de GOP especificado con -g, y luego codificar las otras resoluciones forzando fotogramas clave en los mismos lugares.
Si quisiera hacer eso, podría usar ffprobe para obtener los tiempos de los fotogramas clave y luego usar un script de shell o un lenguaje de programación real para convertirlo en un comando ffmpeg.
Pero para la mayoría del contenido, hay muy poca diferencia entre tener un fotograma clave cada 5 segundos y dos fotogramas clave cada 5 segundos (uno forzado y otro de escena). Se trata del tamaño promedio de los marcos I frente al tamaño de los marcos P y B. Si usa x264 con configuraciones típicas (la única razón por la que creo que debería hacer algo para afectarlas es si configura -qmin, como una mala forma de evitar que x264 use la tasa de bits en contenido fácil; esto limita todos los tipos de fotogramas al mismo valor , creo) y obtener un resultado como el tamaño promedio del fotograma I de 46 kB, el fotograma P de 24 kB, el fotograma B de 17 kB (la mitad de frecuente que los fotogramas P), luego un fotograma I adicional cada segundo a 30 fps es sólo un aumento del 3% en el tamaño del archivo. La diferencia entre h264 y h263 podría estar formada por un conjunto de reducciones del 3%, pero una sola no es muy importante.
En otros tipos de contenido, los tamaños de fotograma serán diferentes. Para ser justos, se trata de complejidad temporal y no de complejidad espacial, por lo que no se trata solo de contenido fácil versus contenido difícil. Pero, en general, los sitios de transmisión de video tienen un límite de velocidad de bits y el contenido con fotogramas I relativamente grandes es contenido fácil que se codificará en alta calidad sin importar cuántos fotogramas clave adicionales se agreguen. Es un desperdicio, pero este desperdicio generalmente no se nota. El caso más derrochador es probablemente un vídeo que es sólo una imagen estática que acompaña a una canción, donde cada fotograma clave es exactamente igual.
Una cosa de la que no estoy seguro es cómo interactúan los fotogramas clave forzados con el limitador de velocidad establecido con -maxrate y -bufsize. Creo que incluso YouTube ha tenido problemas recientes para configurar correctamente los ajustes del búfer para brindar una calidad constante. Si solo estás usando configuraciones de velocidad de bits promedio como se puede ver en algunos sitios (ya que puedes inspeccionar las opciones de x264 en el encabezado/átomo mov? con un editor hexadecimal), entonces el modelo de búfer no es un problema, pero si estás Al ofrecer contenido generado por el usuario, la tasa de bits promedio anima a los usuarios a agregar una pantalla negra al final de su video.
La opción -g de Ffmpeg, o cualquier otra opción del codificador que utilice, se asigna a la opción específica del codificador. Entonces '-x264-params keyint=GOPSIZE' es equivalente a '-g GOPSIZE'.
Un problema con el uso de la detección de escenas es si prefiere fotogramas clave cerca de números específicos por cualquier motivo. Si especifica fotogramas clave cada 5 segundos y utiliza la detección de escena, y hay un cambio de escena en 4,5, entonces debería detectarse, pero el siguiente fotograma clave será en 9,5. Si el tiempo sigue aumentando de esta manera, podría terminar con fotogramas clave en 42,5, 47,5, 52,5, etc., en lugar de 40, 45, 50, 55. Por el contrario, si hay un cambio de escena en 5,5, entonces habrá un fotograma clave en 5 y 5,5 será demasiado pronto para otro. Ffmpeg no le permite especificar "crear un fotograma clave aquí si no hay cambios de escena dentro de los próximos 30 fotogramas". Sin embargo, alguien que entienda C podría agregar esa opción.
Para videos con velocidad de fotogramas variable, cuando no estés transmitiendo en vivo como Twitch, deberías poder usar cambios de escena sin convertirlos permanentemente a una velocidad de fotogramas constante. Si usa el filtro 'select' en ffmpeg y usa la constante 'escena' en la expresión, entonces la salida de depuración (-v debug o presione '+' varias veces mientras codifica) muestra el número de cambio de escena. Probablemente esto sea diferente y no tan útil como el número utilizado por x264, pero aún así podría ser útil.
El procedimiento, entonces, probablemente sería hacer un video de prueba que sea solo para cambios de fotogramas clave, pero tal vez podría usarse para datos de control de velocidad si se usa 2 pasadas. (No estoy seguro de si los datos generados son útiles para diferentes resoluciones y configuraciones; los datos del árbol de macrobloques no lo serán). Conviértalo a video con velocidad de fotogramas constante, pero consulteeste errorsobre la tartamudez de la salida al reducir a la mitad la velocidad de fotogramas si alguna vez decide utilizar el filtro de fps para otros fines. Ejecútelo a través de x264 con el fotograma clave que desee y la configuración GOP.
Luego simplemente use estos tiempos de fotogramas clave con el video de velocidad de fotogramas variable original.
Si permite contenido completamente loco generado por el usuario con un espacio de 20 segundos entre fotogramas, entonces, para la codificación de velocidad de fotogramas variable, podría dividir la salida, usar el filtro fps, usar de alguna manera el filtro de selección (tal vez crear una expresión realmente larga que tenga cada fotograma clave)... o tal vez podrías usar el video de prueba como entrada y decodificar solo fotogramas clave, si esa opción ffmpeg funciona, o usar el filtro de selección para seleccionar fotogramas clave. Luego escale al tamaño correcto (incluso hay un filtro scale2ref para esto) y superponga el video original en él. Luego use el filtro intercalado para combinar estos fotogramas clave destinados a ser forzados con el video original. Si esto da como resultado dos fotogramas separados por 0,001 segundos que el filtro de entrelazado no evita, entonces solucione este problema usted mismo con otro filtro de selección. Lidiar con los límites del búfer de fotogramas para el filtro de entrelazado podría ser el principal problema aquí. Todos estos podrían funcionar: usar algún tipo de filtro para amortiguar el flujo más denso (¿filtro quince?); consulte el archivo de entrada varias veces para que se decodifique más de una vez y no sea necesario almacenar los fotogramas; usar el filtro 'streamselect', que nunca he hecho, exactamente en los momentos de los fotogramas clave; Mejore el filtro de entrelazado cambiando su comportamiento predeterminado o agregando una opción para generar el fotograma más antiguo en un búfer en lugar de descartar un fotograma.