流媒体协议.之(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;使他们能够高效地开发出优秀的游戏。本文将探讨如何…

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

今年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;如何保证消息的…

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

需求 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…

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

文章目录 截取一个字符串的一部分赋值给另一个字符串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;安装编译工具…

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

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

通过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 ☘️…

C++之多态的深度剖析

目录 前言 1.多态的概念 2.多态的定义及实现 2.1多态的构成条件 2.1.1重要条件 2.1.2 虚函数 2.1.3 虚函数的重写/覆盖 2.1.4 选择题 2.1.5 虚函数其他知识 协变&#xff08;了解&#xff09; 析构函数的重写 override 和 final关键字 3. 重载&#xff0c;重写&…

Codeforces Round 919 (Div. 2)

B. Summation Game 题意 输入 输出 思路 遇到两人博弈问题&#xff0c;可以分别贪心&#xff0c;先贪心一个固定下来&#xff0c;然后遍历贪心另一个人 void solve() {int n, k, x;cin >> n >> k >> x;vector<int> arr(n 1);int *s new int[n …

GIT分布式版本控制系统基础操作

问题大纲 1、什么分布式版本控制系统 2、简述Git的使用分为哪几个步骤 3、克隆和拉取的区别是什么&#xff1f; 4、git相关的所有指令 一、分布式版本控制系统 分布式版本控制系统是一种版本控制系统&#xff0c;它允许每个用户都拥有完整的项目历史记录和版本控制信息。与…

【linux网络编程】| socket套接字 | 实现UDP协议聊天室

前言&#xff1a;本节内容将带友友们实现一个UDP协议的聊天室。 主要原理是客户端发送数据给服务端。 服务端将数据再转发给所有链接服务端的客户端。 所以&#xff0c; 我们主要就是要实现客户端以及服务端的逻辑代码。 那么&#xff0c; 接下来开始我们的学习吧。 ps:本节内容…

vivo 轩辕文件系统:AI 计算平台存储性能优化实践

在早期阶段&#xff0c;vivo AI 计算平台使用 GlusterFS 作为底层存储基座。随着数据规模的扩大和多种业务场景的接入&#xff0c;开始出现性能、维护等问题。为此&#xff0c;vivo 转而采用了自研的轩辕文件系统&#xff0c;该系统是基于 JuiceFS 开源版本开发的一款分布式文件…

Java | Leetcode Java题解之第520题检测大写字母

题目&#xff1a; 题解&#xff1a; class Solution {public boolean detectCapitalUse(String word) {// 若第 1 个字母为小写&#xff0c;则需额外判断第 2 个字母是否为小写if (word.length() > 2 && Character.isLowerCase(word.charAt(0)) && Charact…