基于RTP的H264视频数据打包解包类

最近考虑使用RTP替换原有的高清视频传输协议,遂上网查找有关H264视频RTP打包、解包的文档和代码。功夫不负有心人,找到不少有价值的文档和代码。参考这些资料,写了H264RTP打包类、解包类,实现了单个NAL单元包和FU_A分片单元包。对于丢包处理,采用简单的策略:丢弃随后的所有数据包,直到收到关键帧。测试效果还不错,代码贴上来,若能为同道中人借鉴一二,足矣。两个类的使用说明如下(省略了错误处理过程):

 DWORD H264SSRC;
 CH264_RTP_PACK pack ( H264SSRC ) ;
 BYTE *pVideoData ;
 DWORD Size, ts ;
 bool IsEndOfFrame ;
 WORD wLen ;
 pack.Set ( pVideoData, Size, ts, IsEndOfFrame );
 BYTE *pPacket ;
 while ( pPacket = pack.Get (&wLen ) )
 {
  // rtp packet process
  // ...
 }


 HRESULT hr ;
 CH264_RTP_UNPACK unpack (hr ) ;
 BYTE *pRtpData;
 WORDinSize;
 int outSize;
 BYTE *pFrame =unpack.Parse_RTP_Packet ( pRtpData, inSize,&outSize ) ;
 if ( pFrame !=NULL )
 {
  // frameprocess
  //...
 }

 // class CH264_RTP_PACK start

class CH264_RTP_PACK
{
    #defineRTP_VERSION 2

    typedefstruct NAL_msg_s
    {
      bool eoFrame ;
      unsigned chartype;      // NAL type
      unsigned char*start;    //pointer to first location in the send buffer
      unsigned char*end;    //pointer to last location in send buffer
      unsigned long size ;
    }NAL_MSG_t;

    typedefstruct
    {
      //LITTLE_ENDIAN
      unsignedshort  cc:4;      
      unsignedshort  x:1;      
      unsignedshort  p:1;      
      unsignedshort  v:2;      
      unsignedshort  pt:7;      
      unsignedshort  m:1;      

      unsignedshort   seq;      
      unsignedlong    ts;      
      unsignedlong    ssrc;      
    }rtp_hdr_t;

    typedefstruct tagRTP_INFO
    {
      NAL_MSG_t   nal;      // NAL information
      rtp_hdr_t   rtp_hdr;   // RTP header is assembled here
      int hdr_len;         // length of RTP header

      unsigned char*pRTP;    //pointer to where RTP packet has beem assembled
      unsigned char*start;    //pointer to start of payload
      unsigned char*end;      // pointer to end of payload

      unsigned ints_bit;      // bit in the FU header
      unsigned inte_bit;      // bit in the FU header
      bool FU_flag;      // fragmented NAL Unit flag
    }RTP_INFO;

public:
   CH264_RTP_PACK(unsigned long H264SSRC, unsignedchar H264PAYLOADTYPE=96, unsigned short MAXRTPPACKSIZE=1472 )
    {
      m_MAXRTPPACKSIZE = MAXRTPPACKSIZE ;
      if ( m_MAXRTPPACKSIZE > 10000)
      {
         m_MAXRTPPACKSIZE = 10000 ;
      }
      if ( m_MAXRTPPACKSIZE < 50 )
      {
         m_MAXRTPPACKSIZE = 50 ;
      }
      
      memset ( &m_RTP_Info, 0,sizeof(m_RTP_Info) ) ;

      m_RTP_Info.rtp_hdr.pt = H264PAYLOADTYPE ;
      m_RTP_Info.rtp_hdr.ssrc = H264SSRC ;
      m_RTP_Info.rtp_hdr.v = RTP_VERSION ;

      m_RTP_Info.rtp_hdr.seq = 0 ;
    }

   ~CH264_RTP_PACK(void)
    {
    }

