Lors du conditionnement d'un flux pour la lecture DASH, les points d'accès aléatoires doivent être exactement au même temps de flux source dans tous les flux. La manière habituelle de faire cela est de forcer une cadence d'images fixe et une longueur de GOP fixe (c'est-à-dire une image clé toutes les N images).
Dans FFmpeg, la cadence fixe est facile (-r NUMBER).
Mais pour les emplacements d’images clés fixes (longueur du groupe d’opérations), il existe trois méthodes ... laquelle est "correcte"? La documentation de FFmpeg est frustrante et vague à ce sujet.
-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1
Il semble y avoir un débat quant à l'activation ou la désactivation du scénario, car il est difficile de savoir si le "compteur" des images clés est redémarré lorsqu'une scène est coupée.
-g GOP_LEN_IN_FRAMES
Ceci n'est malheureusement documenté que de manière passagère dans la documentation FFMPEG, et l'effet de cet argument est donc très flou.
-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
Cette est explicitement documentée. 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 existe une image clé scenecut
injectée 3 secondes dans libx264, l'image clé suivante sera-t-elle 5 secondes plus tard ou 2 secondes plus tard?
En fait, la documentation de FFmpeg fait la distinction entre cette option et l'option -g
, mais elle ne dit pas vraiment en quoi ces deux options ci-dessus diffèrent le moins (évidemment, -g
nécessitera une fréquence d'images fixe).
Il semblerait que le -force_key_frames
serait supérieur , car il ne nécessiterait pas de fréquence d'images fixe. Cependant, cela nécessite que
scenecut
. Il semblerait également que -g
ne puisse fonctionner sans imposer une cadence fixe (-r
) , car rien ne garantit que plusieurs exécutions de ffmpeg
avec des arguments de codec différents fourniraient la même cadence instantanée dans chaque résolution. Des fréquences d'images fixes peuvent réduire les performances de compression (IMPORTANT dans un scénario DASH!).
Enfin, la méthode keyint
apparaît simplement comme un hack . J'espère contre espoir que ce n'est pas la bonne réponse.
Références:
Un exemple utilisant la méthode -force_key_frames
La réponse semble donc être:
libx264
et entraîne le coût de l'élimination de l'option très utile scenecut
dans libx264
.-g
semble être obsolète. Il ne semble pas fonctionner, ni défini explicitement dans la documentation, ni trouvé dans l'aide, ni utilisé dans le code. L'inspection du code montre que l'option -g
est probablement destinée aux flux MPEG-2 (il existe même des strophes de code faisant référence à PAL et NTSC!).Également:
-force_key_frames
Voici un court programme Perl que j'ai utilisé pour vérifier la cadence de l'image I en fonction du résultat de la suggestion ffprobe de slhck. Il semble vérifier que la méthode -force_key_frames
fonctionnera également, et présente l’avantage supplémentaire de permettre des images scenecut
. Je n'ai absolument aucune idée de la façon dont FFMPEG rend ce travail efficace, ni de la chance que j'ai eu parce que mes flux sont bien conditionnés.
Dans mon cas, j'ai encodé à 30 images par seconde avec une taille de GOP attendue de 6 secondes, ou 180 images. J'ai utilisé 180 comme argument gopsize pour ce programme qui vérifiait une trame I à chaque multiple de 180, mais sa valeur à 181 (ou à tout autre nombre non multiple de 180) l'a fait se plaindre.
#!/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;
}
Je recommanderais ce qui suit:
libx264
: -g X -keyint_min X
(et éventuellement ajouter -force_key_frames "expr:gte(t,n_forced*N)"
)libx265
: -x265-params "keyint=X:min-keyint=X"
libvpx-vp9
: -g X
où X
est l'intervalle en images et N
est l'intervalle en secondes. Par exemple, pour un intervalle de 2 secondes avec une vidéo à 30 images par seconde, X
= 60 et N
= 2.
Pour bien expliquer ce sujet, nous devons d’abord définir les deux types d’images I/images clés:
Pour le cas de streaming, vous voulez:
Afin de configurer l'encodeur, nous devons comprendre le rôle des paramètres d'image clé. J'ai fait quelques tests et découvert ce qui suit pour les trois encodeurs libx264
, libx265
et libvpx-vp9
dans FFmpeg:
libx264
:
-g
définit l'intervalle d'images clés.-keyint_min
définit l'intervalle minimum des images clés.-x264-params "keyint=x:min-keyint=y"
est identique à -g x -keyint_min y
. Remarque: Lorsque vous définissez la même valeur, le minimum est défini en interne sur half l'intervalle maximum plus un, comme indiqué dans le code x264
:
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 implémenté.-x265-params "keyint=x:min-keyint=y"
fonctionne.libvpx-vp9
:
-g
définit l'intervalle d'images clés.-keyint_min
définit l'intervalle minimum d'images clés Remarque: 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;
Cela pourrait être un bogue (ou un manque de fonctionnalité?), Puisque libvpx
supporte définitivement la définition d'une valeur différente pour kf_min_dist
.
-force_key_frames
?L'option -force_key_frames
insère de force les images clés à l'intervalle donné (expression). Cela fonctionne pour tous les encodeurs, mais cela pourrait déranger le mécanisme de contrôle du débit. Surtout pour VP9, j'ai remarqué de graves variations de qualité, je ne peux donc pas recommander son utilisation dans ce cas.
Voici mes cinquante cents pour le cas.
Méthode 1:
jouer avec les arguments de libx264
-c: v libx264 -x264opts keyint = GOPSIZE: min-keyint = GOPSIZE: scenecut = -1
Générez 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érez les iframes comme prévu de la manière suivante:
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. Ommitted.
Méthode 3:
insérez 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érez un iframes 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 scénario (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. Très étrange que même avec plus d'images clés dans Méthode 3 , il génère parfois moins de fichiers que l'algorithme de bibliothèque x264 standard.
Pour générer plusieurs fichiers à débit binaire pour le flux HLS, nous avons choisi la troisième méthode. Il est parfaitement aligné avec 2 secondes entre les morceaux, ils ont iframe au début de chaque morceau et ils ont des iframes supplémentaires sur les scènes complexes qui offrent une meilleure expérience pour les utilisateurs qui ont des périphériques obsolètes et ne peuvent pas lire les profils élevés x264.
J'espère que ça aide quelqu'un.
Je voulais ajouter quelques informations ici, car mon travail sur Google a fait avancer la discussion dans ma quête pour trouver des informations sur la tentative de segmentation de mon codage DASH comme je le voulais, et aucune des informations que j'ai trouvées n'était totalement correcte.
D'abord, plusieurs idées fausses à éliminer:
Toutes les images I ne sont pas identiques. Il y a de grands cadres en "I" et de petits cadres en "i". Ou pour utiliser la terminologie correcte, IDR I-Frames et non-IDR I-Frames. Les images I IDR (parfois appelées "images clés") créeront un nouveau groupe d'images. Les cadres non-IDR ne le seront pas. Ils sont pratiques pour avoir dans un GOP où il y a un changement de scène.
-x264opts keyint=GOPSIZE:min-keyint=GOPSIZE
← Cela ne fait pas ce que vous pensez. Cela m'a pris un peu de temps pour comprendre. Il s'avère que le min-keyint
est limité dans le code. Il n'est pas autorisé à être supérieur à (keyint / 2) + 1
. Donc, si vous affectez la même valeur à ces deux variables, la valeur de min-keyint
sera réduite de moitié lors du codage.
Voici la chose: la scène est vraiment géniale, surtout dans les vidéos qui ont des coupes rapides et dures. Cela le maintient agréable et net, donc je ne veux pas le désactiver, mais en même temps, je ne pouvais pas obtenir une taille de GOP fixe tant qu'elle était activée. Je voulais activer la découpe de scènes, mais ne lui faire utiliser que des images I non IDR. Mais ça ne marchait pas. Jusqu’à ce que j’ai compris (à partir de beaucoup de lectures) au sujet de l’idée fausse n ° 2.
Il s'est avéré que je devais configurer keyint
pour doubler la taille de GOP souhaitée. Cela signifie que min-keyint
peut être réglé sur la taille de GOP souhaitée (sans que le code interne ne le coupe en deux), ce qui empêche la détection de découpes de scène d'utiliser des images IDR I dans la taille du GOP car le nombre d'images depuis le dernier ID I IDR est toujours inférieur à min-keyinit
.
Enfin, la définition de l'option force_key_frame
remplace la taille double keyint
. Alors, voici ce qui fonctionne:
Je préfère les segments en tranches de 2 secondes, donc mon 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>
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 les images I IDR qu’à des intervalles GOPSIZE
fixes, alors que toutes les autres images I sont des images I non IDR insérées au besoin par la détection de la scène.
Il semble que cette syntaxe ne fonctionne pas toujours. J'ai testé pas mal de contenu VOD ainsi que de contenu en direct (vidages de fichiers) et parfois, scénecut ne fonctionne pas et déclenche un entre-deux iframe:
Syntaxe pour une conversion ascendante i50 -> p50, 2 secondes gop/segment, IDR au début, iframes entre 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