61 votes

Quelle est la manière correcte de fixer les images clés dans FFmpeg pour DASH ?

Lors du conditionnement d'un flux pour la lecture DASH, les points d'accès aléatoires doivent se trouver exactement au même moment du flux source dans tous les flux. La façon habituelle d'y parvenir est de forcer une fréquence d'images fixe et une longueur de GOP fixe (c'est-à-dire une image clé toutes les N images).

Dans FFmpeg, la fixation de la fréquence d'images est facile (-r NUMBER).

Mais pour des emplacements d'images clés fixes (longueur du GOP), il existe trois méthodes... laquelle est "correcte" ? La documentation de FFmpeg est désespérément vague à ce sujet.

Méthode 1 : modifier les arguments de libx264

-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1

Il semble qu'il y ait un débat pour savoir si le scenecut doit être désactivé ou non, car il n'est pas clair si le "compteur" d'images clés est relancé lorsqu'une scène est coupée.

Méthode 2 : fixer une taille GOP fixe :

-g GOP_LEN_IN_FRAMES

Ceci est malheureusement seulement documenté en passant dans la documentation FFMPEG, et donc l'effet de cet argument est très peu clair.

Méthode 3 : insérer une image clé toutes les N secondes ( Peut-être ? ):

-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)

Ce site est explicitement documenté. Mais il n'est toujours pas clair si le "compteur de temps" redémarre après chaque image clé. Par exemple, dans un GOP attendu de 5 secondes, s'il y a un scenecut image clé injectée 3 secondes par libx264, l'image clé suivante serait-elle 5 secondes plus tard ou 2 secondes plus tard ?

En fait, la documentation de FFmpeg fait la différence entre cette méthode et celle de la -g mais il ne dit pas vraiment en quoi ces deux options ci-dessus sont un tant soit peu différentes (évidemment, -g va nécessiter une fréquence d'images fixe).

Lequel est le bon ?

Il semblerait que le -force_key_frames serait supérieur car il n'est pas nécessaire d'avoir une fréquence d'images fixe. Cependant, cela exige que

  • il est conforme aux spécifications GOP de la norme H.264 ( le cas échéant )
  • il GARANTIT qu'il y aura une image clé dans la cadence fixe, indépendamment de libx264 scenecut images clés.

Il semblerait également que -g ne pourrait pas fonctionner sans forcer une fréquence d'images fixe ( -r ) car il n'y a aucune garantie que de multiples exécutions de ffmpeg avec des arguments codecs différents fourniraient la même fréquence d'images instantanée dans chaque résolution. Les fréquences d'images fixes peuvent réduire les performances de compression (IMPORTANT dans un scénario DASH !).

Enfin, le site keyint la méthode semble juste comme un hack . J'espère contre toute attente que ce n'est pas la bonne réponse.

Références :

Un exemple utilisant le -force_key_frames méthode

Un exemple utilisant le keyint méthode

Section des options vidéo avancées de FFmpeg

46voto

slhck Points 209720

TL;DR

Je vous recommande les éléments suivants :

  • libx264 : -g _X_ -keyint_min _X_ (et ajoutez éventuellement -force_key_frames "expr:gte(t,n_forced*_N_)" )
  • libx265 : -x265-params "keyint=_X_:min-keyint=_X_"
  • libvpx-vp9 : -g _X_

X est l'intervalle en trames et N est l'intervalle en secondes. Par exemple, pour un intervalle de 2 secondes avec une vidéo de 30fps, X = 60 et N = 2.

Une remarque sur les différents types de cadres

Afin d'expliquer correctement ce sujet, nous devons d'abord définir les deux types d'images I / images clés :

  • Trames de rafraîchissement instantané du décodeur (IDR) : Elles permettent le décodage indépendant des trames suivantes, sans accès aux trames antérieures à la trame IDR.
  • Trames non-IDR : Elles nécessitent une trame IDR précédente pour que le décodage fonctionne. Les trames non-IDR peuvent être utilisées pour les coupures de scènes au milieu d'un GOP (groupe d'images).

Qu'est-ce qui est recommandé pour le streaming ?

Pour le cas du streaming, vous voulez :

  • Assurez-vous que toutes les images IDR se trouvent à des positions régulières (par exemple, à 2, 4, 6, secondes) afin que la vidéo puisse être divisée en segments de longueur égale.
  • Permet la détection des coupures de scènes, afin d'améliorer l'efficacité et la qualité du codage. Cela signifie qu'il faut permettre aux images I d'être placées entre les images IDR. Vous pouvez toujours travailler avec la détection de coupure de scène désactivée (et cela fait partie de nombreux guides, encore), mais ce n'est pas nécessaire.

A quoi servent les paramètres ?

