(转)基于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为依据进行打包解包(参考文章) 两个星 ...
随机推荐
- php转换字符编码为utf-8
php转换字符编码为utf-8 function strToUtf8($str){ $encode = mb_detect_encoding($str, array("ASCII" ...
- ARCGIS SDK For DotNet 路径
ARCGIS SDK For DotNet 路径 驱动器 C 中的卷是 WIN7 卷的序列号是 06AC-BD3E C:\Program Files (x86)\ArcGIS\DeveloperKit ...
- 更新YUM源后的arning: rpmts_HdrFromFdno: Header V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY错误
yum源更新后需要导入key值,否则报错如下,无法安装相关的包. Totalsize:42M DownloadingPackages: warning:rpmts_HdrFromFdno:Header ...
- js - 锚点-scrollIntoView()
document.getElementById("view").scrollIntoView(false);
- ubuntu下某些文件目录
1.#include <stdio.h> 2.#include <stdlib.h> stdio.h和stdlib.h的路径:/usr/include
- Spring MVC中@RequestParam/@RequestBody/@RequestHeader的用法收集(转)
简介: handler method参数绑定常用的注解,我们根据他们处理的Request的不同内容部分分为四类:(主要讲解常用类型) A.处理requet uri部分(这里指uri template中 ...
- IntelliJ IDEA提示:Class JavaLaunchHelper is implemented in both的错误解决
这个错误是Mac下特有的,并且据说是一个老Bug,不影响使用. 修复方法: Help->Edit Custom Properties,没有这个properties文件的话,IDEA会提示创建,然 ...
- SQL-基础学习2--ORDER BY ,DESC,WHERE, BETWEEN,AND ,OR ,IN ,NOT
所使用的数据库资料在:数据库资料 第三课:排序检索数据 3.1 排序数据 按单列排序 如果不排序,数据一般将以它在底层表中出现的顺序显示,这有可能是数据最初添加到表中的顺序.但是,如果数据随后进行 ...
- DevExpress控件GridControl使用 z
设置选中行的背景色.而不改变前景色. EnableAppearanceFocusedCell = False, EnableAppearanceFocusedRow = False private v ...
- vue doubleclick 鼠标双击事件
Vue-dblclick事件(此外事件还有mouseover,mouseout,click,mousdown...): v-on:dblclick="函数" v-on:click/ ...