什么是open-GOP,close-GOP
H.264主要有五种编码帧:
代码语言:javascript复制I(IDR帧)、i(非IDR I帧)、P(P帧)、B(参考B帧)、b(非参考B帧)。
- IDR帧,NALU_type = 0x65。IDR帧是一种特殊的I帧,在解码IDR帧前,清空所有前后向参考缓冲区,IDR帧随后的所有帧不能参考IDR前面的任何帧。IDR帧是视频安全的随机访问点,找到一个IDR帧可以丢弃前面所有帧数据而正确解码。
- I帧是帧内预测帧,它的解码不依赖与任何其他帧。NALU_type = 0x61。有些解码器没有区分这两种I帧,将所有的i帧都缺省的认为是IDR帧,这样会造成解码错误。这两种i帧在open-GOP的结构中需要明确区分,而在close-GOP中可以视为相同。
- P帧 即前向预测编码图像帧,参考之前的I帧或P帧,NALU_type = 0x41。
- B帧 即双向预测编码图像帧,参考前向或/和后向I帧或P帧。B帧也可以作为参考帧,但一般不常用。B帧提供最高的压缩比,NALU_type = 0x21 或 0x01。
GOP参数
GOP(Group of picture 图像组),指两个I帧之间的所有帧结构。x264里面决定GOP长度的参数有i_keyint_max,i_keyint_min,i_scenecut_threshold,b_open_gop.
- i_scenecut_threshold是场景切换概率值,x264会计算当前帧与前一帧的不同程度并得出一个值。如果这个值低于i_scenecut_threshold,那么就认为场景之间有切换,不能用前面的帧预测当前帧,需要插入一个帧内编码帧。较高的值会增加监测到“场景变换”的几率;i_scenecut_threshold=0,不监测场景切换。
- i_keyint_max是IDR帧最大间隔 一般取帧率的4-10倍,即4-10秒一个I帧。这个值太小,码率会过高(I帧码率通常远大于PB帧)或者视频质量较差(牺牲质量赖降低码率);这个值太大,码率会低下来,但是会牺牲实时性,在用户实时接入的时候,需要等到第一个I帧才能正确解码,过大的 i_keyint_max会增加随机接入用户的等待时间。
- i_keyint_min是当监测到一个场景切换,并且此时距离上一IDR帧的距离小于min-keyint则插入一个I帧,反之则插入一个IDR帧。
例如:
代码语言:javascript复制i_keyint_max = 10;
i_keyint_min = 3;
i_scenecut_threshold = 0;
代码语言:javascript复制F00 F01 F02 F03 F04 F05 F06 F07 F08 F09 F10 F11 F12 F13 F14 F15 F16 F17
IDR ... IDR ...
每10帧强制一个IDR帧, 如果i_scenecut_threshold = 40; 并检测到F06场景切换则
代码语言:javascript复制F00 F01 F02 F03 F04 F05 F06 F07 F08 F09 F10 F11 F12 F13 F14 F15 F16 F17
IDR IDR IDR
发现一个场景切换,并且与前一个IDR距离大于i_keyint_min,插入IDR并重新计数IDR距离
如果i_scenecut_threshold = 40; 并检测到F02场景切换则
代码语言:javascript复制F00 F01 F02 F03 F04 F05 F06 F07 F08 F09 F10 F11 F12 F13 F14 F15 F16 F17
IDR I IDR
发现一个场景切换,并且与前一个IDR距离小于i_keyint_min,插入I帧并重新计数IDR距离
如果i_scenecut_threshold = 40; 并检测到F02 F07场景切换则
代码语言:javascript复制F00 F01 F02 F03 F04 F05 F06 F07 F08 F09 F10 F11 F12 F13 F14 F15 F16 F17
IDR I IDR IDR
第一个场景切换距离小于i_keyint_min插入I帧,第二个场景切换距离前一个I帧距离大于i_keyint_min,插入IDR帧,并从新计数
open-GOP
码流里面包含B帧的时候才会出现open-GOP。一个GOP里面的某一帧在解码时要依赖于前一个GOP的某些帧,这个GOP就称为open-GOP。有些解码器不能完全支持open-GOP码流,例如蓝光解码器,因此在x264里面open-GOP是默认关闭的。对于解码端,接收到的码流如果如下:
代码语言:javascript复制I0 B0 B1 P0 B2 B3...
这就是一个open-GOP码流(I帧后面紧跟B帧)。因此B0 B1的解码需要用到I0前面一个GOP的数据,B0 B1的dts是小于I0的。如果码流如下:
代码语言:javascript复制I0 P0 B0 B1 P1 B2 B3...
这就是一个close-GOP码流,I0后面所有帧的解码不依赖于I0前面的帧,I0后面所有帧的dts都比I0的大。如果码流是
代码语言:javascript复制IDR0 B0 B1 P0 B2 B3...
那个这个GOP是close-GOP,B0,B1虽然dst比IDR0小,但编解码端都刷新了参考缓冲,B0,B1参考不到前向GOP帧。
对于编码端,如果编码帧类型决定如下:
代码语言:javascript复制...P0 B1 B2 P3 B4 B5 I6
这就会输出open-Gop码流
代码语言:javascript复制P0 P3 B1 B2 I6 B4 B5...,
B4 B5的解码依赖P3。如果编码帧类型决定如下
代码语言:javascript复制...P0 B1 B2 P3 B4 P5 I6
这样就不会输出open-GOP码流
代码语言:javascript复制P0 P3 B1 B2 P5 B4 I6...
两者区别在于I6前面的第5帧是设置为B帧还是P帧,如果一个GOP的最后一帧(上例中是第5帧)设置为B帧,这个码流就是open-GOP,设置为P帧就是close-GOP。由于B帧压缩性能好于P帧,因此open-GOP在编码性能上稍微优于close-GOP,但为了兼容性和少一些麻烦,还是把opne-GOP关闭的好。