H264-解码顺序 显示顺序 参考顺序

H264-解码顺序 显示顺序 参考顺序

解码参考帧标记过程

当一个解码图像的nal_ref_idc 不为0时,这个解码图像会被当做参考图像,即会被用作长参考帧或者段参考帧。
IDR帧
长参考帧技术
DPB解码图像缓冲区,片被解码后生成的数据保存在DPB中。DPB中的图像数据可用于后边图像编码时的帧间预测,或者输出到显示。
在这里插入图片描述
在这里插入图片描述

解码顺序

片头的frame_num参数决定了图像的解码顺序。frame_num 以解码顺序增加,不需要表示显示顺序。
IDR: 立即刷新帧,frame_num 要清零
其他: 从之前的参考帧开始逐次加一

显示顺序

Picture Order Count决定了解码图像的播放顺序。从IDR图像的第一场POC=0开始。
POC可以从片头中以可选的三种方式推导出来。POC推导为TopfiledOrderCount 顶场顺序和BottomfieldOrderCount底场顺序。

方式:0:在片头中直接发送POC

TopFieldOrderCount = POCMsb + POCLsb
POClsb 在每个片头发送
POCMsb加一,每当POCLsb达到它的最大值时。
在这里插入图片描述
如上表中,Access unit 表示接收到的顺序,frame_num 在每遇到一个参考帧时加一。POC_lsb在每个片头发送,顶场顺序等于POC_lsb,显示顺序是顶场顺序右移一位。
frame_num 的限制

  • 如果当前帧为IDR 帧,PrevRefFrameNum = 0
  • 否则,PrevRefFrameNum的推导如下:

方式1:在序列参数集中设置需要的增量,只发送一个delta值如果这个增量需要变化时

这种方式中的变量计算参考标准中的8.2.1.2

方式2:显示顺序等于解码顺序

POC从frame_num中推导得到。参考标准中的8.2.1.3