   //传入Set的数据必须是一个完整的NAL,起始码为0x00000001。
   //起始码之前至少预留10个字节,以避免内存COPY操作。
   //打包完成后,原缓冲区内的数据被破坏。
    bool Set (unsigned char *NAL_Buf, unsigned long NAL_Size, unsigned longTime_Stamp, bool End_Of_Frame )
    {
      unsigned long startcode = StartCode(NAL_Buf);
      
      if ( startcode != 0x01000000 )
      {
         return false ;
      }

      int type = NAL_Buf[4] & 0x1f;
      if ( type < 1 || type> 12 )
      {
         return false ;
      }

      m_RTP_Info.nal.start = NAL_Buf ;
      m_RTP_Info.nal.size = NAL_Size ;
      m_RTP_Info.nal.eoFrame = End_Of_Frame ;
      m_RTP_Info.nal.type = m_RTP_Info.nal.start[4];
      m_RTP_Info.nal.end = m_RTP_Info.nal.start +m_RTP_Info.nal.size ;

      m_RTP_Info.rtp_hdr.ts = Time_Stamp ;

      m_RTP_Info.nal.start += 4;    // skip thesyncword
                           
      if ( (m_RTP_Info.nal.size + 7) >m_MAXRTPPACKSIZE )
      {
         m_RTP_Info.FU_flag = true ;
         m_RTP_Info.s_bit = 1 ;
         m_RTP_Info.e_bit = 0 ;

         m_RTP_Info.nal.start += 1;    // skip NALheader
      }
      else
      {
         m_RTP_Info.FU_flag = false ;
         m_RTP_Info.s_bit = m_RTP_Info.e_bit = 0 ;
      }
      
      m_RTP_Info.start = m_RTP_Info.end =m_RTP_Info.nal.start ;
      m_bBeginNAL = true ;

      return true ;
    }

   //循环调用Get获取RTP包,直到返回值为NULL
    unsignedchar* Get ( unsigned short *pPacketSize )
    {
      if ( m_RTP_Info.end == m_RTP_Info.nal.end )
      {
         *pPacketSize = 0 ;
         return NULL ;
      }

      if ( m_bBeginNAL )
      {
         m_bBeginNAL = false ;
      }
      else
      {
         m_RTP_Info.start =m_RTP_Info.end;   // continue with the next RTP-FU packet
      }

      int bytesLeft = m_RTP_Info.nal.end -m_RTP_Info.start ;
      int maxSize = m_MAXRTPPACKSIZE - 12;    //sizeof(basic rtp header) == 12 bytes
      if ( m_RTP_Info.FU_flag )
         maxSize -= 2 ;

      if ( bytesLeft > maxSize )
      {
         m_RTP_Info.end = m_RTP_Info.start + maxSize;    // limitRTP packetsize to 1472 bytes
      }
      else
      {
         m_RTP_Info.end = m_RTP_Info.start + bytesLeft;
      }

      if ( m_RTP_Info.FU_flag )
      {   // multiple packet NAL slice
         if ( m_RTP_Info.end == m_RTP_Info.nal.end )
         {
            m_RTP_Info.e_bit = 1 ;
         }
      }

      m_RTP_Info.rtp_hdr.m=   m_RTP_Info.nal.eoFrame ? 1 : 0 ; // should be setat EofFrame
      if ( m_RTP_Info.FU_flag&& !m_RTP_Info.e_bit )
      {
         m_RTP_Info.rtp_hdr.m = 0 ;
      }

      m_RTP_Info.rtp_hdr.seq++ ;

      unsigned char *cp = m_RTP_Info.start ;
      cp -= ( m_RTP_Info.FU_flag ? 14 : 12 ) ;
      m_RTP_Info.pRTP = cp ;
      
      unsigned char *cp2 = (unsigned char*)&m_RTP_Info.rtp_hdr ;
      cp[0] = cp2[0] ;
      cp[1] = cp2[1] ;

      cp[2] = ( m_RTP_Info.rtp_hdr.seq>> 8 ) & 0xff ;
      cp[3] = m_RTP_Info.rtp_hdr.seq &0xff ;

      cp[4] = ( m_RTP_Info.rtp_hdr.ts>> 24 ) & 0xff;
      cp[5] = ( m_RTP_Info.rtp_hdr.ts>> 16 ) & 0xff;
      cp[6] = ( m_RTP_Info.rtp_hdr.ts>>  8 )& 0xff ;
      cp[7] = m_RTP_Info.rtp_hdr.ts &0xff ;

      cp[8] =  (m_RTP_Info.rtp_hdr.ssrc >> 24 )& 0xff ;
      cp[9] =  (m_RTP_Info.rtp_hdr.ssrc >> 16 )& 0xff ;
      cp[10] = ( m_RTP_Info.rtp_hdr.ssrc>>  8 )& 0xff ;
      cp[11] = m_RTP_Info.rtp_hdr.ssrc& 0xff ;
      m_RTP_Info.hdr_len = 12 ;
      
      if ( m_RTP_Info.FU_flag )
      {
         // FU indicator F|NRI|Type
         cp[12] = ( m_RTP_Info.nal.type &0xe0 ) | 28 ;   //Type is 28 for FU_A
         //FU header      S|E|R|Type
         cp[13] = ( m_RTP_Info.s_bit<< 7 ) | ( m_RTP_Info.e_bit<< 6 ) | ( m_RTP_Info.nal.type& 0x1f ) ; //R = 0, must be ignored byreceiver

         m_RTP_Info.s_bit = m_RTP_Info.e_bit= 0 ;
         m_RTP_Info.hdr_len = 14 ;
      }
      m_RTP_Info.start =&cp[m_RTP_Info.hdr_len];    // newstart of payload

      *pPacketSize = m_RTP_Info.hdr_len + (m_RTP_Info.end - m_RTP_Info.start ) ;
      return m_RTP_Info.pRTP ;
    }

private:
    unsigned intStartCode( unsigned char *cp )
    {
      unsigned int d32 ;
      d32 = cp[3] ;
      d32 <<= 8 ;
      d32 |= cp[2] ;
      d32 <<= 8 ;
      d32 |= cp[1] ;
      d32 <<= 8 ;
      d32 |= cp[0] ;
      return d32 ;
    }

private:
    RTP_INFOm_RTP_Info ;
    boolm_bBeginNAL ;
    unsignedshort m_MAXRTPPACKSIZE ;
};

