【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

  // ...

 }

[cpp] view
plain
 copy
  1. //////////////////////////////////////////////////////////////////////////////////////////
  2. // class CH264_RTP_PACK start
  3. class CH264_RTP_PACK
  4. {
  5. #define RTP_VERSION 2
  6. typedef struct NAL_msg_s
  7. {
  8. bool eoFrame ;
  9. unsigned char type;     // NAL type
  10. unsigned char *start;   // pointer to first location in the send buffer
  11. unsigned char *end; // pointer to last location in send buffer
  12. unsigned long size ;
  13. } NAL_MSG_t;
  14. typedef struct
  15. {
  16. //LITTLE_ENDIAN
  17. unsigned short   cc:4;      /* CSRC count                 */
  18. unsigned short   x:1;       /* header extension flag      */
  19. unsigned short   p:1;       /* padding flag               */
  20. unsigned short   v:2;       /* packet type                */
  21. unsigned short   pt:7;      /* payload type               */
  22. unsigned short   m:1;       /* marker bit                 */
  23. unsigned short    seq;      /* sequence number            */
  24. unsigned long     ts;       /* timestamp                  */
  25. unsigned long     ssrc;     /* synchronization source     */
  26. } rtp_hdr_t;
  27. typedef struct tagRTP_INFO
  28. {
  29. NAL_MSG_t   nal;        // NAL information
  30. rtp_hdr_t   rtp_hdr;    // RTP header is assembled here
  31. int hdr_len;            // length of RTP header
  32. unsigned char *pRTP;    // pointer to where RTP packet has beem assembled
  33. unsigned char *start;   // pointer to start of payload
  34. unsigned char *end;     // pointer to end of payload
  35. unsigned int s_bit;     // bit in the FU header
  36. unsigned int e_bit;     // bit in the FU header
  37. bool FU_flag;       // fragmented NAL Unit flag
  38. } RTP_INFO;
  39. public:
  40. CH264_RTP_PACK(unsigned long H264SSRC, unsigned char H264PAYLOADTYPE=96, unsigned short MAXRTPPACKSIZE=1472 )
  41. {
  42. m_MAXRTPPACKSIZE = MAXRTPPACKSIZE ;
  43. if ( m_MAXRTPPACKSIZE > 10000 )
  44. {
  45. m_MAXRTPPACKSIZE = 10000 ;
  46. }
  47. if ( m_MAXRTPPACKSIZE < 50 )
  48. {
  49. m_MAXRTPPACKSIZE = 50 ;
  50. }
  51. memset ( &m_RTP_Info, 0, sizeof(m_RTP_Info) ) ;
  52. m_RTP_Info.rtp_hdr.pt = H264PAYLOADTYPE ;
  53. m_RTP_Info.rtp_hdr.ssrc = H264SSRC ;
  54. m_RTP_Info.rtp_hdr.v = RTP_VERSION ;
  55. m_RTP_Info.rtp_hdr.seq = 0 ;
  56. }
  57. ~CH264_RTP_PACK(void)
  58. {
  59. }
  60. //传入Set的数据必须是一个完整的NAL,起始码为0x00000001。
  61. //起始码之前至少预留10个字节,以避免内存COPY操作。
  62. //打包完成后,原缓冲区内的数据被破坏。
  63. bool Set ( unsigned char *NAL_Buf, unsigned long NAL_Size, unsigned long Time_Stamp, bool End_Of_Frame )
  64. {
  65. unsigned long startcode = StartCode(NAL_Buf) ;
  66. if ( startcode != 0x01000000 )
  67. {
  68. return false ;
  69. }
  70. int type = NAL_Buf[4] & 0x1f ;
  71. if ( type < 1 || type > 12 )
  72. {
  73. return false ;
  74. }
  75. m_RTP_Info.nal.start = NAL_Buf ;
  76. m_RTP_Info.nal.size = NAL_Size ;
  77. m_RTP_Info.nal.eoFrame = End_Of_Frame ;
  78. m_RTP_Info.nal.type = m_RTP_Info.nal.start[4] ;
  79. m_RTP_Info.nal.end = m_RTP_Info.nal.start + m_RTP_Info.nal.size ;
  80. m_RTP_Info.rtp_hdr.ts = Time_Stamp ;
  81. m_RTP_Info.nal.start += 4 ; // skip the syncword
  82. if ( (m_RTP_Info.nal.size + 7) > m_MAXRTPPACKSIZE )
  83. {
  84. m_RTP_Info.FU_flag = true ;
  85. m_RTP_Info.s_bit = 1 ;
  86. m_RTP_Info.e_bit = 0 ;
  87. m_RTP_Info.nal.start += 1 ; // skip NAL header
  88. }
  89. else
  90. {
  91. m_RTP_Info.FU_flag = false ;
  92. m_RTP_Info.s_bit = m_RTP_Info.e_bit = 0 ;
  93. }
  94. m_RTP_Info.start = m_RTP_Info.end = m_RTP_Info.nal.start ;
  95. m_bBeginNAL = true ;
  96. return true ;
  97. }
  98. //循环调用Get获取RTP包,直到返回值为NULL
  99. unsigned char* Get ( unsigned short *pPacketSize )
  100. {
  101. if ( m_RTP_Info.end == m_RTP_Info.nal.end )
  102. {
  103. *pPacketSize = 0 ;
  104. return NULL ;
  105. }
  106. if ( m_bBeginNAL )
  107. {
  108. m_bBeginNAL = false ;
  109. }
  110. else
  111. {
  112. m_RTP_Info.start = m_RTP_Info.end;  // continue with the next RTP-FU packet
  113. }
  114. int bytesLeft = m_RTP_Info.nal.end - m_RTP_Info.start ;
  115. int maxSize = m_MAXRTPPACKSIZE - 12 ;   // sizeof(basic rtp header) == 12 bytes
  116. if ( m_RTP_Info.FU_flag )
  117. maxSize -= 2 ;
  118. if ( bytesLeft > maxSize )
  119. {
  120. m_RTP_Info.end = m_RTP_Info.start + maxSize ;   // limit RTP packetsize to 1472 bytes
  121. }
  122. else
  123. {
  124. m_RTP_Info.end = m_RTP_Info.start + bytesLeft ;
  125. }
  126. if ( m_RTP_Info.FU_flag )
  127. {   // multiple packet NAL slice
  128. if ( m_RTP_Info.end == m_RTP_Info.nal.end )
  129. {
  130. m_RTP_Info.e_bit = 1 ;
  131. }
  132. }
  133. m_RTP_Info.rtp_hdr.m =  m_RTP_Info.nal.eoFrame ? 1 : 0 ; // should be set at EofFrame
  134. if ( m_RTP_Info.FU_flag && !m_RTP_Info.e_bit )
  135. {
  136. m_RTP_Info.rtp_hdr.m = 0 ;
  137. }
  138. m_RTP_Info.rtp_hdr.seq++ ;
  139. unsigned char *cp = m_RTP_Info.start ;
  140. cp -= ( m_RTP_Info.FU_flag ? 14 : 12 ) ;
  141. m_RTP_Info.pRTP = cp ;
  142. unsigned char *cp2 = (unsigned char *)&m_RTP_Info.rtp_hdr ;
  143. cp[0] = cp2[0] ;
  144. cp[1] = cp2[1] ;
  145. cp[2] = ( m_RTP_Info.rtp_hdr.seq >> 8 ) & 0xff ;
  146. cp[3] = m_RTP_Info.rtp_hdr.seq & 0xff ;
  147. cp[4] = ( m_RTP_Info.rtp_hdr.ts >> 24 ) & 0xff ;
  148. cp[5] = ( m_RTP_Info.rtp_hdr.ts >> 16 ) & 0xff ;
  149. cp[6] = ( m_RTP_Info.rtp_hdr.ts >>  8 ) & 0xff ;
  150. cp[7] = m_RTP_Info.rtp_hdr.ts & 0xff ;
  151. cp[8] =  ( m_RTP_Info.rtp_hdr.ssrc >> 24 ) & 0xff ;
  152. cp[9] =  ( m_RTP_Info.rtp_hdr.ssrc >> 16 ) & 0xff ;
  153. cp[10] = ( m_RTP_Info.rtp_hdr.ssrc >>  8 ) & 0xff ;
  154. cp[11] = m_RTP_Info.rtp_hdr.ssrc & 0xff ;
  155. m_RTP_Info.hdr_len = 12 ;
  156. /*!
  157. * /n The FU indicator octet has the following format:
  158. * /n
  159. * /n      +---------------+
  160. * /n MSB  |0|1|2|3|4|5|6|7|  LSB
  161. * /n      +-+-+-+-+-+-+-+-+
  162. * /n      |F|NRI|  Type   |
  163. * /n      +---------------+
  164. * /n
  165. * /n The FU header has the following format:
  166. * /n
  167. * /n      +---------------+
  168. * /n      |0|1|2|3|4|5|6|7|
  169. * /n      +-+-+-+-+-+-+-+-+
  170. * /n      |S|E|R|  Type   |
  171. * /n      +---------------+
  172. */
  173. if ( m_RTP_Info.FU_flag )
  174. {
  175. // FU indicator  F|NRI|Type
  176. cp[12] = ( m_RTP_Info.nal.type & 0xe0 ) | 28 ;  //Type is 28 for FU_A
  177. //FU header     S|E|R|Type
  178. 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
  179. m_RTP_Info.s_bit = m_RTP_Info.e_bit= 0 ;
  180. m_RTP_Info.hdr_len = 14 ;
  181. }
  182. m_RTP_Info.start = &cp[m_RTP_Info.hdr_len] ;    // new start of payload
  183. *pPacketSize = m_RTP_Info.hdr_len + ( m_RTP_Info.end - m_RTP_Info.start ) ;
  184. return m_RTP_Info.pRTP ;
  185. }
  186. private:
  187. unsigned int StartCode( unsigned char *cp )
  188. {
  189. unsigned int d32 ;
  190. d32 = cp[3] ;
  191. d32 <<= 8 ;
  192. d32 |= cp[2] ;
  193. d32 <<= 8 ;
  194. d32 |= cp[1] ;
  195. d32 <<= 8 ;
  196. d32 |= cp[0] ;
  197. return d32 ;
  198. }
  199. private:
  200. RTP_INFO m_RTP_Info ;
  201. bool m_bBeginNAL ;
  202. unsigned short m_MAXRTPPACKSIZE ;
  203. };
  204. // class CH264_RTP_PACK end
  205. //////////////////////////////////////////////////////////////////////////////////////////
  206. //////////////////////////////////////////////////////////////////////////////////////////
  207. // class CH264_RTP_UNPACK start
  208. class CH264_RTP_UNPACK
  209. {
  210. #define RTP_VERSION 2
  211. #define BUF_SIZE (1024 * 500)
  212. typedef struct
  213. {
  214. //LITTLE_ENDIAN
  215. unsigned short   cc:4;      /* CSRC count                 */
  216. unsigned short   x:1;       /* header extension flag      */
  217. unsigned short   p:1;       /* padding flag               */
  218. unsigned short   v:2;       /* packet type                */
  219. unsigned short   pt:7;      /* payload type               */
  220. unsigned short   m:1;       /* marker bit                 */
  221. unsigned short    seq;      /* sequence number            */
  222. unsigned long     ts;       /* timestamp                  */
  223. unsigned long     ssrc;     /* synchronization source     */
  224. } rtp_hdr_t;
  225. public:
  226. CH264_RTP_UNPACK ( HRESULT &hr, unsigned char H264PAYLOADTYPE = 96 )
  227. : m_bSPSFound(false)
  228. , m_bWaitKeyFrame(true)
  229. , m_bPrevFrameEnd(false)
  230. , m_bAssemblingFrame(false)
  231. , m_wSeq(1234)
  232. , m_ssrc(0)
  233. {
  234. m_pBuf = new BYTE[BUF_SIZE] ;
  235. if ( m_pBuf == NULL )
  236. {
  237. hr = E_OUTOFMEMORY ;
  238. return ;
  239. }
  240. m_H264PAYLOADTYPE = H264PAYLOADTYPE ;
  241. m_pEnd = m_pBuf + BUF_SIZE ;
  242. m_pStart = m_pBuf ;
  243. m_dwSize = 0 ;
  244. hr = S_OK ;
  245. }
  246. ~CH264_RTP_UNPACK(void)
  247. {
  248. delete [] m_pBuf ;
  249. }
  250. //pBuf为H264 RTP视频数据包,nSize为RTP视频数据包字节长度,outSize为输出视频数据帧字节长度。
  251. //返回值为指向视频数据帧的指针。输入数据可能被破坏。
  252. BYTE* Parse_RTP_Packet ( BYTE *pBuf, unsigned short nSize, int *outSize )
  253. {
  254. if ( nSize <= 12 )
  255. {
  256. return NULL ;
  257. }
  258. BYTE *cp = (BYTE*)&m_RTP_Header ;
  259. cp[0] = pBuf[0] ;
  260. cp[1] = pBuf[1] ;
  261. m_RTP_Header.seq = pBuf[2] ;
  262. m_RTP_Header.seq <<= 8 ;
  263. m_RTP_Header.seq |= pBuf[3] ;
  264. m_RTP_Header.ts = pBuf[4] ;
  265. m_RTP_Header.ts <<= 8 ;
  266. m_RTP_Header.ts |= pBuf[5] ;
  267. m_RTP_Header.ts <<= 8 ;
  268. m_RTP_Header.ts |= pBuf[6] ;
  269. m_RTP_Header.ts <<= 8 ;
  270. m_RTP_Header.ts |= pBuf[7] ;
  271. m_RTP_Header.ssrc = pBuf[8] ;
  272. m_RTP_Header.ssrc <<= 8 ;
  273. m_RTP_Header.ssrc |= pBuf[9] ;
  274. m_RTP_Header.ssrc <<= 8 ;
  275. m_RTP_Header.ssrc |= pBuf[10] ;
  276. m_RTP_Header.ssrc <<= 8 ;
  277. m_RTP_Header.ssrc |= pBuf[11] ;
  278. BYTE *pPayload = pBuf + 12 ;
  279. DWORD PayloadSize = nSize - 12 ;
  280. // Check the RTP version number (it should be 2):
  281. if ( m_RTP_Header.v != RTP_VERSION )
  282. {
  283. return NULL ;
  284. }
  285. /*
  286. // Skip over any CSRC identifiers in the header:
  287. if ( m_RTP_Header.cc )
  288. {
  289. long cc = m_RTP_Header.cc * 4 ;
  290. if ( Size < cc )
  291. {
  292. return NULL ;
  293. }
  294. Size -= cc ;
  295. p += cc ;
  296. }
  297. // Check for (& ignore) any RTP header extension
  298. if ( m_RTP_Header.x )
  299. {
  300. if ( Size < 4 )
  301. {
  302. return NULL ;
  303. }
  304. Size -= 4 ;
  305. p += 2 ;
  306. long l = p[0] ;
  307. l <<= 8 ;
  308. l |= p[1] ;
  309. p += 2 ;
  310. l *= 4 ;
  311. if ( Size < l ) ;
  312. {
  313. return NULL ;
  314. }
  315. Size -= l ;
  316. p += l ;
  317. }
  318. // Discard any padding bytes:
  319. if ( m_RTP_Header.p )
  320. {
  321. if ( Size == 0 )
  322. {
  323. return NULL ;
  324. }
  325. long Padding = p[Size-1] ;
  326. if ( Size < Padding )
  327. {
  328. return NULL ;
  329. }
  330. Size -= Padding ;
  331. }*/
  332. // Check the Payload Type.
  333. if ( m_RTP_Header.pt != m_H264PAYLOADTYPE )
  334. {
  335. return NULL ;
  336. }
  337. int PayloadType = pPayload[0] & 0x1f ;
  338. int NALType = PayloadType ;
  339. if ( NALType == 28 ) // FU_A
  340. {
  341. if ( PayloadSize < 2 )
  342. {
  343. return NULL ;
  344. }
  345. NALType = pPayload[1] & 0x1f ;
  346. }
  347. if ( m_ssrc != m_RTP_Header.ssrc )
  348. {
  349. m_ssrc = m_RTP_Header.ssrc ;
  350. SetLostPacket () ;
  351. }
  352. if ( NALType == 0x07 ) // SPS
  353. {
  354. m_bSPSFound = true ;
  355. }
  356. if ( !m_bSPSFound )
  357. {
  358. return NULL ;
  359. }
  360. if ( NALType == 0x07 || NALType == 0x08 ) // SPS PPS
  361. {
  362. m_wSeq = m_RTP_Header.seq ;
  363. m_bPrevFrameEnd = true ;
  364. pPayload -= 4 ;
  365. *((DWORD*)(pPayload)) = 0x01000000 ;
  366. *outSize = PayloadSize + 4 ;
  367. return pPayload ;
  368. }
  369. if ( m_bWaitKeyFrame )
  370. {
  371. if ( m_RTP_Header.m ) // frame end
  372. {
  373. m_bPrevFrameEnd = true ;
  374. if ( !m_bAssemblingFrame )
  375. {
  376. m_wSeq = m_RTP_Header.seq ;
  377. return NULL ;
  378. }
  379. }
  380. if ( !m_bPrevFrameEnd )
  381. {
  382. m_wSeq = m_RTP_Header.seq ;
  383. return NULL ;
  384. }
  385. else
  386. {
  387. if ( NALType != 0x05 ) // KEY FRAME
  388. {
  389. m_wSeq = m_RTP_Header.seq ;
  390. m_bPrevFrameEnd = false ;
  391. return NULL ;
  392. }
  393. }
  394. }
  395. ///////////////////////////////////////////////////////////////
  396. if ( m_RTP_Header.seq != (WORD)( m_wSeq + 1 ) ) // lost packet
  397. {
  398. m_wSeq = m_RTP_Header.seq ;
  399. SetLostPacket () ;
  400. return NULL ;
  401. }
  402. else
  403. {
  404. // 码流正常
  405. m_wSeq = m_RTP_Header.seq ;
  406. m_bAssemblingFrame = true ;
  407. if ( PayloadType != 28 ) // whole NAL
  408. {
  409. *((DWORD*)(m_pStart)) = 0x01000000 ;
  410. m_pStart += 4 ;
  411. m_dwSize += 4 ;
  412. }
  413. else // FU_A
  414. {
  415. if ( pPayload[1] & 0x80 ) // FU_A start
  416. {
  417. *((DWORD*)(m_pStart)) = 0x01000000 ;
  418. m_pStart += 4 ;
  419. m_dwSize += 4 ;
  420. pPayload[1] = ( pPayload[0] & 0xE0 ) | NALType ;
  421. pPayload += 1 ;
  422. PayloadSize -= 1 ;
  423. }
  424. else
  425. {
  426. pPayload += 2 ;
  427. PayloadSize -= 2 ;
  428. }
  429. }
  430. if ( m_pStart + PayloadSize < m_pEnd )
  431. {
  432. CopyMemory ( m_pStart, pPayload, PayloadSize ) ;
  433. m_dwSize += PayloadSize ;
  434. m_pStart += PayloadSize ;
  435. }
  436. else // memory overflow
  437. {
  438. SetLostPacket () ;
  439. return NULL ;
  440. }
  441. if ( m_RTP_Header.m ) // frame end
  442. {
  443. *outSize = m_dwSize ;
  444. m_pStart = m_pBuf ;
  445. m_dwSize = 0 ;
  446. if ( NALType == 0x05 ) // KEY FRAME
  447. {
  448. m_bWaitKeyFrame = false ;
  449. }
  450. return m_pBuf ;
  451. }
  452. else
  453. {
  454. return NULL ;
  455. }
  456. }
  457. }
  458. void SetLostPacket()
  459. {
  460. m_bSPSFound = false ;
  461. m_bWaitKeyFrame = true ;
  462. m_bPrevFrameEnd = false ;
  463. m_bAssemblingFrame = false ;
  464. m_pStart = m_pBuf ;
  465. m_dwSize = 0 ;
  466. }
  467. private:
  468. rtp_hdr_t m_RTP_Header ;
  469. BYTE *m_pBuf ;
  470. bool m_bSPSFound ;
  471. bool m_bWaitKeyFrame ;
  472. bool m_bAssemblingFrame ;
  473. bool m_bPrevFrameEnd ;
  474. BYTE *m_pStart ;
  475. BYTE *m_pEnd ;
  476. DWORD m_dwSize ;
  477. WORD m_wSeq ;
  478. BYTE m_H264PAYLOADTYPE ;
  479. DWORD m_ssrc ;
  480. };
  481. // class CH264_RTP_UNPACK end
  482. //////////////////////////////////////////////////////////////////////////////////////////
上一篇:No input file specified. phpStudy nginx报错解决方案


下一篇:Codeforces 786 B. Legacy