以读文件的方式解析H264裸码流

H264 NALU类型

标识NAL单元中的RBSP数据类型,其中,nal_unit_type为1, 2, 3, 4, 5及12的NAL单元称为VCL的NAL单元,其他类型的NAL单元为非VCL的NAL单元。

0:未规定 
1:非IDR图像中不采用数据划分的片段 
2:非IDR图像中A类数据划分片段 
3:非IDR图像中B类数据划分片段 
4:非IDR图像中C类数据划分片段 
5:IDR图像的片段 
6:补充增强信息 (SEI) 
7:序列参数集 
8:图像参数集 
9:分割符 
10:序列结束符 
11:流结束符 
12:填充数据 
13 – 23:保留 
24 – 31:未规定 

思路

思路是将每次填入的这帧数据提取出来,然后通过RTP实时发送。

*cam >> frame_in;
if(frame_in.empty()){
    cout<<"frame_in blank frame"<0){
    fread (buf, 1, end_pos-start_pos, file);

    transmitter->sendFrame(buf, end_pos-start_pos);
}

sendFrame

    void imageTransmitter :: sendFrame(unsigned char* buf,int len){
    NALU_HEADER* nalu_hdr;
    FU_INDICATOR* fu_ind;
    FU_HEADER* fu_hdr;
    char* nalu_payload;  
    unsigned char rltBuf[800000];
    char sendbuf[1500]; 

    int rlt = GetAnNALU(buf,len,rltBuf,n);
    if(rlt<0){
        printf("get null error!\n");
                return;
    }

    /* 输出NALU长度和TYPE */
    dump(n);

    /*当一个NALU小于MAX_RTP_PKT_LENGTH字节的时候,采用一个单RTP包发送  */
    if(n->len<=MAX_RTP_PKT_LENGTH){
        nalu_hdr =(NALU_HEADER*)&sendbuf[0]; 
        nalu_hdr->F=n->forbidden_bit;
        nalu_hdr->NRI=n->nal_reference_idc>>5;
        nalu_hdr->TYPE=n->nal_unit_type; 
        nalu_payload=&sendbuf[1];
        memcpy(nalu_payload,n->buf+1,n->len-1);//去掉nalu头的nalu剩余内容写入sendbuf[13]开始的字符串。

        this->sendPacket(sendbuf,n->len);

    }else if(n->len>MAX_RTP_PKT_LENGTH){
        int k=0,l=0;
        k=n->len/MAX_RTP_PKT_LENGTH;//需要k个MAX_RTP_PKT_LENGTH字节的RTP包  
        l=n->len%MAX_RTP_PKT_LENGTH;//最后一个RTP包的需要装载的字节数 
        int t=0;//用于指示当前发送的是第几个分片RTP包 
        while(t<=k){
            if(!t)//发送一个需要分片的NALU的第一个分片,置FU HEADER的S位 
            {
                memset(sendbuf,0,1500); 
                fu_ind =(FU_INDICATOR*)&sendbuf[0];
                fu_ind->F=n->forbidden_bit;
                fu_ind->NRI=n->nal_reference_idc>>5;
                fu_ind->TYPE=28;

                fu_hdr =(FU_HEADER*)&sendbuf[1];
                fu_hdr->E=0;  
                fu_hdr->R=0;  
                fu_hdr->S=1;  
                fu_hdr->TYPE=n->nal_unit_type; 

                nalu_payload=&sendbuf[2];
                memcpy(nalu_payload,n->buf+1,MAX_RTP_PKT_LENGTH);//去掉NALU头
                this->sendPacket(sendbuf,MAX_RTP_PKT_LENGTH+2);
                t++; 

            }else if(k==t)//发送的是最后一个分片,注意最后一个分片的长度可能超过
            {
                memset(sendbuf,0,1500); 
                fu_ind =(FU_INDICATOR*)&sendbuf[0];
                fu_ind->F=n->forbidden_bit;
                fu_ind->NRI=n->nal_reference_idc>>5;
                fu_ind->TYPE=28;

                fu_hdr =(FU_HEADER*)&sendbuf[1];
                fu_hdr->R=0;  
                fu_hdr->S=0;  
                fu_hdr->E=1;  
                fu_hdr->TYPE=n->nal_unit_type; 

                nalu_payload=&sendbuf[2];
                memcpy(nalu_payload,n->buf+t*MAX_RTP_PKT_LENGTH+1,l-1);//将nalu最后剩余的
                this->sendPacket(sendbuf,l+1); 
                t++; 

            }else if(tF=n->forbidden_bit;
                fu_ind->NRI=n->nal_reference_idc>>5;
                fu_ind->TYPE=28;

                fu_hdr =(FU_HEADER*)&sendbuf[1];
                fu_hdr->R=0;  
                fu_hdr->S=0;  
                fu_hdr->E=0;  
                fu_hdr->TYPE=n->nal_unit_type; 

                nalu_payload=&sendbuf[2];
                memcpy(nalu_payload,n->buf+t*MAX_RTP_PKT_LENGTH+1,MAX_RTP_PKT_LENGTH);
                this->sendPacket(sendbuf,MAX_RTP_PKT_LENGTH+2);
                t++; 
            }

        }
    }

    if(rlt>0){
        sendFrame(rltBuf, len-rlt);
    }
} 

