最近考虑使用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视频数据打包解包类的更多相关文章

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

    from:http://blog.csdn.net/dengzikun/article/details/5807694 最近考虑使用RTP替换原有的高清视频传输协议,遂上网查找有关H264视频RTP打 ...

  2. 【FFMPEG】基于RTP的H264视频数据打包解包类

    最近考虑使用RTP替换原有的高清视频传输协议,遂上网查找有关H264视频RTP打包.解包的文档和代码.功夫不负有心人,找到不少有价值的文档和代码.参考这些资料,写了H264 RTP打包类.解包类,实现 ...

  3. h264_rtp打包解包类及实现demo

    打包头文件: class CH2642Rtp { public: CH2642Rtp(uint32_t ssrc, uint8_t payloadType = 96, uint8_t fps = 25 ...

  4. Java之集合初探(二)Iterator(迭代器),collections,打包/解包(装箱拆箱),泛型(Generic),comparable接口

    Iterator(迭代器) 所有实现了Collection接口的容器都有一个iterator方法, 用来返回一个实现了Iterator接口的对象 Iterator对象称作迭代器, 用来方便的实现对容器 ...

  5. 07.进程管理+作业控制+文件查找与压缩+文件压缩与打包+tar打包解包+NFS

    进程管理 程序放在磁盘上叫文件,把它复制到内存,并在cpu运行,就叫进程, 进程多少也反映当前运行程序的多少 进程在系统中会为每个进程生成一个进程号,在所有的进程中有一个特殊进程即init进程, 它是 ...

  6. Mtk Android 打包解包*.img

    打包/解包 boot.img, system.img, userdata.img, or recovery.img [DESCRIPTION] MTK codebase编译出来的image必须使用MT ...

  7. 【Unity】AssetBundle的使用——打包/解包

    最近参考了各位大神的资源,初步学习了Unity的资源管理模式,包括在编辑器管理(使用AssetDatabase)和在运行时管理(使用Resources和AssetBundle).在此简单总结运行时用A ...

  8. xpack文件打包解包代码库

    Github ###概述 xpack是一个文件资源打包工具及类库,可以对多文件进行打包解包. 其使用文件名的hash作为索引,建立hash索引表以加速文件查找. ###特性 支持hashid自动解冲突 ...

  9. Ruby中星号打包解包操作

    Ruby中可以使用一个星号*和两个星号**完成一些打包.解包操作,它们称为splat操作符: 一个星号:以数组为依据进行打包解包(参考文章) 两个星号:以hash为依据进行打包解包(参考文章) 两个星 ...

随机推荐

  1. Day 18 函数之一

    函数参数: 1.形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元.因此,形参只在函数内部有效.函数调用结束返回主调用函数后则不能再使用该形参变量 2.实参可以是常量.变量. ...

  2. HDU 4405: Aeroplane chess

    类型:概率DP 题意:一条直线下飞行棋,色子六个面等概率.同时存在一些飞机航线,到了某个点可以直接飞到后面的另一个点,可以连飞,保证一个点至多一条航线.求到达或者超过终点 所需要 掷色子的期望次数. ...

  3. C#知识点总结:Monitor和Lock以及区别

    Monitor对象 1.Monitor.Enter(object)方法是获取锁,Monitor.Exit(object)方法是释放锁,这就是Monitor最常用的两个方法,当然在使用过程中为了避免获取 ...

  4. AC日记——飞行员配对方案问题 洛谷 P2756

    题目背景 第二次世界大战时期.. 题目描述 英国皇家空军从沦陷国征募了大量外籍飞行员.由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2 名飞行员,其中1 名是英国飞行员,另1名是外 ...

  5. Play框架的用户验证。

    最近刚刚参与一个基于Play框架的管理平台的升级工作,其中涉及到了用户的验证工作.第一次接触play框架,直接看已有代码,有点晕.因此,自己实现了一个简单的用户验证功能. 首先,新建一个User类,包 ...

  6. 两点C#的propertyGrid的使用心得【转】

    源文:http://www.cnblogs.com/bicker/p/3318934.html 最近接触C#的PropertyGrid比较多,得到了两个小心得记录一下. 第1点是关于控制Propert ...

  7. IDG | 四则运算表达式计算

    分析 首先将中缀表达式转换为后缀表达式(逆波兰式),然后使用栈进行计算. 没有考虑括号.小数. 代码 import java.util.LinkedList; import java.util.Lis ...

  8. PriorityQueue ,ArrayList , 数组排序

    static class E implements Comparable<E>{ int x ; int y ; int state ; int money ; public E(int ...

  9. java 图片加水印,设置透明度。说明非常具体

    package com.yidao.common; import java.awt.AlphaComposite; import java.awt.Graphics2D; import java.aw ...

  10. maven编译maven-surefire-plugin插件报错

    1.编译错误信息: [INFO] ------------------------------------------------------------------------ [ERROR] Fa ...