// class CH264_RTP_PACK end
//////////////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////////////////
// class CH264_RTP_UNPACK start

class CH264_RTP_UNPACK
{

#define RTP_VERSION 2
#define BUF_SIZE (1024 * 500)

    typedefstruct
    {
      //LITTLE_ENDIAN
      unsignedshort  cc:4;      
      unsignedshort  x:1;      
      unsignedshort  p:1;      
      unsignedshort  v:2;      
      unsignedshort  pt:7;      
      unsignedshort  m:1;      

      unsignedshort   seq;      
      unsignedlong    ts;      
      unsignedlong    ssrc;      
    }rtp_hdr_t;
public:

   CH264_RTP_UNPACK ( HRESULT &hr,unsigned char H264PAYLOADTYPE = 96 )
      : m_bSPSFound(false)
      , m_bWaitKeyFrame(true)
      , m_bPrevFrameEnd(false)
      , m_bAssemblingFrame(false)
      , m_wSeq(1234)
      , m_ssrc(0)
    {
      m_pBuf = new BYTE[BUF_SIZE] ;
      if ( m_pBuf == NULL )
      {
         hr = E_OUTOFMEMORY ;
         return ;
      }

      m_H264PAYLOADTYPE = H264PAYLOADTYPE ;
      m_pEnd = m_pBuf + BUF_SIZE ;
      m_pStart = m_pBuf ;
      m_dwSize = 0 ;
      hr = S_OK ;
    }

   ~CH264_RTP_UNPACK(void)
    {
      delete [] m_pBuf ;
    }