GetAnNALU

/*主要功能为得到一个完整的NALU并保存在NALU_t的buf中,获取他的长度,填充F,IDC,TYPE位。
* 并判断这个buf中是否还存在其他的NALU单元。
* parms:
* buf:为一帧原始码流
* len:原始码流长度,
* retBuf: 如果有多个NALU,则将已经获取的第一个NALU去掉,其余部分存储在retBuf中
* nalu: 获取的一个NALU
* 返回值:-1没有获取的NALU,0获取到NALU并且只有一个,大于0说明不止一个NALU,并且值代表被取出的NALU的长度包含前缀
*/

int GetAnNALU(unsigned char* buf,int len, unsigned char* retBuf, NALU_t *nalu){
    int pos = 0;
    int StartCodeFound;

    if(len<4){
        return -1;
    }

    nalu->startcodeprefix_len=3;//初始化码流序列的开始字符为3个字节

    info2 = FindStartCode2 (buf);//判断是否为0x000001
    if(info2 != 1) 
    {
        info3 = FindStartCode3 (buf);//判断是否为0x00000001
        if (info3 != 1)//如果不是,返回-1
        { 
             return -1;
        }
        else 
        {
            //如果是0x00000001,得到开始前缀为4个字节
             pos = 4;
            nalu->startcodeprefix_len = 4;
        }
    }

    else
    {
       //如果是0x000001,得到开始前缀为3个字节
        pos = 3;
       nalu->startcodeprefix_len = 3;
     }


   //查找下一个开始字符的标志位
   StartCodeFound = 0;
   info2 = 0;
   info3 = 0;

  while (!StartCodeFound)
  {
    if(pos >= len-1){
        break;
    }
    pos++;
    info3 = FindStartCode3(&buf[pos-4]);//判断是否为0x00000001
    if(info3 != 1)
      info2 = FindStartCode2(&buf[pos-3]);//判断是否为0x000001
    StartCodeFound = (info2 == 1 || info3 == 1);
  }



  if(!StartCodeFound){
      nalu->len = (len)-nalu->startcodeprefix_len;
  }else{
      if(info2 == 1){
        pos = pos - 3;
      }else{
        pos = pos - 4;
      }
      nalu->len = pos-nalu->startcodeprefix_len;
  } 



  memcpy (nalu->buf, &buf[nalu->startcodeprefix_len], nalu->len);//拷贝一个完整NALU,不拷贝起始前缀0x000001或0x00000001
  nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit
  nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit
  nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit

  if(!StartCodeFound){
    return 0;
  }else{
      memcpy(retBuf, buf+pos, len-pos);
      return pos;
  }
}

你可能感兴趣的:(人工智能)