IDR幀的作用是立刻刷新, 使錯誤不致傳播。從IDR幀開始, 重新算一個新的序列開始編碼。而I幀不具有隨機訪問的能力,這個功能是由IDR承擔。IDR幀會導致DPB (DecodedPictureBuffer 參考幀列表——這是關鍵所在)清空,而I不會。
在IDR幀之後的所有幀都不能引用任何IDR幀之前的幀的內容,而普通的I幀之後的B和P幀可以引用位於普通I幀之前的I幀。
從隨機存取的視頻流中,播放器永遠可以從一個IDR幀播放,因爲在它之後沒有任何幀引用之前的幀。但是,不能在一個沒有IDR幀的視頻中從任意點開始播放,因爲後面的幀總是會引用前面的幀。
x264的x264_encoder_encode()中有一段代碼:
if( !IS_X264_TYPE_I( h->fenc->i_type ) )//su: 如果當前幀不是關鍵幀
{
int valid_refs_left = 0;
for( int i = 0; h->frames.reference[i]; i++ )
if( !h->frames.reference[i]->b_corrupt )
valid_refs_left++;
/* No valid reference frames left: force an IDR. */
if( !valid_refs_left )
{
h->fenc->b_keyframe = 1;
h->fenc->i_type = X264_TYPE_IDR;
}
}
這段代碼的意思是,從當前幀位置向前尋找參考幀,如果當前幀不會參考前面的幀,那麼就強制設成IDR幀。
if( h->fenc->i_type == X264_TYPE_IDR )
{
/* reset ref pictures */
i_nal_type = NAL_SLICE_IDR;
i_nal_ref_idc = NAL_PRIORITY_HIGHEST;
h->sh.i_type = SLICE_TYPE_I;//su: Slice header類型
reference_reset( h );
h->frames.i_poc_last_open_gop = -1;
}
如果是IDR幀,在reference_reset函數中,會清空參考幀列表:
static inline void reference_reset( x264_t *h )
{
while( h->frames.reference[0] )
x264_frame_push_unused( h, x264_frame_pop( h->frames.reference ) );
h->fdec->i_poc =
h->fenc->i_poc = 0;
}