http://superuser.com/questions/908280/what-is-the-correct-way-to-fix-keyframes-in-ffmpeg-for-dash
Method 1: with libx264's arguments
-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1
#在壓x264時,有二個參數可以用來設定gop的大小,如果要設定成固定長度的gop,如每25個frame為一個GOP,則要把keyint 及 min-keyint設成同樣的值。
#在壓x264時,如果只設定keyint,則x264內定演算法scene_cut會偵測場景變化,選擇最有效率的I frame及GOP大小進行壓縮,此時,每個GOP的大小會是不定長度。
#scenecut=-1代表disable,則會採固定長度GOP進行壓縮,壓縮效率會比較差。
You should simply set the
keyint and
min-keyint
to the same value and disable scene detection. If you don't disable
scene detection, the "counter" will indeed be reset, and your keyframes
end up at irregular intervals.
Note that I am not assuming variable framerate sequences here. I
honestly have no idea how to deal with those in practice—I honestly
haven't come across any before.
To give you an example, I converted a clip three times:
keyint=25 (i.e., scenecut enabled)
keyint=25:scenecut=-1
keyint=25:min-keyint=25:scenecut=-1
The frames are shown in the three columns:

As you can see, disabling scene cut detection and setting the
keyframe interval is enough. It seems that even if you don't set a
minimum, it'll insert keyframes
at least at the interval specified by
keyint. To be safe you should probably set
min-keyint too.
I've researched this quite a bit a while ago—I'm currently involved
in creating an ITU-T recommendation dealing with the analysis of
DASH-type video transmission, so we had to figure out encoding settings
that would be realistic, and from all of the documents and guides I've
read, disabling scene cut detection and forcing the keyframe interval in
x264 directly was the method of choice.
–
–
–
–
–
|
|
The answer therefore seems to be:
- Method 1 is verified to work, but is
libx264-specific, and comes at the cost of eliminating the very useful scenecut option in libx264.
- Method 3 works as of the FFMPEG version of April 2015, but you
should verify your results with with the script included at the bottom
of this post, as the FFMPEG documentation is unclear as to the effect of
the option. If it works, it is the superior of the two options.
- DO NOT USE Method 2,
-g appears to be deprecated.
It neither appears to work, nor is it explicitly defined in the
documentation, nor is found in the help, nor does it appear to be used
in the code. Code inspection shows that the -g option is likely meant for MPEG-2 streams (there are even code stanzas referring to PAL and NTSC!).
Also:
- Files generated with Method 3 may be slightly larger than Method 1, as
interstitial I frames (keyframes) are allowed.
- You should explicitly set the "-r" flag in both cases, even though Method 3 places an I frame at the next frameslot on or after
the time specified. Failure to set the "-r" flag places you at the
mercy of the source file, possibly with a variable frame rate.
Incompatible DASH transitions may result.
- Despite the warnings in the FFMPEG documentation, method 3 is NOT less efficient than others. In fact, tests show that it might be slightly MORE efficient than method 1.
Script for the -force_key_frames option
Here is a short PERL program I used to verify I-frame cadence based
on the output of slhck's ffprobe suggestion. It seems to verify that the
-force_key_frames method will also work, and has the added benefit of allowing for scenecut
frames. I have absolutely no idea how FFMPEG makes this work, or if I
just lucked out somehow because my streams happen to be
well-conditioned.
In my case, I encoded at 30fps with an expected GOP size of 6
seconds, or 180 frames. I used 180 as the gopsize argument to this
program verified an I frame at each multiple of 180, but setting it to
181 (or any other number not a multiple of 180) made it complain.
#!/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;
}
|