void decode_poc(VideoParameters *p_Vid, Slice *pSlice)
{
  seq_parameter_set_rbsp_t *active_sps = p_Vid->active_sps;
  int i;
  // for POC mode 0:
  unsigned int MaxPicOrderCntLsb = (1<<(active_sps->log2_max_pic_order_cnt_lsb_minus4+4));

  switch ( active_sps->pic_order_cnt_type )
  {
  case 0: // POC MODE 0
    // 1st
    if(pSlice->idr_flag)
    {
     当为i帧时
      p_Vid->PrevPicOrderCntMsb = 0;
      p_Vid->PrevPicOrderCntLsb = 0;
    }
    else
    {
      if (p_Vid->last_has_mmco_5)
      {
        if (p_Vid->last_pic_bottom_field)
        {
          p_Vid->PrevPicOrderCntMsb = 0;
          p_Vid->PrevPicOrderCntLsb = 0;
        }
        else
        {
          p_Vid->PrevPicOrderCntMsb = 0;
          p_Vid->PrevPicOrderCntLsb = pSlice->toppoc;
        }
      }
    }
    // Calculate the MSBs of current picture
    if( pSlice->pic_order_cnt_lsb  <  p_Vid->PrevPicOrderCntLsb  &&
      ( p_Vid->PrevPicOrderCntLsb - pSlice->pic_order_cnt_lsb )  >=  ( MaxPicOrderCntLsb / 2 ) )
      pSlice->PicOrderCntMsb = p_Vid->PrevPicOrderCntMsb + MaxPicOrderCntLsb;
    else if ( pSlice->pic_order_cnt_lsb  >  p_Vid->PrevPicOrderCntLsb  &&
      ( pSlice->pic_order_cnt_lsb - p_Vid->PrevPicOrderCntLsb )  >  ( MaxPicOrderCntLsb / 2 ) )
      pSlice->PicOrderCntMsb = p_Vid->PrevPicOrderCntMsb - MaxPicOrderCntLsb;
    else
      pSlice->PicOrderCntMsb = p_Vid->PrevPicOrderCntMsb;

    // 2nd

    if(pSlice->field_pic_flag==0)
    {           //frame pix
      pSlice->toppoc = pSlice->PicOrderCntMsb + pSlice->pic_order_cnt_lsb;
      pSlice->bottompoc = pSlice->toppoc + pSlice->delta_pic_order_cnt_bottom;
      pSlice->ThisPOC = pSlice->framepoc = (pSlice->toppoc < pSlice->bottompoc)? pSlice->toppoc : pSlice->bottompoc; // POC200301
    }
    else if (pSlice->bottom_field_flag == FALSE)
    {  //top field
      pSlice->ThisPOC= pSlice->toppoc = pSlice->PicOrderCntMsb + pSlice->pic_order_cnt_lsb;
    }
    else
    {  //bottom field
      pSlice->ThisPOC= pSlice->bottompoc = pSlice->PicOrderCntMsb + pSlice->pic_order_cnt_lsb;
    }
    pSlice->framepoc = pSlice->ThisPOC;

    p_Vid->ThisPOC = pSlice->ThisPOC;

    //if ( pSlice->frame_num != p_Vid->PreviousFrameNum) //Seems redundant
      p_Vid->PreviousFrameNum = pSlice->frame_num;

    if(pSlice->nal_reference_idc)
    {
      p_Vid->PrevPicOrderCntLsb = pSlice->pic_order_cnt_lsb;
      p_Vid->PrevPicOrderCntMsb = pSlice->PicOrderCntMsb;
    }

    break;

  case 1: // POC MODE 1
    // 1st
    if(pSlice->idr_flag)
    {
      p_Vid->FrameNumOffset=0;     //  first pix of IDRGOP,
      if(pSlice->frame_num)
        error("frame_num not equal to zero in IDR picture", -1020);
    }
    else
    {
      if (p_Vid->last_has_mmco_5)
      {
        p_Vid->PreviousFrameNumOffset = 0;
        p_Vid->PreviousFrameNum = 0;
      }
      if (pSlice->frame_num<p_Vid->PreviousFrameNum)
      {             //not first pix of IDRGOP
        p_Vid->FrameNumOffset = p_Vid->PreviousFrameNumOffset + p_Vid->max_frame_num;
      }
      else
      {
        p_Vid->FrameNumOffset = p_Vid->PreviousFrameNumOffset;
      }
    }

    // 2nd
    if(active_sps->num_ref_frames_in_pic_order_cnt_cycle)
      pSlice->AbsFrameNum = p_Vid->FrameNumOffset+pSlice->frame_num;
    else
      pSlice->AbsFrameNum=0;
    if( (!pSlice->nal_reference_idc) && pSlice->AbsFrameNum > 0)
      pSlice->AbsFrameNum--;

    // 3rd
    p_Vid->ExpectedDeltaPerPicOrderCntCycle=0;

    if(active_sps->num_ref_frames_in_pic_order_cnt_cycle)
    for(i=0;i<(int) active_sps->num_ref_frames_in_pic_order_cnt_cycle;i++)
      p_Vid->ExpectedDeltaPerPicOrderCntCycle += active_sps->offset_for_ref_frame[i];

    if(pSlice->AbsFrameNum)
    {
      p_Vid->PicOrderCntCycleCnt = (pSlice->AbsFrameNum-1)/active_sps->num_ref_frames_in_pic_order_cnt_cycle;
      p_Vid->FrameNumInPicOrderCntCycle = (pSlice->AbsFrameNum-1)%active_sps->num_ref_frames_in_pic_order_cnt_cycle;
      p_Vid->ExpectedPicOrderCnt = p_Vid->PicOrderCntCycleCnt*p_Vid->ExpectedDeltaPerPicOrderCntCycle;
      for(i=0;i<=(int)p_Vid->FrameNumInPicOrderCntCycle;i++)
        p_Vid->ExpectedPicOrderCnt += active_sps->offset_for_ref_frame[i];
    }
    else
      p_Vid->ExpectedPicOrderCnt=0;

    if(!pSlice->nal_reference_idc)
      p_Vid->ExpectedPicOrderCnt += active_sps->offset_for_non_ref_pic;

    if(pSlice->field_pic_flag==0)
    {           //frame pix
      pSlice->toppoc = p_Vid->ExpectedPicOrderCnt + pSlice->delta_pic_order_cnt[0];
      pSlice->bottompoc = pSlice->toppoc + active_sps->offset_for_top_to_bottom_field + pSlice->delta_pic_order_cnt[1];
      pSlice->ThisPOC = pSlice->framepoc = (pSlice->toppoc < pSlice->bottompoc)? pSlice->toppoc : pSlice->bottompoc; // POC200301
    }
    else if (pSlice->bottom_field_flag == FALSE)
    {  //top field
      pSlice->ThisPOC = pSlice->toppoc = p_Vid->ExpectedPicOrderCnt + pSlice->delta_pic_order_cnt[0];
    }
    else
    {  //bottom field
      pSlice->ThisPOC = pSlice->bottompoc = p_Vid->ExpectedPicOrderCnt + active_sps->offset_for_top_to_bottom_field + pSlice->delta_pic_order_cnt[0];
    }
    pSlice->framepoc=pSlice->ThisPOC;

    p_Vid->PreviousFrameNum=pSlice->frame_num;
    p_Vid->PreviousFrameNumOffset=p_Vid->FrameNumOffset;

    break;


  case 2: // POC MODE 2
    if(pSlice->idr_flag) // IDR picture
    {
      p_Vid->FrameNumOffset=0;     //  first pix of IDRGOP,
      pSlice->ThisPOC = pSlice->framepoc = pSlice->toppoc = pSlice->bottompoc = 0;
      if(pSlice->frame_num)
        error("frame_num not equal to zero in IDR picture", -1020);
    }
    else
    {
      if (p_Vid->last_has_mmco_5)
      {
        p_Vid->PreviousFrameNum = 0;
        p_Vid->PreviousFrameNumOffset = 0;
      }
      if (pSlice->frame_num<p_Vid->PreviousFrameNum)
        p_Vid->FrameNumOffset = p_Vid->PreviousFrameNumOffset + p_Vid->max_frame_num;
      else
        p_Vid->FrameNumOffset = p_Vid->PreviousFrameNumOffset;


      pSlice->AbsFrameNum = p_Vid->FrameNumOffset+pSlice->frame_num;
      if(!pSlice->nal_reference_idc)
        pSlice->ThisPOC = (2*pSlice->AbsFrameNum - 1);
      else
        pSlice->ThisPOC = (2*pSlice->AbsFrameNum);

      if (pSlice->field_pic_flag==0)
        pSlice->toppoc = pSlice->bottompoc = pSlice->framepoc = pSlice->ThisPOC;
      else if (pSlice->bottom_field_flag == FALSE)
         pSlice->toppoc = pSlice->framepoc = pSlice->ThisPOC;
      else 
        pSlice->bottompoc = pSlice->framepoc = pSlice->ThisPOC;
    }

    p_Vid->PreviousFrameNum=pSlice->frame_num;
    p_Vid->PreviousFrameNumOffset=p_Vid->FrameNumOffset;
    break;


  default:
    //error must occurs
    assert( 1==0 );
    break;
  }
}

