(转)基于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
(转)基于RTP的H264视频数据打包解包类的更多相关文章
- 基于RTP的H264视频数据打包解包类
from:http://blog.csdn.net/dengzikun/article/details/5807694 最近考虑使用RTP替换原有的高清视频传输协议,遂上网查找有关H264视频RTP打 ...
- 【FFMPEG】基于RTP的H264视频数据打包解包类
最近考虑使用RTP替换原有的高清视频传输协议,遂上网查找有关H264视频RTP打包.解包的文档和代码.功夫不负有心人,找到不少有价值的文档和代码.参考这些资料,写了H264 RTP打包类.解包类,实现 ...
- h264_rtp打包解包类及实现demo
打包头文件: class CH2642Rtp { public: CH2642Rtp(uint32_t ssrc, uint8_t payloadType = 96, uint8_t fps = 25 ...
- Java之集合初探(二)Iterator(迭代器),collections,打包/解包(装箱拆箱),泛型(Generic),comparable接口
Iterator(迭代器) 所有实现了Collection接口的容器都有一个iterator方法, 用来返回一个实现了Iterator接口的对象 Iterator对象称作迭代器, 用来方便的实现对容器 ...
- 07.进程管理+作业控制+文件查找与压缩+文件压缩与打包+tar打包解包+NFS
进程管理 程序放在磁盘上叫文件,把它复制到内存,并在cpu运行,就叫进程, 进程多少也反映当前运行程序的多少 进程在系统中会为每个进程生成一个进程号,在所有的进程中有一个特殊进程即init进程, 它是 ...
- Mtk Android 打包解包*.img
打包/解包 boot.img, system.img, userdata.img, or recovery.img [DESCRIPTION] MTK codebase编译出来的image必须使用MT ...
- 【Unity】AssetBundle的使用——打包/解包
最近参考了各位大神的资源,初步学习了Unity的资源管理模式,包括在编辑器管理(使用AssetDatabase)和在运行时管理(使用Resources和AssetBundle).在此简单总结运行时用A ...
- xpack文件打包解包代码库
Github ###概述 xpack是一个文件资源打包工具及类库,可以对多文件进行打包解包. 其使用文件名的hash作为索引,建立hash索引表以加速文件查找. ###特性 支持hashid自动解冲突 ...
- Ruby中星号打包解包操作
Ruby中可以使用一个星号*和两个星号**完成一些打包.解包操作,它们称为splat操作符: 一个星号:以数组为依据进行打包解包(参考文章) 两个星号:以hash为依据进行打包解包(参考文章) 两个星 ...
随机推荐
- 小程序-生成一个小程序码画在canvas画布上生成一张图片分享出去
这个需求我遇到过2次.一次是在识别二维码后跳转到其它页面,另一次是识别二维码后进入到生成小程序码的当前页面. 我有一个梦想,就是成为一名黑客!!!!!! 小程序中js wx.request({ ...
- HDU 5988最小网络流(浮点数)
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5988 哇,以前的模版一直T,加了优先队列优化才擦边过. 建图很好建,概率乘法化成概率加法不 ...
- Xamarin.Forms支持的地图显示类型
Xamarin.Forms支持的地图显示类型 在Xamarin.Forms中,专门提供了一个Map视图,用来显示地图.根据用户的需求不同,该视图支持三种地图显示类型,用户可以通过Map视图提供的M ...
- mysql InnoDb存储引擎索引
B+树索引:使用B+树索引查找数据时,并不能找到一个给定键值的具体行,只是找到被查找数据行所在的页,然后数据库通过把页读取到内存,再在内存中进行查找,最后得到要查找的数据. 聚集索引:按照表中主键构造 ...
- Jackson对泛型的序列化和反序列化方法汇总
说明:Jackson对于简单泛型是可以正常操作的,但是如果对于太过于复杂的泛型类有时会不成功.目前还在找着更合适的Json库.不过这一点在dotnet原生方案JavaScriptSerializer可 ...
- 【swagger】2.swagger提供开发者文档--返回统一格式篇【spring mvc】【spring boot】
接着上一篇来说, 不管正常返回结果还是后台出现异常,应该返回给前台统一的响应格式. 所以这一篇就为了应对解决这个问题. ======================================== ...
- Mac下export生效
在Terminal下用export PS1=XXX 修改完后,本次生效,但是重新启动Teminal后又恢复到默认格式.如何才能永久保存自定义的提示符格式呢? 1,~下面本来没有 .bash_pro ...
- HDOJ1071
The area 拿到题的第一想法,又是一道水题,知道P1.P2.P3三点的坐标,就能够确定抛物线的公式.确定抛物线的公式就能够进行积分,然后就没有然后了.纯粹的数学题. #include< ...
- apk 签名
给apk签名步骤:(比方apk名称是EasyMsg.apk) (1)将EasyMsg.apk包后缀改为zip, EasyMsg.zip (2)删除EasyMsg.zip文件包中的META-INF目录, ...
- python(13)- 文件处理应用Ⅱ:增删改查
用户选择1,增加功能: 用户输入www.oldboy2.org和server 11111 weight 2222 maxconn 3333后, 在www.oldboy2.org下增加一条server信 ...