流媒体协议.之(RTP,RTCP,RTSP,RTMP,HTTP)(二)

继续上篇介绍,本篇介绍一下封装RTP的数据格式,如何将摄像头采集的码流,音频的码流,封装到rtp里,传输。
有自己私有协议例子,有rtp协议,参考代码。注意不是rtsp协议。

在这里插入图片描述

一、私有协议

玩过tcp协议都知道,有这么一层关系
在这里插入图片描述

玩过网络开发的,应该都自己定义封装过私有协议。

我把tcp,udp,网络传输封装在另一个文件,网络传输层。然后分一个模块,私有协议,封装有效数据,数据封装层。

封装一个协议头

typedef struct MediumPrivateHead
{SX_U32 	u32Mf;SX_U16 	u16Type;SX_U32 	u32Ts;SX_U16 	u16Seqno;SX_U32 	u32Length;SX_U8 	u8Ch;SX_U8 	u8Of;SX_U8 	u8Marker;SX_U8 	u8X;
}T_MediumPrivateHead, *PT_MediumPrivateHead;
#pragma pack()typedef enum PacketType
{PACKET_TYPE_H264 = 118,PACKET_TYPE_JPEG = 119,PACKET_TYPE_AAC  = 120,PACKET_TYPE_G711 = 121,PACKET_TYPE_H265 = 122,PACKET_TYPE_ADPCM_DVI4 = 123,PACKET_TYPE_G721 = 124,PACKET_TYPE_RS422 = 125,PACKET_TYPE_BUTT = 126,
}E_PacketType;

分片发送,数据包太大,进行分包传输,不管是什么协议,都需要调用这个函数来分包传输,传入一个bufer数据指针。

static SX_U32 MEDIUM_UDP_WriteFrame1( SX_S32 ch, SX_S8 ePacketType, void *handle, SX_S8 *ps8Buffer, SX_U32 u32Length, SX_U64 u64Pts, SX_S8 *ps8TsString )
{PT_Medium ptTmp = (PT_Medium)handle;SX_U32 u32Pos = 0;SX_U32 u32LeftLen = u32Length;SX_S8 *ps8Tmp = ps8Buffer;int allow_send_max_len = 0;//if(ptTmp->u32FragmentLen < 300) // len too short//ptTmp->u32FragmentLen = 300;allow_send_max_len = ptTmp->u32FragmentLen - sizeof(T_MediumPrivateHead);if( u32LeftLen <= allow_send_max_len ){if( MEDIUM_UDP_WritePrivatePacket( ch, ePacketType, handle, ps8Tmp, u32LeftLen, u64Pts, 1, 0 ) < 0 )return 0;	}else{while( u32LeftLen > allow_send_max_len ){if( MEDIUM_UDP_WritePrivatePacket( ch, ePacketType, handle, ps8Tmp + u32Pos, allow_send_max_len, u64Pts, 0, 0 ) < 0 )return 0;u32LeftLen -= allow_send_max_len;u32Pos += allow_send_max_len;}if( MEDIUM_UDP_WritePrivatePacket( ch, ePacketType, handle, ps8Tmp + u32Pos, u32LeftLen, u64Pts, 1, 0 ) < 0 )return 0;}return u32Length;
}

封装私有协议包,供上面函数调用,如果是其他协议,就封装另一个函数。将有效数据和协议头封装,打包,通过udp或tcp的接口sendto发送出去