Afin de configurer l'encodeur, nous devons comprendre ce que font les paramètres des images clés. J'ai fait quelques tests et j'ai découvert ce qui suit, pour les trois encodeurs libx264 , libx265 y libvpx-vp9 dans FFmpeg :

  • libx264 :

    • -g définit l'intervalle des images clés.
    • -keyint_min définit l'intervalle minimum des images clés.
    • -x264-params "keyint=x:min-keyint=y" est la même chose que -g x -keyint_min y .
    • Nota: Lorsque les deux sont réglés sur la même valeur, le minimum est réglé en interne sur moitié l'intervalle maximal plus un, comme on peut le voir dans l'exemple suivant x264 code :

      h->param.i_keyint_min = x264_clip3( h->param.i_keyint_min, 1, h->param.i_keyint_max/2+1 );
  • libx265 :

    • -g n'est pas mis en œuvre.
    • -x265-params "keyint=x:min-keyint=y" travaux.
  • libvpx-vp9 :

    • -g définit l'intervalle des images clés.
    • -keyint_min définit l'intervalle minimum des images clés
    • Nota: En raison du fonctionnement de FFmpeg, -keyint_min n'est transmis à l'encodeur que s'il est identique à -g . Dans le code de libvpxenc.c dans FFmpeg nous pouvons trouver :

      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;

      Il s'agit peut-être d'un bogue (ou d'une absence de fonctionnalité ?), puisque libvpx supporte définitivement la définition d'une valeur différente pour kf_min_dist .

Devriez-vous utiliser -force_key_frames ?

El -force_key_frames insère de force des images clés à l'intervalle (expression) donné. Cela fonctionne pour tous les encodeurs, mais cela peut perturber le mécanisme de contrôle de la vitesse. En particulier pour VP9, j'ai remarqué de sévères fluctuations de qualité, donc je ne peux pas recommander son utilisation dans ce cas.

14voto

Ara Saahov Points 241

Voici mes cinquante centimes pour l'affaire.

Méthode 1 :

en jouant avec les arguments de libx264

-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1

Générer des iframes uniquement aux intervalles souhaités.

Exemple 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

Générer des iframes comme prévu comme ceci :

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

La méthode 2 est amortie. Omis.

Méthode 3 :

insérer une image clé toutes les N secondes (PEUT-ÊTRE) :

-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)

Exemple 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

Générer une iframe d'une manière légèrement différente :

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

Comme vous pouvez le voir, il place les iframes toutes les 2 secondes ET sur le scenecut (secondes avec partie flottante) ce qui est important pour la complexité du flux vidéo à mon avis.

La taille des fichiers générés est à peu près la même. Il est très étrange que, même avec plus d'images clés dans le fichier Méthode 3 il génère parfois moins de fichiers que l'algorithme standard de la bibliothèque x264.

Pour générer des fichiers à débit binaire multiple pour le flux HLS, nous choisissons la méthode trois. Elle est parfaitement alignée avec 2 secondes entre les chunks, ils ont des iframes au début de chaque chunk et ils ont des iframes supplémentaires sur les scènes complexes qui fournissent une meilleure expérience pour les utilisateurs qui ont des appareils obsolètes et ne peuvent pas lire les profils élevés x264.

J'espère que cela aidera quelqu'un.

9voto

Reuben Points 119

J'ai voulu ajouter quelques informations ici car j'ai trouvé cette discussion en cherchant des informations pour essayer de trouver un moyen de segmenter mon encodage DASH comme je le voulais, et aucune des informations que j'ai trouvées n'était totalement correcte.

Il faut d'abord se débarrasser de plusieurs idées fausses :

  1. Toutes les trames I ne sont pas identiques. Il y a des grandes trames "I" et des petites trames "I". Ou, pour utiliser une terminologie correcte, les images I IDR et les images I non-IDR. Les IDR I-frames (parfois appelées "keyframes") créent un nouveau GOP. Les images non-IDR ne le feront pas. Elles sont pratiques à l'intérieur d'un GOP lorsqu'il y a un changement de scène.

  2. -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE ← Cela ne fait pas ce que vous pensez que cela fait. Cela m'a pris un peu de temps pour le comprendre. Il s'avère que le min-keyint est limitée dans le code. Il n'est pas autorisé à être supérieur à (keyint / 2) + 1 . Ainsi, en attribuant la même valeur à ces deux variables, on obtient la valeur de min-keyint qui sont réduites de moitié lors de l'encodage.

Voilà le truc : le découpage de scène est vraiment génial, surtout dans les vidéos qui ont des coupes rapides et dures. Il permet de garder une image nette et précise, et je ne veux pas le désactiver, mais en même temps, je n'ai pas pu obtenir une taille de GOP fixe tant qu'il était activé. Je voulais activer le découpage de scène, mais pour qu'il n'utilise que des I-frames non-IDR. Mais cela ne fonctionnait pas. Jusqu'à ce que je comprenne (en lisant beaucoup) l'idée fausse n°2.

Il s'avère que j'avais besoin de mettre keyint pour doubler la taille souhaitée de mon GOP. Cela signifie que min-keyint peut être fixé à la taille de GOP souhaitée (sans que le code interne ne le coupe en deux), ce qui empêche la détection de coupure de scène d'utiliser des images IDR I-frame à l'intérieur de la taille de GOP, car le nombre d'images depuis la dernière IDR I-frame est toujours inférieur à min-keyinit .

