Mar*_*tos 54 video ffmpeg streaming video-conversion
在调节用于 DASH 播放的流时,随机访问点必须在所有流中的源流时间完全相同。执行此操作的常用方法是强制固定帧速率和固定 GOP 长度(即每 N 帧一个关键帧)。
在 FFmpeg 中,固定帧速率很容易(-r NUMBER)。
但是对于固定的关键帧位置(GOP 长度),有三种方法……哪一种是“正确的”?FFmpeg 文档对此非常含糊。
-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1
Run Code Online (Sandbox Code Playgroud)
是否应该关闭场景切换似乎存在一些争论,因为当场景切换发生时关键帧“计数器”是否重新启动尚不清楚。
-g GOP_LEN_IN_FRAMES
Run Code Online (Sandbox Code Playgroud)
不幸的是,这只是在 FFMPEG 文档中顺便记录,因此这个论点的效果非常不清楚。
-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
Run Code Online (Sandbox Code Playgroud)
这是明确记录的。但是,“时间计数器”是否在每个关键帧后都重新启动,目前还不清楚。例如,在预期的 5 秒 GOP 中,如果scenecut
libx264 在 3 秒内注入了一个关键帧,那么下一个关键帧是 5 秒后还是 2 秒后?
事实上,FFmpeg 文档区分了这个和-g
选项,但它并没有真正说明上面的这两个选项有何不同(显然,-g
将需要固定的帧速率)。
看起来-force_key_frames
会更好,因为它不需要固定的帧速率。然而,这要求
scenecut
关键帧。如果-g
不强制固定帧速率 ( -r
),这似乎也无法工作,因为无法保证ffmpeg
使用不同编解码器参数的多次运行将在每个分辨率下提供相同的瞬时帧速率。固定帧率可能会降低压缩性能(在 DASH 场景中很重要!)。
最后,这个keyint
方法看起来就像一个 hack。我希望这不是正确的答案。
参考:
slh*_*hck 41
I would recommend the following:
libx264
: -g X -keyint_min X
(and optionally add -force_key_frames "expr:gte(t,n_forced*N)"
)libx265
: -x265-params "keyint=X:min-keyint=X"
libvpx-vp9
: -g X
where X
is the interval in frames and N
is the interval in seconds. For example, for a 2-second interval with a 30fps video, X
= 60 and N
= 2.
In order to properly explain this topic, we first have to define the two types of I-frames / keyframes:
For the streaming case, you want to:
In order to configure the encoder, we have to understand what the keyframe parameters do. I did some tests and discovered the following, for the three encoders libx264
, libx265
and libvpx-vp9
in FFmpeg:
libx264
:
-g
sets the keyframe interval.-keyint_min
sets the minimum keyframe interval.-x264-params "keyint=x:min-keyint=y"
is the same as -g x -keyint_min y
.Note: When setting both to the same value, the minimum is internally set to half the maximum interval plus one, as seen in the x264
code:
h->param.i_keyint_min = x264_clip3( h->param.i_keyint_min, 1, h->param.i_keyint_max/2+1 );
Run Code Online (Sandbox Code Playgroud)libx265
:
-g
is not implemented.-x265-params "keyint=x:min-keyint=y"
works.libvpx-vp9
:
-g
sets the keyframe interval.-keyint_min
sets the minimum keyframe intervalNote: Due to how FFmpeg works, -keyint_min
is only forwarded to the encoder when it is the same as -g
. In the code from libvpxenc.c
in FFmpeg we can find:
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;
Run Code Online (Sandbox Code Playgroud)
This might be a bug (or lack of feature?), since libvpx
definitely supports setting a different value for kf_min_dist
.
-force_key_frames
?The -force_key_frames
option forcibly inserts keyframes at the given interval (expression). This works for all encoders, but it might mess with the rate control mechanism. Especially for VP9, I've noticed severe quality fluctuations, so I cannot recommend using it in this case.
小智 14
这是我的案子五十美分。
方法一:
搞乱 libx264 的参数
-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1
仅以所需的时间间隔生成 iframe。
示例 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
Run Code Online (Sandbox Code Playgroud)
按预期生成 iframe,如下所示:
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
Run Code Online (Sandbox Code Playgroud)
方法二折旧。省略。
方法三:
每 N 秒插入一个关键帧(可能):
-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
示例 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
Run Code Online (Sandbox Code Playgroud)
以稍微不同的方式生成 iframe:
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
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,它每 2 秒放置一次 iframe,并且在场景剪切(带有浮动部分的秒)上放置一次 iframe,这在我看来对于视频流的复杂性很重要。
生成的文件大小几乎相同。很奇怪,即使在方法 3 中有更多关键帧,它生成的文件有时也比标准 x264 库算法少。
为了为 HLS 流生成多个比特率文件,我们选择方法三。它完美地与块之间的 2 秒对齐,它们在每个块的开头都有 iframe,并且它们在复杂的场景中有额外的 iframe,这为拥有过时设备且无法播放 x264 高配置文件的用户提供更好的体验。
希望它可以帮助某人。
因此,答案似乎是:
libx264
特异性,并配备消除了非常有用的成本scenecut
的选项libx264
。-g
似乎已被弃用。它似乎不起作用,也没有在文档中明确定义,也没有在帮助中找到,也没有出现在代码中。代码检查表明该-g
选项可能适用于 MPEG-2 流(甚至有代码节指的是 PAL 和 NTSC!)。还:
-force_key_frames
选项的脚本这是一个简短的 PERL 程序,我用来根据 slhck 的 ffprobe 建议的输出来验证 I 帧节奏。它似乎验证了该-force_key_frames
方法也将起作用,并且具有允许scenecut
帧的额外好处。我完全不知道 FFMPEG 是如何完成这项工作的,或者我是否只是因为我的流恰好处于良好状态而幸运。
就我而言,我以 30fps 编码,预期 GOP 大小为 6 秒或 180 帧。我使用 180 作为该程序的 gopsize 参数验证了每个 180 的倍数处的 I 帧,但将其设置为 181(或任何其他不是 180 的倍数的数字)使它抱怨。
#!/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;
}
Run Code Online (Sandbox Code Playgroud)
我想在这里添加一些信息,因为我的谷歌搜索在我寻求找到有关尝试找到一种方法以我想要的方式分割我的 DASH 编码的信息时相当多地提出了这个讨论,而且我发现的信息都不是完全正确的。
首先要摆脱的几个误解:
并非所有 I 帧都相同。有大的“I”框架和小的“i”框架。或者使用正确的术语,IDR I 帧和非 IDR I 帧。IDR I 帧(有时称为“关键帧”)将创建一个新的 GOP。非 IDR 帧不会。它们可以方便地放在有场景变化的 GOP 内部。
-x264opts keyint=GOPSIZE:min-keyint=GOPSIZE
← 这并不像你认为的那样。这花了我一点时间才弄明白。事实证明min-keyint
,代码中的is 是有限的。不允许大于(keyint / 2) + 1
。因此,为这两个变量分配相同的值会导致min-keyint
编码时的值减半。
事情是这样的:场景剪辑真的很棒,尤其是在快速硬剪辑的视频中。它保持良好和清晰,所以我不想禁用它,但同时只要启用它,我就无法获得固定的 GOP 大小。我想启用场景剪切,但只能使用非 IDR I 帧。但它不起作用。直到我发现(通过大量阅读)关于误解 #2。
事实证明,我需要将keyint
所需的 GOP 大小设置为两倍。这意味着min-keyint
可以将其设置为我想要的 GOP 大小(无需内部代码将其切成两半),这会阻止场景切换检测使用 GOP 大小内的 IDR I 帧,因为自上一个 IDR I 帧以来的帧数是总是小于min-keyinit
。
最后设置force_key_frame
选项会覆盖 double size keyint
。所以这是有效的:
我更喜欢 2 秒的片段,所以我的 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>
Run Code Online (Sandbox Code Playgroud)
您可以使用 ffprobe 进行验证:
ffprobe <SRC_FLE> -select_streams v -show_frames -of csv -show_entries frame=coded_picture_number,key_frame,pict_type > frames.csv
Run Code Online (Sandbox Code Playgroud)
在生成的 CSV 文件中,每一行都会告诉你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
Run Code Online (Sandbox Code Playgroud)
结果是您应该只看到固定GOPSIZE
间隔的IDR I 帧,而所有其他 I 帧都是根据场景切换检测的需要插入的非 IDR I 帧。
归档时间: |
|
查看次数: |
97002 次 |
最近记录: |