static SX_S32 MEDIUM_UDP_WritePrivatePacket( SX_S32 ch, SX_S8 ePacketType, void *handle, SX_S8 *ps8Buffer, SX_U32 u32Length, SX_U64 u64Pts, SX_U32 u32Marker, SX_U32 u32X )
{if(ch < 0 || ch >= MEDIUM_MAX_CH)return -1;SX_U32 u32Pos = 0;PT_Medium ptTmp 	= (PT_Medium)handle;pthread_mutex_lock( &ptTmp->mutex);SX_S8 *ps8Buf	= ptTmp->ps8Buffer;memset(ps8Buf ,0 ,MAX_CONFIG_FILE_LEN);SX_U32 u32myframeLen;if( u32Length > ptTmp->u32FragmentLen - sizeof(T_MediumPrivateHead))u32myframeLen = ptTmp->u32FragmentLen- sizeof(T_MediumPrivateHead);elseu32myframeLen = u32Length;//file private head	//PT_VedioDataPkt pkthead1 = (PT_VedioDataPkt)(ps8Buf);//PT_MediumPrivateHead ptHead2 = (PT_MediumPrivateHead)(&pkthead1->headprivate);PT_MediumPrivateHead ptHead2 = (PT_MediumPrivateHead)(ps8Buf);memset((SX_U8 *)ptHead2, 0, sizeof(T_MediumPrivateHead) );ptHead2->u32Mf 	            = (SX_U32)0x4b4e4148;ptHead2->u16Type           = (SX_U16)ePacketType;ptHead2->u32Ts 	            = (SX_U32)u64Pts;	//not usedptHead2->u16Seqno 	    = ((ePacketType ==  PACKET_TYPE_AAC )? (SX_U16)ptTmp->u16AudioSeqno[ch] : (SX_U16)ptTmp-  >u16VedioSeqno[ch]);ptHead2->u32Length        = (SX_U32)u32myframeLen;ptHead2->u8Ch 	            = (SX_U8)ch;ptHead2->u8Of 		    = (SX_U8)0x19;ptHead2->u8Marker 	    = (SX_U8)u32Marker;ptHead2->u8X 		    = (SX_U8)u32X;u32Pos 				    = sizeof(T_MediumPrivateHead);//filevedio frame datamemcpy( ps8Buf + u32Pos, ps8Buffer, u32myframeLen );//CRC32 	= 0xC4C3C2C1;*(ps8Buf + u32Pos + u32myframeLen + 0)= 0xC1;*(ps8Buf + u32Pos + u32myframeLen + 1)= 0xC2;*(ps8Buf + u32Pos + u32myframeLen + 2)= 0xC3;*(ps8Buf + u32Pos + u32myframeLen + 3)= 0xC4;if(ePacketType ==  PACKET_TYPE_AAC){//printf("++   u16AudioSeqno  %ld    u32Length  %ld ++\r\n",ptTmp->u16AudioSeqno[ch],sizeof(T_MediumPrivateHead) + ptHead2->u32Length + 4);ptTmp->u16AudioSeqno[ch]++;}else{ptTmp->u16VedioSeqno[ch]++;}if(ptTmp->u16AudioSeqno[ch] > 65536)ptTmp->u16AudioSeqno[ch] = 0;if(ptTmp->u16VedioSeqno[ch] > 65536)ptTmp->u16VedioSeqno[ch] = 0;
#endifpthread_mutex_unlock( &ptTmp->mutex);ts1 = get_sys_ms();if( sendto(ptTmp->fd,ps8Buf,sizeof(T_MediumPrivateHead) + u32myframeLen + 4,	//headlen + datalen + crclen,0,(struct sockaddr *)&ptTmp->other[(ePacketType ==  PACKET_TYPE_AAC) ? 1 : 0],sizeof(struct sockaddr_in)) < 0 )	            TRACE( DL_WARNING, "udp send failed\n" );return 0;
}

二、 RTP协议

RTP传输音视频过程如下:
在这里插入图片描述

在这里插入图片描述

如果不按我上面私有协议传输,那就需要封装一个RTP协议,要熟悉协议格式,进行封装。

RTP报文格式
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
用C语言来封装,如下
在这里插入图片描述

取一段抓包数据
在这里插入图片描述

三、RTP封装视频

3.1、RTP封装H264

首先看一下H264 NALU头部定义:一个字节,是8位,按位进行划分,代表的意义。

在这里插入图片描述

F: 1 个比特. 一般为0 forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0.
NRI: 2 个比特. nal_ref_idc. 取 00 ~ 11, 指示nalu单元的重要性,, 如 00 的 NALU 解码器可以丢弃而不影响图像的回放. 不过一般情况下不太关心这个属性.
Type: 5 个比特.nal_unit_type. 这个 NALU 单元的类型.
在这里插入图片描述

RTP打包原则

