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

最近考虑使用RTP替换原有的高清视频传输协议,遂上网查找有关H264视频RTP打包、解包的文档和代码。功夫不负有心人,找到不少有价值的文档和代码。参考这些资料,写了H264 RTP打包类、解包类,实现了单个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 ;
 WORD inSize;
 int outSize ;
 BYTE *pFrame = unpack.Parse_RTP_Packet ( pRtpData, inSize, &outSize ) ;
 if ( pFrame != NULL )
 {
  // frame process
  // ...
 }

//////////////////////////////////////////////////////////////////////////////////////////
// class CH264_RTP_PACK start class CH264_RTP_PACK
{
#define RTP_VERSION 2 typedef struct NAL_msg_s
{
bool eoFrame ;
unsigned char type; // 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; typedef struct
{
//LITTLE_ENDIAN
unsigned short cc:; /* CSRC count */
unsigned short x:; /* header extension flag */
unsigned short p:; /* padding flag */
unsigned short v:; /* packet type */
unsigned short pt:; /* payload type */
unsigned short m:; /* marker bit */ unsigned short seq; /* sequence number */
unsigned long ts; /* timestamp */
unsigned long ssrc; /* synchronization source */
} rtp_hdr_t; typedef struct 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 int s_bit; // bit in the FU header
unsigned int e_bit; // bit in the FU header
bool FU_flag; // fragmented NAL Unit flag
} RTP_INFO; public:
CH264_RTP_PACK(unsigned long H264SSRC, unsigned char H264PAYLOADTYPE=, unsigned short MAXRTPPACKSIZE= )
{
m_MAXRTPPACKSIZE = MAXRTPPACKSIZE ;
if ( m_MAXRTPPACKSIZE > )
{
m_MAXRTPPACKSIZE = ;
}
if ( m_MAXRTPPACKSIZE < )
{
m_MAXRTPPACKSIZE = ;
} memset ( &m_RTP_Info, , 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 = ;
} ~CH264_RTP_PACK(void)
{
} //传入Set的数据必须是一个完整的NAL,起始码为0x00000001。
//起始码之前至少预留10个字节,以避免内存COPY操作。
//打包完成后,原缓冲区内的数据被破坏。
bool Set ( unsigned char *NAL_Buf, unsigned long NAL_Size, unsigned long Time_Stamp, bool End_Of_Frame )
{
unsigned long startcode = StartCode(NAL_Buf) ; if ( startcode != 0x01000000 )
{
return false ;
} int type = NAL_Buf[] & 0x1f ;
if ( type < || type > )
{
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[] ;
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 += ; // skip the syncword if ( (m_RTP_Info.nal.size + ) > m_MAXRTPPACKSIZE )
{
m_RTP_Info.FU_flag = true ;
m_RTP_Info.s_bit = ;
m_RTP_Info.e_bit = ; m_RTP_Info.nal.start += ; // skip NAL header
}
else
{
m_RTP_Info.FU_flag = false ;
m_RTP_Info.s_bit = m_RTP_Info.e_bit = ;
} m_RTP_Info.start = m_RTP_Info.end = m_RTP_Info.nal.start ;
m_bBeginNAL = true ; return true ;
} //循环调用Get获取RTP包,直到返回值为NULL
unsigned char* Get ( unsigned short *pPacketSize )
{
if ( m_RTP_Info.end == m_RTP_Info.nal.end )
{
*pPacketSize = ;
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 - ; // sizeof(basic rtp header) == 12 bytes
if ( m_RTP_Info.FU_flag )
maxSize -= ; if ( bytesLeft > maxSize )
{
m_RTP_Info.end = m_RTP_Info.start + maxSize ; // limit RTP 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 = ;
}
} m_RTP_Info.rtp_hdr.m = m_RTP_Info.nal.eoFrame ? : ; // should be set at EofFrame
if ( m_RTP_Info.FU_flag && !m_RTP_Info.e_bit )
{
m_RTP_Info.rtp_hdr.m = ;
} m_RTP_Info.rtp_hdr.seq++ ; unsigned char *cp = m_RTP_Info.start ;
cp -= ( m_RTP_Info.FU_flag ? : ) ;
m_RTP_Info.pRTP = cp ; unsigned char *cp2 = (unsigned char *)&m_RTP_Info.rtp_hdr ;
cp[] = cp2[] ;
cp[] = cp2[] ; cp[] = ( m_RTP_Info.rtp_hdr.seq >> ) & 0xff ;
cp[] = m_RTP_Info.rtp_hdr.seq & 0xff ; cp[] = ( m_RTP_Info.rtp_hdr.ts >> ) & 0xff ;
cp[] = ( m_RTP_Info.rtp_hdr.ts >> ) & 0xff ;
cp[] = ( m_RTP_Info.rtp_hdr.ts >> ) & 0xff ;
cp[] = m_RTP_Info.rtp_hdr.ts & 0xff ; cp[] = ( m_RTP_Info.rtp_hdr.ssrc >> ) & 0xff ;
cp[] = ( m_RTP_Info.rtp_hdr.ssrc >> ) & 0xff ;
cp[] = ( m_RTP_Info.rtp_hdr.ssrc >> ) & 0xff ;
cp[] = m_RTP_Info.rtp_hdr.ssrc & 0xff ;
m_RTP_Info.hdr_len = ;
/*!
* /n The FU indicator octet has the following format:
* /n
* /n +---------------+
* /n MSB |0|1|2|3|4|5|6|7| LSB
* /n +-+-+-+-+-+-+-+-+
* /n |F|NRI| Type |
* /n +---------------+
* /n
* /n The FU header has the following format:
* /n
* /n +---------------+
* /n |0|1|2|3|4|5|6|7|
* /n +-+-+-+-+-+-+-+-+
* /n |S|E|R| Type |
* /n +---------------+
*/
if ( m_RTP_Info.FU_flag )
{
// FU indicator F|NRI|Type
cp[] = ( m_RTP_Info.nal.type & 0xe0 ) | ; //Type is 28 for FU_A
//FU header S|E|R|Type
cp[] = ( m_RTP_Info.s_bit << ) | ( m_RTP_Info.e_bit << ) | ( m_RTP_Info.nal.type & 0x1f ) ; //R = 0, must be ignored by receiver m_RTP_Info.s_bit = m_RTP_Info.e_bit= ;
m_RTP_Info.hdr_len = ;
}
m_RTP_Info.start = &cp[m_RTP_Info.hdr_len] ; // new start of payload *pPacketSize = m_RTP_Info.hdr_len + ( m_RTP_Info.end - m_RTP_Info.start ) ;
return m_RTP_Info.pRTP ;
} private:
unsigned int StartCode( unsigned char *cp )
{
unsigned int d32 ;
d32 = cp[] ;
d32 <<= ;
d32 |= cp[] ;
d32 <<= ;
d32 |= cp[] ;
d32 <<= ;
d32 |= cp[] ;
return d32 ;
} private:
RTP_INFO m_RTP_Info ;
bool m_bBeginNAL ;
unsigned short 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) typedef struct
{
//LITTLE_ENDIAN
unsigned short cc:; /* CSRC count */
unsigned short x:; /* header extension flag */
unsigned short p:; /* padding flag */
unsigned short v:; /* packet type */
unsigned short pt:; /* payload type */
unsigned short m:; /* marker bit */ unsigned short seq; /* sequence number */
unsigned long ts; /* timestamp */
unsigned long ssrc; /* synchronization source */
} rtp_hdr_t;
public: CH264_RTP_UNPACK ( HRESULT &hr, unsigned char H264PAYLOADTYPE = )
: m_bSPSFound(false)
, m_bWaitKeyFrame(true)
, m_bPrevFrameEnd(false)
, m_bAssemblingFrame(false)
, m_wSeq()
, m_ssrc()
{
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 = ;
hr = S_OK ;
} ~CH264_RTP_UNPACK(void)
{
delete [] m_pBuf ;
} //pBuf为H264 RTP视频数据包,nSize为RTP视频数据包字节长度,outSize为输出视频数据帧字节长度。
//返回值为指向视频数据帧的指针。输入数据可能被破坏。
BYTE* Parse_RTP_Packet ( BYTE *pBuf, unsigned short nSize, int *outSize )
{
if ( nSize <= )
{
return NULL ;
} BYTE *cp = (BYTE*)&m_RTP_Header ;
cp[] = pBuf[] ;
cp[] = pBuf[] ; m_RTP_Header.seq = pBuf[] ;
m_RTP_Header.seq <<= ;
m_RTP_Header.seq |= pBuf[] ; m_RTP_Header.ts = pBuf[] ;
m_RTP_Header.ts <<= ;
m_RTP_Header.ts |= pBuf[] ;
m_RTP_Header.ts <<= ;
m_RTP_Header.ts |= pBuf[] ;
m_RTP_Header.ts <<= ;
m_RTP_Header.ts |= pBuf[] ; m_RTP_Header.ssrc = pBuf[] ;
m_RTP_Header.ssrc <<= ;
m_RTP_Header.ssrc |= pBuf[] ;
m_RTP_Header.ssrc <<= ;
m_RTP_Header.ssrc |= pBuf[] ;
m_RTP_Header.ssrc <<= ;
m_RTP_Header.ssrc |= pBuf[] ; BYTE *pPayload = pBuf + ;
DWORD PayloadSize = nSize - ; // Check the RTP version number (it should be 2):
if ( m_RTP_Header.v != RTP_VERSION )
{
return NULL ;
} /*
// Skip over any CSRC identifiers in the header:
if ( m_RTP_Header.cc )
{
long cc = m_RTP_Header.cc * 4 ;
if ( Size < cc )
{
return NULL ;
} Size -= cc ;
p += cc ;
} // Check for (& ignore) any RTP header extension
if ( m_RTP_Header.x )
{
if ( Size < 4 )
{
return NULL ;
} Size -= 4 ;
p += 2 ;
long l = p[0] ;
l <<= 8 ;
l |= p[1] ;
p += 2 ;
l *= 4 ;
if ( Size < l ) ;
{
return NULL ;
}
Size -= l ;
p += l ;
} // Discard any padding bytes:
if ( m_RTP_Header.p )
{
if ( Size == 0 )
{
return NULL ;
}
long Padding = p[Size-1] ;
if ( Size < Padding )
{
return NULL ;
}
Size -= Padding ;
}*/ // Check the Payload Type.
if ( m_RTP_Header.pt != m_H264PAYLOADTYPE )
{
return NULL ;
} int PayloadType = pPayload[] & 0x1f ;
int NALType = PayloadType ;
if ( NALType == ) // FU_A
{
if ( PayloadSize < )
{
return NULL ;
} NALType = pPayload[] & 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 ) // SPS PPS
{
m_wSeq = m_RTP_Header.seq ;
m_bPrevFrameEnd = true ; pPayload -= ;
*((DWORD*)(pPayload)) = 0x01000000 ;
*outSize = PayloadSize + ;
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 + ) ) // lost packet
{
m_wSeq = m_RTP_Header.seq ;
SetLostPacket () ;
return NULL ;
}
else
{
// 码流正常 m_wSeq = m_RTP_Header.seq ;
m_bAssemblingFrame = true ; if ( PayloadType != ) // whole NAL
{
*((DWORD*)(m_pStart)) = 0x01000000 ;
m_pStart += ;
m_dwSize += ;
}
else // FU_A
{
if ( pPayload[] & 0x80 ) // FU_A start
{
*((DWORD*)(m_pStart)) = 0x01000000 ;
m_pStart += ;
m_dwSize += ; pPayload[] = ( pPayload[] & 0xE0 ) | NALType ; pPayload += ;
PayloadSize -= ;
}
else
{
pPayload += ;
PayloadSize -= ;
}
} 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 = ; if ( NALType == 0x05 ) // KEY FRAME
{
m_bWaitKeyFrame = false ;
}
return m_pBuf ;
}
else
{
return NULL ;
}
}
} void SetLostPacket()
{
m_bSPSFound = false ;
m_bWaitKeyFrame = true ;
m_bPrevFrameEnd = false ;
m_bAssemblingFrame = false ;
m_pStart = m_pBuf ;
m_dwSize = ;
} private:
rtp_hdr_t m_RTP_Header ; BYTE *m_pBuf ; bool m_bSPSFound ;
bool m_bWaitKeyFrame ;
bool m_bAssemblingFrame ;
bool m_bPrevFrameEnd ;
BYTE *m_pStart ;
BYTE *m_pEnd ;
DWORD m_dwSize ; WORD m_wSeq ; BYTE m_H264PAYLOADTYPE ;
DWORD m_ssrc ;
}; // class CH264_RTP_UNPACK end
//////////////////////////////////////////////////////////////////////////////////////////

参考:

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

http://blog.csdn.net/dengzikun/article/details/5807694

上一篇:U-BOOT 命令的介绍


下一篇:0x00411202指令引用的0x00000000内存该内存不能为read错误,怎么解决