    //pBuf为H264RTP视频数据包,nSize为RTP视频数据包字节长度,outSize为输出视频数据帧字节长度。
   //返回值为指向视频数据帧的指针。输入数据可能被破坏。
    BYTE*Parse_RTP_Packet ( BYTE *pBuf, unsigned short nSize, int *outSize)
    {
      if ( nSize <= 12 )
      {
         return NULL ;
      }

      BYTE *cp = (BYTE*)&m_RTP_Header;
      cp[0] = pBuf[0] ;
      cp[1] = pBuf[1] ;

      m_RTP_Header.seq = pBuf[2] ;
      m_RTP_Header.seq<<= 8 ;
      m_RTP_Header.seq |= pBuf[3] ;

      m_RTP_Header.ts = pBuf[4] ;
      m_RTP_Header.ts <<=8 ;
      m_RTP_Header.ts |= pBuf[5] ;
      m_RTP_Header.ts <<=8 ;
      m_RTP_Header.ts |= pBuf[6] ;
      m_RTP_Header.ts <<=8 ;
      m_RTP_Header.ts |= pBuf[7] ;

      m_RTP_Header.ssrc = pBuf[8] ;
      m_RTP_Header.ssrc<<= 8 ;
      m_RTP_Header.ssrc |= pBuf[9] ;
      m_RTP_Header.ssrc<<= 8 ;
      m_RTP_Header.ssrc |= pBuf[10] ;
      m_RTP_Header.ssrc<<= 8 ;
      m_RTP_Header.ssrc |= pBuf[11] ;

      BYTE *pPayload = pBuf + 12 ;
      DWORD PayloadSize = nSize - 12 ;

      // Check the RTP version number (it should be2):
      if ( m_RTP_Header.v != RTP_VERSION )
      {
         return NULL ;
      }

      

      // Check the Payload Type.
      if ( m_RTP_Header.pt != m_H264PAYLOADTYPE )
      {
         return NULL ;
      }

      int PayloadType = pPayload[0] &0x1f ;
      int NALType = PayloadType ;
      if ( NALType == 28 ) // FU_A
      {
         if ( PayloadSize < 2 )
         {
            return NULL ;
         }

         NALType = pPayload[1] & 0x1f;
      }

      if ( m_ssrc != m_RTP_Header.ssrc )
      {
         m_ssrc = m_RTP_Header.ssrc ;
         SetLostPacket () ;
      }
    
      if ( NALType == 0x07 ) // SPS
      {
         m_bSPSFound = true ;
      }

      if ( !m_bSPSFound )
      {
         return NULL ;
      }

      if ( NALType == 0x07 || NALType == 0x08 ) // SPSPPS
      {
         m_wSeq = m_RTP_Header.seq ;
         m_bPrevFrameEnd = true ;

         pPayload -= 4 ;
         *((DWORD*)(pPayload)) = 0x01000000 ;
         *outSize = PayloadSize + 4 ;
         return pPayload ;
      }

      if ( m_bWaitKeyFrame )
      {
         if ( m_RTP_Header.m ) // frame end
         {
            m_bPrevFrameEnd = true ;
            if ( !m_bAssemblingFrame )
            {
               m_wSeq = m_RTP_Header.seq ;
               return NULL ;
            }
         }

         if ( !m_bPrevFrameEnd )
         {
            m_wSeq = m_RTP_Header.seq ;
            return NULL ;
         }
         else
         {
            if ( NALType != 0x05 ) // KEY FRAME
            {
               m_wSeq = m_RTP_Header.seq ;
               m_bPrevFrameEnd = false ;
               return NULL ;
            }
         }
      }


///////////////////////////////////////////////////////////////
         
      if ( m_RTP_Header.seq != (WORD)( m_wSeq + 1 ) )// lost packet
      {
         m_wSeq = m_RTP_Header.seq ;
         SetLostPacket ();         
         return NULL ;
      }
      else
      {
         // 码流正常

         m_wSeq = m_RTP_Header.seq ;
         m_bAssemblingFrame = true ;
         
         if ( PayloadType != 28 ) // whole NAL
         {
            *((DWORD*)(m_pStart)) = 0x01000000 ;
            m_pStart += 4 ;
            m_dwSize += 4 ;
         }
         else // FU_A
         {
            if ( pPayload[1] & 0x80 ) // FU_Astart
            {
               *((DWORD*)(m_pStart)) = 0x01000000 ;
               m_pStart += 4 ;
               m_dwSize += 4 ;

               pPayload[1] = ( pPayload[0] &0xE0 ) | NALType ;
               
               pPayload += 1 ;
               PayloadSize -= 1 ;
            }
            else
            {
               pPayload += 2 ;
               PayloadSize -= 2 ;
            }
         }

         if ( m_pStart + PayloadSize <m_pEnd )
         {
            CopyMemory ( m_pStart, pPayload, PayloadSize );
            m_dwSize += PayloadSize ;
            m_pStart += PayloadSize ;
         }
         else // memory overflow
         {
            SetLostPacket () ;
            return NULL ;
         }

         if ( m_RTP_Header.m ) // frame end
         {
            *outSize = m_dwSize ;

            m_pStart = m_pBuf ;
            m_dwSize = 0 ;

            if ( NALType == 0x05 ) // KEY FRAME
            {
               m_bWaitKeyFrame = false ;
            }
            return m_pBuf ;
         }
         else
         {
            return NULL ;
         }
      }
    }

    voidSetLostPacket()
    {
      m_bSPSFound = false ;
      m_bWaitKeyFrame = true ;
      m_bPrevFrameEnd = false ;
      m_bAssemblingFrame = false ;
      m_pStart = m_pBuf ;
      m_dwSize = 0 ;
    }

private:
    rtp_hdr_tm_RTP_Header ;

    BYTE *m_pBuf;

    boolm_bSPSFound ;
    boolm_bWaitKeyFrame ;
    boolm_bAssemblingFrame ;
    boolm_bPrevFrameEnd ;
    BYTE*m_pStart ;
    BYTE *m_pEnd;
    DWORDm_dwSize ;

    WORD m_wSeq;

    BYTEm_H264PAYLOADTYPE ;
    DWORD m_ssrc;
};

// class CH264_RTP_UNPACK end
//////////////////////////////////////////////////////////////////////////////////////////

 



你可能感兴趣的:(header,null,basic,Class,文档,byte)