RTP的包长度必须要小于MTU(最大传输单元),IP协议中MTU的最大长度为1500字节。除去IP报头(20字节)、UDP报头(8字节)、RTP头(12字节),所有RTP有效载荷(即NALU内容)的长度不得超过1460字节。TCP(20字节)。上面我的例子代码有参考,分包。

RTP有三种封包模式:单一封包模式,组合封包模式,分片封包模式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这是我的协议,我所有的通信协议,都会加上这么一个起始头,用来区别本系统,发送的数据包开始字段
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3.4、PES分组头部
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以看到RTP数据头,协议,非常多,没什么难度,就是多,一般我们都不会从新造轮子,网上很多大佬,开源分享,移植过来,修改就OK了,下篇,直接贴代码,封装协议,要老命,自己去手撸出来,手撸linux内核代码没必要。

找到一个博主封装的h264,封包,解包

RTP荷载H264的代码参考:
http://blog.csdn.net/dengzikun/article/details/5807694

RTP荷载PS流的代码参考:

http://www.pudn.com/downloads33/sourcecode/windows/multimedia/detail105823.html
http://www.oschina.net/code/snippet_99626_23737

这是他说的
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  

四、推拉流测试

另一个博主封装了一个推拉流的demo,可以移植到项目中,对h264,aac,感谢博主的热心分享。
他用FFmpeg去对一个事先准备好的mp4文件,读取流,然后通过RTSP协议,推流到一个文件夹,然后写了个客户端,rtsp,拉流,播放。
如果我么要移植到ipc项目中,这里需要修改一下,将soc采集到的视频流,放到rtp包里,去掉FFmpeg解码,也不需要移植FFmpeg。

原文链接:https://blog.csdn.net/weixin_43147845/article/details/140923649

在这里插入图片描述
地址:https://github.com/BreakingY/simple-rtsp-client

1、准备

simple-rtsp-server依赖ffmpeg,版本要求>=4.x。支持系统:Linux

依赖安装:

sudo apt-get -y install autoconf automake build-essential libass-dev libfreetype6-dev libsdl2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texinfo zlib1g-dev

汇编库:

sudo apt-get install yasm
sudo apt-get install nasm

视频库:

sudo apt-get install libx264-dev
sudo apt-get install libx265-dev

音频库:

sudo apt-get install libfdk-aac-dev
sudo apt-get install libmp3lame-dev
sudo apt-get install libopus-dev

ffmpeg源码下载:

wget https://ffmpeg.org//releases/ffmpeg-4.0.5.tar.bz2

tar xjvf ffmpeg-4.0.5.tar.bz2

cd ffmpeg-4.0.5

编译安装:

./configure --prefix=/usr/local --enable-libx264 --disable-x86asm --enable-nonfree --enable-libfdk-aac  --enable-shared --enable-gpl --enable-libmp3lame --enable-libopus  --extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/libmakemake install

2、simple-rtsp-server下载编译
下载后

cd simple-rtsp-servermkdir buildcd buildcmake ..make -j

3、运行

cp -r ../mp4path ../rtsp_server 0 (0-不鉴权;1-鉴权)

4、拉流测试
项目中mp4path自带了测试文件,后面把想回放的视频放到mp4path中即可

TCP拉流:

ffmpeg -rtsp_transport tcp -i "rtsp://192.168.10.17:8554/test_h264_aac.mp4" -vcodec copy -acodec copy  test_h264_aac_tcp.mp4

UDP拉流:

ffmpeg -i "rtsp://192.168.10.17:8554/test_h264_aac.mp4" -vcodec copy -acodec copy  test_h264_aac_udp.mp4

也可通过VLC直接播放,点击媒体->打开网络串流,输入rtsp地址即可。默认是udp拉流,要使用TCP需要打开工具->偏好设置->输入/编解码器,拉到最下方,选择“RTP over RTSP(TCP)”

他也写了一个客户端拉流,不用上面测试命令FFmpeg

二、RTSP Client实战项目

地址:https://github.com/BreakingY/simple-rtsp-client

支持RTP OVER UDP、RTP OVER TCP,支持H264/H265、AAC/PCMA、支持鉴权。

不需要任何依赖。

1、下载后编译