参考顺序

在编码和解码之前,参考帧排列在一个或者两个链表中。P帧使用单独的链表list0,B帧使用两条链表list0 和list1. 在每一个链表中,短参考帧后边是长参考帧,长参考帧按照LongPicNum升序排列。
list0(P slice):默认顺序是以PicNum的降序排列,PicNum是frame_num模MaxFrameNum的结果
list0(B slice):默认顺序是(1)POC(显示顺序)在当前帧之前的以POC的降序排列。(2)POC在当前帧之后的以POC的升序排列。
list1(B slice): 默认顺序是(1)POC在当前帧之后的以POC升序排列。(2)POC在当前帧之前的以POC的降序排列。

list0(P slice)

在这里插入图片描述
最初的参考帧列表是空的。当前frame num是150,解码图像缓冲区的长度是5帧,罗马数字是长参考帧号。list0的入口是最近的编码图像,是最佳的参考图像。

B帧 list

在这里插入图片描述
当前帧的POC是68,DPB中包含短参考帧60,62,70,72,78 长参考帧1,3。当前帧的poc为68.
list0的默认顺序为:62,60,70,72,78,1,3
list1的默认顺序为:70,72,78,62,60,1,3
上图中显示list0和list1从DPB中构成的过程。
按照默认顺序,list0的入口62,是当前帧前面第一个图像。list1入口是当前帧后面第一个图像。它们是最佳的B帧参考图像。
在这里插入图片描述

改变参考图像列表顺序

未完待续。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值