H264关键帧和丢帧策略研究

来源:互联网 发布:畅唐网络待遇 编辑:程序博客网 时间:2024/05/29 10:59

H264关键帧和丢帧策略研究

p帧是根据前面的I帧和P帧预测而来

首先,你看开头是000001还是00000001,然后看到后面一般是41,67

67可能代表的就是关键帧,41为非关键帧

然后与0x1F做与运算,观察结果的不同,来判断是否为关键帧

int type = packet.data[4]&0x1F;

    if (type == 0) {
        NSLog(@"%d",type);
        return;

    }

type为:0,1,2,3,4。。。0,1,2

当为0的时候该帧为关键帧,关键帧不能被丢掉,要丢掉也要和后面的p帧,b帧一起丢掉


问题1:如何识别关键帧?
从x264的源代码中可以看到,如果某一帧为关键帧,这么这一帧的所携带的几个nalu中,必然有一个的type 必然为NAL_SLICE_IDR。
view plaincopy to clipboardprint?
/* Set output picture properties */  
   if( h->sh.i_type == SLICE_TYPE_I )   
       pic_out->i_type = h->i_nal_type == NAL_SLICE_IDR ? X264_TYPE_IDR : X264_TYPE_I;   
   else if( h->sh.i_type == SLICE_TYPE_P )   
       pic_out->i_type = X264_TYPE_P;   
   else  
       pic_out->i_type = X264_TYPE_B;  
/* Set output picture properties */
    if( h->sh.i_type == SLICE_TYPE_I )
        pic_out->i_type = h->i_nal_type == NAL_SLICE_IDR ? X264_TYPE_IDR : X264_TYPE_I;
    else if( h->sh.i_type == SLICE_TYPE_P )
        pic_out->i_type = X264_TYPE_P;
    else
        pic_out->i_type = X264_TYPE_B; 
我的理解就是如果nalu的type为NAL_SLICE_IDR的话,那么这帧肯定就是关键帧了。
因此只需要判断每一帧的nalu的type中是否包还有type为NAL_SLICE_IDR的,如果有则是关键帧。
判断nalu的type的可用如下的方法。
for(int i=0; i<i_nalu; i++)
{
      uint8_t *ptr = nalu[i];
      int type = ptr[4]&0x1F;
      if(type == NAL_SLICE_IDR)
      {
        key_frame = true;
        break;
       }
}
判断nalu的type中用到int type = ptr[4]&0x1F; 为什么是0x1F呢,这得说说每个nalu的结构;
nalu有三部分组成。
起始码      nalu_header       data 
起始码: 分为longcode 和  shortcode  ;
longcode :0x 00 00 00 01;
shortcode :0x 00 00 01;
nalu_header:有三个子域;
NALU 头由一个字节组成, 它的语法如下: 
      +---------------+
      |0|1|2|3|4|5|6|7|
      +-+-+-+-+-+-+-+-+
      |F|NRI|  Type   |
      +---------------+ 
F: 1 个比特.
  forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0. 
NRI: 2 个比特.
  nal_ref_idc. 取 00 ~ 11, 似乎指示这个 NALU 的重要性, 如 00 的 NALU 解码器可以丢弃它而不影响图像的回放. 不过一般情
况下不太关心 
这个属性. 
Type: 5 个比特.
  nal_unit_type. 这个 NALU 单元的类型. 简述如下: 
  0     没有定义
  1-23  NAL单元  单个 NAL 单元包.
  24    STAP-A   单一时间的组合包
  24    STAP-B   单一时间的组合包
  26    MTAP16   多个时间的组合包
  27    MTAP24   多个时间的组合包
  28    FU-A     分片的单元
  29    FU-B     分片的单元
  30-31 没有定义 
大家可以看到,要获得type的话,就得将nalu起始码后面紧跟的第一个字节的内容与0x1F做与运算后得到。
问题2:如何判断每一帧的中nalu的起始和结束。
数据在经过int x264_nal_encode( void *, int *, int b_annexeb, x264_nal_t *nal )后,会将数据组装成nalu的标准格式,即按照上面的格式:
起始码   nalu_header   data。
x264使用0x 00 00 00 01作为起始码(longcode)。int x264_nal_encode( void *, int *, int b_annexeb, x264_nal_t *nal )的作用就是使得数据流中不会再出现连续的两个以上的0。如果有的话就会插入一个0x03;
因此如果要还原一帧数据中的逻辑结构的话,可以遍历数据流,寻找0x00000001这样的特征代码。
这一点对于使用x264自带的mkv文件保存接口很是有用。因为我们接收到的网络数据可能没有没有带上详细的nalu分界信息和帧类型,需要自己重新还原


0 0