mkdir buildcd buildcmake ..make -j

2、测试
./rtsp_client rtsp_url

客户端会把收到的音视频写入文件,H264/H265写入到test_out.h26x,AAC写入到test_out.aac,PCMA写入到test_out.pcma。

另一个比较厉害的,比较全
https://github.com/ireader/media-server

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/58322.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

构建灵活、高效的HTTP/1.1应用:探索h11库

文章目录 构建灵活、高效的HTTP/1.1应用&#xff1a;探索h11库背景这个库是什么&#xff1f;如何安装这个库&#xff1f;库函数使用方法使用场景常见的Bug及解决方案总结 构建灵活、高效的HTTP/1.1应用&#xff1a;探索h11库 背景 在现代网络应用中&#xff0c;HTTP协议是基础…

利用游戏引擎的优势

大家好&#xff0c;我是小蜗牛。 在当今快速发展的游戏产业中&#xff0c;选择合适的游戏引擎对开发者来说至关重要。Cocos Creator作为一款功能强大且灵活的游戏引擎&#xff0c;为开发者提供了丰富的工具和资源&#xff0c;使他们能够高效地开发出优秀的游戏。本文将探讨如何…

TensorFlow面试整理-分布式

在深度学习的训练过程中,随着数据量和模型的复杂性增加,单个 GPU 或 CPU 无法满足高效训练的需求。TensorFlow 提供了强大的 分布式训练 功能,通过并行处理加速训练过程。分布式训练可以在多个 GPU、多个机器甚至是 TPU 上运行。以下是分布式训练的关键概念及其使用方法。 1…

仓颉编程语言官网正式上线 !首个公测版本开放下载 !

今年6月21日&#xff0c;华为开发者大会&#xff08;HDC&#xff09;正式公开介绍了华为自研的通用编程语言&#xff1a;仓颉编程语言&#xff0c;并发布了HarmonyOS NEXT仓颉语言开发者预览版&#xff0c;开发者可以使用仓颉开发鸿蒙原生应用。4个月以来&#xff0c;仓颉编程语…

PHP爬虫的奇幻之旅:如何用代码“偷窥”京东商品的SKU信息

开篇&#xff1a;代码界的007 想象一下&#xff0c;你是一名代码界的007&#xff0c;你的任务是潜入京东的数据库&#xff0c;获取商品的SKU信息。不过别担心&#xff0c;我们不是真的去偷数据&#xff0c;而是用PHP编写一个爬虫&#xff0c;合法地获取公开的API数据。这不仅是…

C++初阶(七)--类和对象(4)

目录 ​编辑 一、再谈构造函数 1.构造函数体赋值 2.初始化列表 二、类型转换 1.隐式类型转换 2.explicit关键字 3.类类型之间的对象隐式转换 三、static成员函数 1.概念 2.特性 3.面试题&#xff1a; 四、友元函数 1.基本介绍 2.回顾&#xff1a; 3.友元类&am…

【问题记录】当机器人存在多个串口需要绑定时udevadm的作用

一、正常绑定 输入sudo udevadm info -a /dev/ttyUSBx | grep KERNELS 命令 会出现KERNELS的编号&#xff0c;记录编号。 修改规则文件/etc/udev/rules.d/99-usb.rules 添加以下命令 KERNEL"ttyUSB*", KERNELS"2-1.2:1.0", MODE:"0666", GROU…

kafka 分布式(不是单机)的情况下,如何保证消息的顺序消费?

大家好&#xff0c;我是锋哥。今天分享关于【kafka 分布式&#xff08;不是单机&#xff09;的情况下&#xff0c;如何保证消息的顺序消费?】面试题&#xff1f;希望对大家有帮助&#xff1b; kafka 分布式&#xff08;不是单机&#xff09;的情况下&#xff0c;如何保证消息的…

掌握闲鱼商品 API 接口对接流程

对接闲鱼商品 API 接口主要有以下步骤&#xff1a; 注册成为闲鱼平台合作伙伴&#xff1a; 访问闲鱼开放平台&#xff08;如添加 TNY264278 卫星号&#xff09;&#xff0c;按照平台要求进行账号注册。你需要提供个人或公司的相关信息&#xff0c;如联系方式、企业资质&#xf…