Et enfin, la mise en place de la force_key_frame remplace l'option de taille double keyint . Voici donc ce qui fonctionne :

Je préfère les segments par tranches de 2 secondes, donc mon GOPSIZE = Fréquence d'images * 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>

Vous pouvez vérifier en utilisant ffprobe :

ffprobe <SRC_FLE> -select_streams v -show_frames -of csv -show_entries frame=coded_picture_number,key_frame,pict_type > frames.csv

Dans le fichier CSV généré, chaque ligne vous indiquera : 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

Le résultat est que vous ne devriez voir que des cadres IDR I-Frames à des endroits fixes. GOPSIZE tandis que toutes les autres trames I sont des trames I non IDR insérées selon les besoins par la détection des coupures de scènes.

8voto

Michael Day Points 11

La réponse semble donc être :

  • La méthode 1 est vérifiée pour fonctionner, mais elle est libx264 -spécifique, au prix de l'élimination de la très utile scenecut option dans libx264 .
  • La méthode 3 fonctionne à partir de la version FFMPEG d'avril 2015, mais vous devriez vérifier vos résultats avec avec le script inclus au bas de ce post, car la documentation FFMPEG n'est pas claire quant à l'effet de cette option. Si elle fonctionne, c'est la supérieure des deux options.
  • NE PAS UTILISER la méthode 2, -g semble être déprécié. Il ne semble pas fonctionner, il n'est pas explicitement défini dans la documentation, il n'est pas trouvé dans l'aide et il ne semble pas être utilisé dans le code. L'inspection du code montre que le -g est probablement destinée aux flux MPEG-2 (il y a même des strophes de code faisant référence au PAL et au NTSC !)

Aussi :

  • Les fichiers générés avec la méthode 3 peuvent être légèrement plus volumineux que ceux de la méthode 1, car les images I interstitielles (images clés) sont autorisées.
  • Vous devez explicitement activer le drapeau "-r" dans les deux cas, même si la méthode 3 place une trame I dans le prochain framelot sur ou sur après le temps indiqué. Si vous ne définissez pas le drapeau "-r", vous serez à la merci du fichier source, avec éventuellement une fréquence d'images variable. Des transitions DASH incompatibles peuvent en résulter.
  • Malgré les avertissements de la documentation FFMPEG, la méthode 3 est PAS moins efficaces que d'autres. En fait, les tests montrent qu'elle pourrait être légèrement PLUS efficace que la méthode 1.

script pour les -force_key_frames option

Voici un petit programme PERL que j'ai utilisé pour vérifier la cadence des I-frames en me basant sur la sortie de la suggestion ffprobe de slhck. Il semble vérifier que le -force_key_frames fonctionnera également, et présente l'avantage supplémentaire de permettre l'utilisation de la méthode scenecut des cadres. Je n'ai absolument aucune idée de la manière dont FFMPEG fait fonctionner cela, ou si j'ai juste eu de la chance parce que mes flux sont bien conditionnés.

Dans mon cas, j'ai encodé à 30fps avec une taille de GOP prévue de 6 secondes, soit 180 images. J'ai utilisé 180 comme argument de gopsize pour ce programme qui vérifie une image I à chaque multiple de 180, mais en le réglant sur 181 (ou tout autre nombre qui n'est pas un multiple de 180), il se plaint.

#!/usr/bin/perl
use strict;
my $gopsize = shift(@ARGV);
my $file = shift(@ARGV);
print "GOPSIZE = $gopsize\n";
my $linenum = 0;
my $expected = 0;
open my $pipe, "ffprobe -i $file -select_streams v -show_frames -of csv -show_entries frame=pict_type |"
        or die "Blah";
while (<$pipe>) {
  if ($linenum > $expected) {
    # Won't catch all the misses. But even one is good enough to fail.
    print "Missed IFrame at $expected\n";
    $expected = (int($linenum/$gopsize) + 1)*$gopsize;
  }
  if (m/,I\s*$/) {
    if ($linenum < $expected) {
      # Don't care term, just an extra I frame. Snore.
      #print "Free IFrame at $linenum\n";
    } else {
      #print "IFrame HIT at $expected\n";
      $expected += $gopsize;
    }
  }
  $linenum += 1;
}

0voto

TEB Points 1

Il semble que cette syntaxe ne fonctionne pas toujours J'ai testé pas mal de choses sur notre contenu VOD ainsi que sur du contenu live (file dumps) et parfois le scenecut ne fonctionne pas et déclenche une iframe intermédiaire :

Syntaxe pour une upconversion i50->p50, 2 sec gop/segment, IDR au début, iframes entre les deux si nécessaire.

ffmpeg.exe -loglevel verbose -i avc_50i.ts -pix_fmt yuv420p -filter_complex yadif=1,scale=1920:1080 -vcodec libx264 -preset fast -x264-params "rc-lookahead=100:keyint=200:min-keyint=100:hrd=1:vbv_maxrate=12000:vbv_bufsize=12000:no-open-gop=1" -r 50 -crf 22 -force_key_frames "expr:eq(mod(n,100),0)" -codec:a aac -b:a 128k -y target.ts

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X