【FFMPEG】基于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: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
- //////////////////////////////////////////////////////////////////////////////////////////
【FFMPEG】基于RTP的H264视频数据打包解包类的更多相关文章
- (转)基于RTP的H264视频数据打包解包类
最近考虑使用RTP替换原有的高清视频传输协议,遂上网查找有关H264视频RTP打包.解包的文档和代码.功夫不负有心人,找到不少有价值的文档和代码.参考这些资料,写了H264 RTP打包类.解包类,实现 ...
- 基于RTP的H264视频数据打包解包类
from:http://blog.csdn.net/dengzikun/article/details/5807694 最近考虑使用RTP替换原有的高清视频传输协议,遂上网查找有关H264视频RTP打 ...
- h264_rtp打包解包类及实现demo
打包头文件: class CH2642Rtp { public: CH2642Rtp(uint32_t ssrc, uint8_t payloadType = 96, uint8_t fps = 25 ...
- indows下PHP通过ffmpeg给上传的视频截图详解
windows下PHP通过ffmpeg给上传的视频截图详解,php_ffmpeg.dll安装下载,找了很久php_ffmpeg.dll的下载地址和应用,发现有用的资源很少,现在问题解决了,贴出来跟大家 ...
- 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自动解冲突 ...
随机推荐
- C# Tcp和Socket 网络(五)
TcpReceive public Form1() { InitializeComponent(); new Thread(() => { IPAddress ip = IPAddress.Pa ...
- SQL Update多表联合更新的方法
SQL Update多表联合更新的方法 (1) sqlite 多表更新方法 update t1 set col1=t2.col1 from table1 t1 inner join table2 t2 ...
- kubeadm 部署kubernetes1.11.1,dashboard1.10.0
---恢复内容开始--- 实验环境准备2台虚拟机: master节点:172.17.1.36 node节点:172.17.1.40 首先安装master节点: master 的虚拟机是全新的机器,在安 ...
- Ubuntu16.04搭建NetCore2.2运行环境
选择安装的工具 .Net Core Sdk Nginx Supervisor Mysql 一 安装NetCore SDK 参考微软官方文档 https://docs.microsoft.com/zh ...
- 在windows使用gvim的感受
用新下载的gvim写几行代码习惯一下,感觉vim用起来要比atom占用的内存少多了,更加的便捷.由于之前一直在用sublime text2,虽然我也很喜欢ST,但我还是抱着膜拜的心态来试了试gvim, ...
- 转载:在Excel中将数据库字段转换成驼峰式
转载地址 在Excel中将数据库字段转换成驼峰式 1.将数据库字段复制到Excel表格第一列: 2.在第二列顶部输入=PROPER(A1)命令: 3.在第三列顶部输入=SUBSTITUTE(B1,&q ...
- Linux设备驱动程序 之 度量时间差
概述 内核通过定时器中断来跟踪事件流: 时钟中断由系统定时硬件以及周期性的间隔产生,这个间隔由内核根据HZ的值设定,HZ是一个细节结构有关的常数:作为一般性规则,即使知道对应平台上的确切HZ值,也不应 ...
- DOM操作的性能优化
DOM操作的真正问题在于 每次操作都会出发布局的改变.DOM树的修改和渲染. React解决了大面积的DOM操作的性能问题,实现了一个虚拟DOM,即virtual DOM,这个我们一条条讲. 所以关于 ...
- fastadmin编辑内容,有下拉选择关联的内容,自定义的参数去获取相应的下拉内容
1.可以到你的编辑页面中添加自定义条件 data-params='{"custom[shop_id]":"2"}'
- Arcengine获得arcgis安装的版本
ESRI.ArcGIS.RuntimeManager.ActiveRuntime.Version //gsioracle MessageBox.Show(ESRI.ArcGIS.RuntimeMan ...