微信小程序时间弹窗——年月日时分

需求 1、默认当前时间2、选择时间弹窗限制最大值、最小值3、每次弹起更新最大值为当前时间&#xff0c;默认值为上次选中时间4、 minDate: new Date(2023, 10, 1).getTime(),也可以传入时间字符串new Date(2023-10-1 12:22).getTime() html <view class"flex bb ptb…

【UE5.3 Cesium for Unreal】编译GlobePawn

目录 前言 效果 步骤 一、下载所需文件 二、下载CesiumForUnreal插件 三、处理下载的文件 四、修改代码 “CesiumForUnreal.uplugin”部分 “CesiumEditor.cpp”部分 “CesiumEditor.h”部分 “CesiumPanel.cpp”部分 “IonQuickAddPanel.cpp”部分 “IonQuickAd…

单目相机标定

利用ROS的Camera Calibration工具进行USB单目相机标定 标定前准备的东西编译运行标定代码移动棋盘格标定结果总结单目相机标定参考网址 标定前准备的东西 1.大型棋盘格:具有已知尺寸的棋盘格。本教程使用的是一个8x6的棋盘格,方格边长为108毫米。标定时使用棋盘格的内部顶点…

截取一个字符串的一部分赋值给另一个字符串

文章目录 截取一个字符串的一部分赋值给另一个字符串1.string s(s1,pos,len)2.s.substr(pos,n) 返回一个string 截取一个字符串的一部分赋值给另一个字符串 1.string s(s1,pos,len) s是string s1从下标pos开始len个字符的拷贝。如果pos>s1.size()&#xff0c;构造函数未定…

Nginx+Lua脚本+Redis 实现自动封禁访问频率过高IP

1 、安装OpenResty 安装使用 OpenResty&#xff0c;这是一个集成了各种 Lua 模块的 Nginx 服务器&#xff0c;是一个以Nginx为核心同时包含很多第三方模块的Web应用服务器&#xff0c;使用Nginx的同时又能使用lua等模块实现复杂的控制。 &#xff08;1&#xff09;安装编译工具…

uniapp通过id获取div的宽度,高度,位置等(应该是 任意平台都通用 )

uniapp通过id获取div的宽度&#xff0c;高度&#xff0c;位置等&#xff08;应该是 任意平台都通用 &#xff09; <template><view class"" id"domId"></view> </template>// 如果获取的dome高度等不对&#xff0c;还需要加上延迟…

[Linux] linux 软硬链接与动静态库

标题&#xff1a;[Linux] linux 软硬链接与动静态库 个人主页水墨不写bug &#xff08;图片来源于网络&#xff09; /** _oo0oo_* o8888888o* 88" . "88* (| -_- |)* …

力扣-最小覆盖子串

76. 最小覆盖子串 - 力扣&#xff08;LeetCode&#xff09; 给定一个字符串s,和目标字符串t&#xff0c;需要找出s中包含t中所有字符且长度最小子串&#xff0c;输出这个子串 滑动窗口&#xff0c;初始时左右指针都指向s的第一个字符,对于每个遍历到的窗口&#xff0c;判断当…

通过cv库智能切片 把不同的分镜切出来 自媒体抖音快手混剪

用 手机自动化脚本&#xff0c;从自媒体上获取视频&#xff0c;一个商品对应几百个视频&#xff0c;我们把这几百个视频下载下来&#xff0c;进行分镜 视频切片&#xff0c;从自媒体上下载视频&#xff0c;通过cv库用直方图识别每个镜头进行切片。 下载多个图片进行视频的伪原…

学习threejs,使用粒子实现下雪特效

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.Points简介1.11 ☘️…

如何实现PHP的安全最大化

要实现PHP的安全最大化&#xff0c;并避免SQL注入漏洞和XSS跨站脚本攻击漏洞&#xff0c;你需要采取一系列的安全措施。以下是一些关键步骤和最佳实践&#xff1a; 1. 输入验证和过滤 验证用户输入&#xff1a;对所有用户输入进行验证&#xff0c;确保它们符合预期格式。例如…