基于RTP的H264视频数据打包解包类
2016-11-16 00:00
274 查看
from:http://blog.csdn.net/dengzikun/article/details/5807694
最近考虑使用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:4; /* CSRC count */
unsigned short x:1; /* header extension flag */
unsigned short p:1; /* padding flag */
unsigned short v:2; /* packet type */
unsigned short pt:7; /* payload type */
unsigned short m:1; /* 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=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 long Time_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 the syncword
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 NAL header
}
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
unsigned char* 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 ; // 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 = 1 ;
}
}
m_RTP_Info.rtp_hdr.m = m_RTP_Info.nal.eoFrame ? 1 : 0 ; // should be set at 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 ;
/*!
* /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[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 by receiver
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] ; // 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[3] ;
d32 <<= 8 ;
d32 |= cp[2] ;
d32 <<= 8 ;
d32 |= cp[1] ;
d32 <<= 8 ;
d32 |= cp[0] ;
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:4; /* CSRC count */
unsigned short x:1; /* header extension flag */
unsigned short p:1; /* padding flag */
unsigned short v:2; /* packet type */
unsigned short pt:7; /* payload type */
unsigned short m:1; /* 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 = 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为H264 RTP视频数据包,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 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[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 ) // SPS PPS
{
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_A start
{
*((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 ;
}
}
}
void SetLostPacket()
{
m_bSPSFound = false ;
m_bWaitKeyFrame = true ;
m_bPrevFrameEnd = false ;
m_bAssemblingFrame = false ;
m_pStart = m_pBuf ;
m_dwSize = 0 ;
}
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
//////////////////////////////////////////////////////////////////////////////////////////
下一篇使用D3D渲染YUV视频数据
主题推荐视频数据h264timestampsizeof
猜你在找
分享一段H264视频和AAC音频的RTP封包代码rtsp流媒体服务器的搭建RTSP服务器实例live555源代码分析ubuntu下编译ffmpeg公开的几个
rtsp流媒体测试地址SDP协议分析RTP学习三linux下RTP编程使用
JRTPLIB 受益匪浅Windows下编译FFmpeg详解VLC架构及流程分析通过FFmpeg将多媒体文件解码后保存成Bmp图像YUV420
RGB32
【精品课程】Spark+Scala套餐值不值,看你敢不敢学!【精品课程】高安定PMP认证网络套餐5折优惠【精品课程】C语言及程序设计学习套餐【精品课程】学项目管理系列视频教程之基础篇(上)【精品课程】Java
Web高级技术
准备好了么? 跳吧 !更多职位尽在 CSDN
JOB
数据工程师(外包)
工银瑞信基金管理有限公司
|
9-18K/月
我要跳槽
Android视频工程师
国广星空视频科技(北京)有限公司
|
15-20K/月
我要跳槽
H5高级攻城狮
五彩世界(北京)文化传媒有限公司
|
10-15K/月
我要跳槽
H5前端开发工程师
猎聘网
|
10-15K/月
我要跳槽
查看评论
23楼 _beginthreadex 2014-11-07 16:31发表 [回复]
调用博主的代码成功实现了对RTP包的解析和帧重组,直接保存成文件可以用VLC播放。另,博主的解包类里面似乎没有对SEI进行处理,所以在转换某个摄像机的视频时,我得到的数据全都是SPS与PPS,后查看直接保存的二进制文件,发现因为SEI也是有序列号的,不加1的话,下一次比较定会出错。已手动修改。
22楼 对牛乱弹琴 2014-07-09
09:40发表 [回复]
你好,非常感谢你的代码,我调试可以正常解包的。
但是我现在有个问题,UDP传输,我本机发送本机接收,总是断断续续的丢包,
if ( m_RTP_Header.seq != (WORD)( m_wSeq + 1 ) )//lost packet
会进入到这个判断里面?百思不得解,希望博主提示一下哈。
谢谢
Re: dengzikun 2014-07-09
23:20发表 [回复]
回复chen495810242:本机收发丢包,可能是收发速率不匹配,可以增大 socket缓冲区试试。试试把接收数据后的处理逻辑去掉,只接收数据。
Re: 对牛乱弹琴 2014-07-10
08:53发表 [回复]
回复dengzikun:额,我必须表示感谢,增加接收缓冲区大小就可以了,⊙﹏⊙b汗。
在麻烦一下,你还有其他组包模式的解析方式吗?
FU-B,STAP-A,STAP-B等,还有你说的错误处理省略了,能否也加上,我觉得这个其实更重要,非常感谢
Re: dengzikun 2014-07-11
14:26发表 [回复]
回复chen495810242:"省略错误处理"是指博文中的类使用示例代码。这两个类是可以放心使用的。H264 RTP包的其他解析方式你可以参考live555等开源库。
Re: 对牛乱弹琴 2014-07-16
11:12发表 [回复]
回复dengzikun:如果是大端模式怎么处理啊?谢谢
Re: dengzikun 2014-07-16 11:32发表 [回复]
回复chen495810242:typedef struct {
#ifdef _BIG_ENDIAN
unsigned short v:2; /* packet type */
unsigned short p:1; /* padding flag */
unsigned short x:1; /* header extension flag */
unsigned short cc:4; /* CSRC count */
unsigned short m:1; /* marker bit */
unsigned short pt:7; /* payload type */
#else
unsigned short cc:4; /* CSRC count */
unsigned short x:1; /* header extension flag */
unsigned short p:1; /* padding flag */
unsigned short v:2; /* packet type */
unsigned short pt:7; /* payload type */
unsigned short m:1; /* marker bit */
#endif
uint16_t seq; /* sequence number */
uint32_t ts; /* timestamp */
uint32_t ssrc; /* synchronization source */
} rtp_hdr_t;
21楼 nanqingzhe 2014-06-09
17:33发表 [回复]
楼主,最近在做RTP包解析,发现用你这个解包不能成功啊。解包的时候,第一帧是I帧,SPS部分没有解析成功,然后每个包打包的多余字节没有去掉
Re: dengzikun 2014-06-09
19:43发表 [回复]
回复nanqingzhe:代码片段只能解析单个NAL单元包和FU_A分片单元包,请确认你的H264 RTP打包格式。
Re: nanqingzhe 2014-06-11 10:29发表 [回复]
回复dengzikun:打包解包均采用你给的代码。
一个完整的I帧塞进去打包,打包得到的数据交由解包的代码。
Re: nanqingzhe 2014-06-11
10:27发表 [回复]
回复dengzikun:我用的就是你这个打包代码。将一个完整的I帧塞进去打包,打包后的数据交给你的这个解包代码。
Re: dengzikun 2014-06-12 23:12发表 [回复]
回复nanqingzhe:你看一下Set 和Parse_RTP_Packet 的使用说明,应该是用法的问题。
20楼 恒月美剑 2012-11-26
15:57发表 [回复]
rtp_hdr_t结构体与标准的顺序不一样
Re: dengzikun 2012-11-26
18:46发表 [回复]
回复huai_f:代码中注明了是little endian。
Re: 恒月美剑 2012-11-28 01:13发表 [回复]
回复dengzikun:嗯,我错了,我在jrtplib库中也看到过
19楼 chinapacs 2012-06-29 14:16发表 [回复]
非常感谢谢!!!基于时间戳这一块,困扰我大半个月。。目前圆满解决。我采用的方式跟你的类似,再次谢谢您。
memcpy(dst, nal[0].p_payload, i_frame_size);
if (count > 1) {
struct timeval now;
gettimeofday(&now, NULL);
double val = (now.tv_sec - firstTime.tv_sec) +
(now.tv_usec - firstTime.tv_usec) / 1000000.0;
ts_current= ts_current + val * 90000.0 + 0.5;
firstTime = now;
}
else {
ts_current= 0;
gettimeofday(&firstTime, NULL);
}
count ++;
18楼 chinapacs 2012-06-27
18:14发表 [回复] [引用] [举报]
有没有pseudo code?? demo 一下?这一块我一直没搞清楚在fps变化的情况下。
Re: dengzikun 2012-06-29 09:12发表 [回复] [引用] [举报]
回复chinapacs:class RTP_Timestamp
{
public:
RTP_Timestamp(DWORD unit)
: m_dwUnit(unit)
{
QueryPerformanceFrequency ( (LARGE_INTEGER*)&m_Freq ) ;
}
~RTP_Timestamp(void)
{
}
DWORD GetTime ()
{
__int64 current ;
QueryPerformanceCounter ( (LARGE_INTEGER*)¤t ) ;
DWORD ts = current * m_dwUnit / m_Freq ;
return ts ;
}
private:
DWORD m_dwUnit ;
__int64 m_Freq ;
};
RTP_Timestamp TS ( 90000 ) ;
每一帧调用TS.GetTime()获取时间戳。
最近考虑使用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:4; /* CSRC count */
unsigned short x:1; /* header extension flag */
unsigned short p:1; /* padding flag */
unsigned short v:2; /* packet type */
unsigned short pt:7; /* payload type */
unsigned short m:1; /* 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=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 long Time_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 the syncword
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 NAL header
}
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
unsigned char* 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 ; // 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 = 1 ;
}
}
m_RTP_Info.rtp_hdr.m = m_RTP_Info.nal.eoFrame ? 1 : 0 ; // should be set at 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 ;
/*!
* /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[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 by receiver
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] ; // 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[3] ;
d32 <<= 8 ;
d32 |= cp[2] ;
d32 <<= 8 ;
d32 |= cp[1] ;
d32 <<= 8 ;
d32 |= cp[0] ;
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:4; /* CSRC count */
unsigned short x:1; /* header extension flag */
unsigned short p:1; /* padding flag */
unsigned short v:2; /* packet type */
unsigned short pt:7; /* payload type */
unsigned short m:1; /* 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 = 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为H264 RTP视频数据包,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 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[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 ) // SPS PPS
{
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_A start
{
*((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 ;
}
}
}
void SetLostPacket()
{
m_bSPSFound = false ;
m_bWaitKeyFrame = true ;
m_bPrevFrameEnd = false ;
m_bAssemblingFrame = false ;
m_pStart = m_pBuf ;
m_dwSize = 0 ;
}
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
//////////////////////////////////////////////////////////////////////////////////////////
下一篇使用D3D渲染YUV视频数据
主题推荐视频数据h264timestampsizeof
猜你在找
分享一段H264视频和AAC音频的RTP封包代码rtsp流媒体服务器的搭建RTSP服务器实例live555源代码分析ubuntu下编译ffmpeg公开的几个
rtsp流媒体测试地址SDP协议分析RTP学习三linux下RTP编程使用
JRTPLIB 受益匪浅Windows下编译FFmpeg详解VLC架构及流程分析通过FFmpeg将多媒体文件解码后保存成Bmp图像YUV420
RGB32
【精品课程】Spark+Scala套餐值不值,看你敢不敢学!【精品课程】高安定PMP认证网络套餐5折优惠【精品课程】C语言及程序设计学习套餐【精品课程】学项目管理系列视频教程之基础篇(上)【精品课程】Java
Web高级技术
准备好了么? 跳吧 !更多职位尽在 CSDN
JOB
数据工程师(外包)
工银瑞信基金管理有限公司
|
9-18K/月
我要跳槽
Android视频工程师
国广星空视频科技(北京)有限公司
|
15-20K/月
我要跳槽
H5高级攻城狮
五彩世界(北京)文化传媒有限公司
|
10-15K/月
我要跳槽
H5前端开发工程师
猎聘网
|
10-15K/月
我要跳槽
查看评论
23楼 _beginthreadex 2014-11-07 16:31发表 [回复]
调用博主的代码成功实现了对RTP包的解析和帧重组,直接保存成文件可以用VLC播放。另,博主的解包类里面似乎没有对SEI进行处理,所以在转换某个摄像机的视频时,我得到的数据全都是SPS与PPS,后查看直接保存的二进制文件,发现因为SEI也是有序列号的,不加1的话,下一次比较定会出错。已手动修改。
22楼 对牛乱弹琴 2014-07-09
09:40发表 [回复]
你好,非常感谢你的代码,我调试可以正常解包的。
但是我现在有个问题,UDP传输,我本机发送本机接收,总是断断续续的丢包,
if ( m_RTP_Header.seq != (WORD)( m_wSeq + 1 ) )//lost packet
会进入到这个判断里面?百思不得解,希望博主提示一下哈。
谢谢
Re: dengzikun 2014-07-09
23:20发表 [回复]
回复chen495810242:本机收发丢包,可能是收发速率不匹配,可以增大 socket缓冲区试试。试试把接收数据后的处理逻辑去掉,只接收数据。
Re: 对牛乱弹琴 2014-07-10
08:53发表 [回复]
回复dengzikun:额,我必须表示感谢,增加接收缓冲区大小就可以了,⊙﹏⊙b汗。
在麻烦一下,你还有其他组包模式的解析方式吗?
FU-B,STAP-A,STAP-B等,还有你说的错误处理省略了,能否也加上,我觉得这个其实更重要,非常感谢
Re: dengzikun 2014-07-11
14:26发表 [回复]
回复chen495810242:"省略错误处理"是指博文中的类使用示例代码。这两个类是可以放心使用的。H264 RTP包的其他解析方式你可以参考live555等开源库。
Re: 对牛乱弹琴 2014-07-16
11:12发表 [回复]
回复dengzikun:如果是大端模式怎么处理啊?谢谢
Re: dengzikun 2014-07-16 11:32发表 [回复]
回复chen495810242:typedef struct {
#ifdef _BIG_ENDIAN
unsigned short v:2; /* packet type */
unsigned short p:1; /* padding flag */
unsigned short x:1; /* header extension flag */
unsigned short cc:4; /* CSRC count */
unsigned short m:1; /* marker bit */
unsigned short pt:7; /* payload type */
#else
unsigned short cc:4; /* CSRC count */
unsigned short x:1; /* header extension flag */
unsigned short p:1; /* padding flag */
unsigned short v:2; /* packet type */
unsigned short pt:7; /* payload type */
unsigned short m:1; /* marker bit */
#endif
uint16_t seq; /* sequence number */
uint32_t ts; /* timestamp */
uint32_t ssrc; /* synchronization source */
} rtp_hdr_t;
21楼 nanqingzhe 2014-06-09
17:33发表 [回复]
楼主,最近在做RTP包解析,发现用你这个解包不能成功啊。解包的时候,第一帧是I帧,SPS部分没有解析成功,然后每个包打包的多余字节没有去掉
Re: dengzikun 2014-06-09
19:43发表 [回复]
回复nanqingzhe:代码片段只能解析单个NAL单元包和FU_A分片单元包,请确认你的H264 RTP打包格式。
Re: nanqingzhe 2014-06-11 10:29发表 [回复]
回复dengzikun:打包解包均采用你给的代码。
一个完整的I帧塞进去打包,打包得到的数据交由解包的代码。
Re: nanqingzhe 2014-06-11
10:27发表 [回复]
回复dengzikun:我用的就是你这个打包代码。将一个完整的I帧塞进去打包,打包后的数据交给你的这个解包代码。
Re: dengzikun 2014-06-12 23:12发表 [回复]
回复nanqingzhe:你看一下Set 和Parse_RTP_Packet 的使用说明,应该是用法的问题。
20楼 恒月美剑 2012-11-26
15:57发表 [回复]
rtp_hdr_t结构体与标准的顺序不一样
Re: dengzikun 2012-11-26
18:46发表 [回复]
回复huai_f:代码中注明了是little endian。
Re: 恒月美剑 2012-11-28 01:13发表 [回复]
回复dengzikun:嗯,我错了,我在jrtplib库中也看到过
19楼 chinapacs 2012-06-29 14:16发表 [回复]
非常感谢谢!!!基于时间戳这一块,困扰我大半个月。。目前圆满解决。我采用的方式跟你的类似,再次谢谢您。
memcpy(dst, nal[0].p_payload, i_frame_size);
if (count > 1) {
struct timeval now;
gettimeofday(&now, NULL);
double val = (now.tv_sec - firstTime.tv_sec) +
(now.tv_usec - firstTime.tv_usec) / 1000000.0;
ts_current= ts_current + val * 90000.0 + 0.5;
firstTime = now;
}
else {
ts_current= 0;
gettimeofday(&firstTime, NULL);
}
count ++;
18楼 chinapacs 2012-06-27
18:14发表 [回复] [引用] [举报]
有没有pseudo code?? demo 一下?这一块我一直没搞清楚在fps变化的情况下。
Re: dengzikun 2012-06-29 09:12发表 [回复] [引用] [举报]
回复chinapacs:class RTP_Timestamp
{
public:
RTP_Timestamp(DWORD unit)
: m_dwUnit(unit)
{
QueryPerformanceFrequency ( (LARGE_INTEGER*)&m_Freq ) ;
}
~RTP_Timestamp(void)
{
}
DWORD GetTime ()
{
__int64 current ;
QueryPerformanceCounter ( (LARGE_INTEGER*)¤t ) ;
DWORD ts = current * m_dwUnit / m_Freq ;
return ts ;
}
private:
DWORD m_dwUnit ;
__int64 m_Freq ;
};
RTP_Timestamp TS ( 90000 ) ;
每一帧调用TS.GetTime()获取时间戳。
相关文章推荐
- 基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- 【FFMPEG】基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- (转)基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- 基于RTP的H264视频数据打包解包类
- 基于海思媒体库视频H264打包成RTP包
- UDP分包重组算法和 基于RTP的H264视频数据分包重组
- RTP如何打包H264数据
- 按照RFC3984协议实现H264视频RTP打包(附源代码)