28181开发之-视频打包

GB28181视频打包传输采用 ps打包+rtp传输 的方式;看下《28181安全防范视频监控联网系统信息传输》文档里面关于打包的介绍吧:


封装及传输标准

    媒体流在联网系统IP 网络上传输时应支持RTP 传输,RTP 的负载应采用如下两种格式之一:基于PS 封装的视音频数据 或 视音频基本流数据,详细的请看《28181安全防范视频监控联网系统信息传输》附录C。
    媒体流的传输应采用RFC 3550 规定的RTP协议,提供实时数据传输中的时间戳信息及各数据流的同步;应采用RFC 3550 规定的RTCP 协议,为按序传输数据包提供可靠保证,提供流量控制和拥塞控制(RTCP应该不是必须的)。

音视频编码标准

    联网系统中视频压缩编解码和音频编解码应符合《28181安全防范视频监控联网系统信息传输》附录E 的相关要求,采用视频编解码标准H.264/MPEG-4 , 应优先采用适用于安防监控的SVAC 标准; 音频编解码标准推荐采用
G.711/G.723.1/G.729/SVAC。

PS打包详细信息参考ISO/IEC 13818-1:2000,rtp方面参考RFC 3550

*/ES->PES
 
uint64 m_nDts, m_nFrameRate, m_nAudioPts;
 
/*
 
1.PES_packet_length=0,即不限制PES包长,只能用于视频PES包。
 
2.视频I帧和P帧的PES包中必须加入PTS和DTS,并且两者相等。
 
3.音频帧的PES包中只需包含PTS即可,但PTS必须根据实际情况计算,而不能直接加一个常数。
 
4.描述PTS与DTS的变量应为无符号64位的整数,而不是32位。
 
*/
 
int Packet_Video_frame(const char* pSrcBuf, int nSrcLen, char* pDestBuf, int& nLen, bool bFirst /*= true*/)/* 封装视频帧 */
 
{
 
int nTempLen = 0;
 

if((NULL == pSrcBuf)||(NULL == pDestBuf))
 
{
 
return PS_Error_Param;
 
}
 

/* 0~2字节为:包头起始码前缀,3字节为:数据流识别 */
 
memcpy(pDestBuf, PS_Video_Pes_Header, PS_Packet_Start_Code_Len);/* PS_Packet_Start_Code_Len:4个字节 */
 
/* 4~5字节为:PES包长/为0,即不限制后面ES的长度 */
 
pDestBuf[4] = 0x00;
 
pDestBuf[5] = 0x00;
 
/* 6~7字节为:PES包头识别标识 */
 
pDestBuf[6] = 0x88;
 
pDestBuf[7] = 0xC0;/* PTS_DTS_flag=11B */
 
/* 8字节为:PES包头长 */
 
/* 0011填充字段,表示既含有PTS,又含有DTS */
 
pDestBuf[8] = 0x0A;/* 可选字段和填充字段所占的字节数为10 */
 
/* #define OFFSET(x) (90000/x) */
 
m_nDts = m_nVideoPts + OFFSET(m_nFrameRate);/* DTS时标 */
 
m_nVideoPts = m_nVideoPts + OFFSET(m_nFrameRate);/* 视频PTS时标 */
 
/* 视频PTS */
 
pDestBuf[9] = ((0x0E&(m_nVideoPts>>29))|0x31);/* 0011填充字段 */
 
pDestBuf[10] = ((m_nVideoPts>>22)&(0xFF));
 
pDestBuf[11] = (((m_nVideoPts>>14)&(0xFE))|0x01);
 
pDestBuf[12] = ((m_nVideoPts>>7)&(0xFF));
 
pDestBuf[13] = (((m_nVideoPts<<1)&0xFE)|0x01);
 
/* 视频DTS */
 
pDestBuf[14] = ((0x0E&(m_nDts>>29))|0x11);/* 0001填充字段 */
 
pDestBuf[15] = ((m_nDts>>22)&(0xFF));
 
pDestBuf[16] = (((m_nDts>>14)&(0xFE))|0x01);
 
pDestBuf[17] = ((m_nDts>>7)&(0xFF));
 
pDestBuf[18] = (((m_nDts<<1)&0xFE)|0x01);
 

nTempLen = 19 ;
 
memcpy(pDestBuf+nTempLen, pSrcBuf, nSrcLen);/* 添加原始ES数据 */
 
nLen = nTempLen + nSrcLen;
 

return PS_Error_OK;
 
}
 
/*不对ES流/视频帧进行拆包*/
 
int GeneratePacketsFromFrame(const char* pSrcBuf, int nSrcLen, char* pDestBuf, int& nLen)
 
{
Packet_Video_frame(pSrcBuf, nSrcLen, pDestBuf, nLen);
 
m_nal_list.clear();
 
return PS_Error_OK;
 
}
 

int  SetAudioPTS( int aPTS )/* 设置音频PTS时标间隔值 */
 
{
 
m_uaudioPTS = aPTS;
 
return 0;
 
}
 
int Packet_Audio_frame_G711(const char* pSrcBuf, int nSrcLen, char* pDestBuf, int& nLen)/* 封装音频帧 */
 
{
 
int nTempLen = 0;
 

if((NULL == pSrcBuf)||(NULL == pDestBuf))
 
{
 
return PS_Error_Param;
 
}
 

/* 0~2字节为:包头起始码前缀,3字节为:数据流识别 */
 
memcpy(pDestBuf, PS_Audio_Pes_Header, PS_Packet_Start_Code_Len);/* PS_Packet_Start_Code_Len:4个字节 */
 
nTempLen = 8 + nSrcLen;
 
/* 4~5字节为:PES包长 */
 
pDestBuf[4] = (nTempLen>>8)&0xFF;
 
pDestBuf[5] = nTempLen&0xFF;
 
/* 6~7字节为:PES包头识别标识 */
 
pDestBuf[6] = 0x88;
 
pDestBuf[7] = 0x80;/* PTS_DTS_flag=10B */
 
/* 8字节为:PES包头长 */
 
/* 0010填充字段,表示只含有PTS,不含有DTS */
 
pDestBuf[8] = 0x05;/* 可选字段和填充字段所占的字节数为5 */
 
/* 音频PTS */
 
m_nAudioPts = m_nAudioPts + m_uaudioPTS;/* 音频PTS时标 */
 
if((m_nVideoPts-m_nAudioPts)>500000)
 
{
 
m_nAudioPts = m_nVideoPts;
 
printf("now audio pts=%d\n", m_nVideoPts);
 
}
 
pDestBuf[9] = ((0x0E&(m_nAudioPts>>29))|0x21);/* 0010填充字段 */
 
pDestBuf[10] = ((m_nAudioPts>>22)&(0xFF));
 
pDestBuf[11] = (((m_nAudioPts>>14)&(0xFE))|0x01);
 
pDestBuf[12] = ((m_nAudioPts>>7)&(0xFF));
 
pDestBuf[13] = (((m_nAudioPts<<1)&0xFE)|0x01);

nTempLen = 14 ;
 
memcpy(pDestBuf+nTempLen, pSrcBuf, nSrcLen);/* 添加原始ES数据 */
 
nLen = nTempLen + nSrcLen;
 

return PS_Error_OK;
 
}
 
*/PES->PS
 
uint64 m_nScrb;
 
/* PS头 */
 
int Packet_PS_header(char* pDestBuf, int& nLen, bool bVideo, bool bAligned /* = false */)
 
{
 
if(NULL == pDestBuf)
 
{
 
return PS_Error_Param;
 
}
 
/* PS头起始码前缀:0x000001BA */
 
memcpy(pDestBuf, PS_header, PS_Packet_Start_Code_Len);/* PS_Packet_Start_Code_Len:4个字节 */
 
/* 4~9字节为:SCR */
 
m_nScrb = m_nVideoPts;/* 系统时钟参考 */
 
pDestBuf[4] = (((m_nScrb>>27)&0x38)|0x44);
 
pDestBuf[4] = (((m_nScrb>>28)&0x03)|pDestBuf[4]);
 
pDestBuf[5] = ((m_nScrb>>20)&0xFF);
 
pDestBuf[6] = (((m_nScrb>>12)&0xF8)|0x04);
 
pDestBuf[6] = (((m_nScrb>>13)&0x03)|pDestBuf[6]);
 
pDestBuf[7] = ((m_nScrb>>5)&0xFF);
 
pDestBuf[8] = (((m_nScrb<<3)&0xF8)|0x04);
 
pDestBuf[9] = 0x01;/* SCR_EXT=0 */
 
/* 10~12字节为:PS流速率 */
 
pDestBuf[10] = MUX_RATE>>14;
 
pDestBuf[11] = MUX_RATE>>6;
 
pDestBuf[12] = (((MUX_RATE<<2)&0xFC)|0x03);
 
pDestBuf[13] = 0xFA;/* 填充字节数:2 */
 
/* 填充2个字节 */
 
pDestBuf[14] = 0xFF;
 
pDestBuf[15] = 0xFF;
 
nLen = PS_Header_Len_aligned;
 

return PS_Error_OK;
 
}
 
/* 系统头 */
 
int Packet_System_header(char* pDestBuf, int& nLen)
 
{
 
if(NULL == pDestBuf)
 
{
 
return PS_Error_Param;
 
}
 
/* 系统头起始码前缀:0x000001BB */
 
memcpy(pDestBuf, PS_System_Header, PS_Packet_Start_Code_Len);/* PS_Packet_Start_Code_Len:4个字节 */
 
/* 4~5字节为:该字段后的系统头长度 */
 
pDestBuf[4] = 0x00;/* 12字节 */
 
pDestBuf[5] = 0x0C;
 
/* rate_bound:0x0F7F */
 
/* rate_bound为大于或等于在任意节目流包中编码的program_mux_rate字段的最大值的整数值 */
 
pDestBuf[6] = 0x80;
 
pDestBuf[7] = 0x1E;
 
pDestBuf[8] = 0xFF;
 
/* audio_bound:0x3F */
 
/* audio_bound为大于或等于ISO/IEC 13818-3和ISO/IEC 11172-3音频流最大数的整数值 */
 
/* fixed_flag:1 */
 
/* fixed_flag为1时表示固定的比特速率操作,为0时表示可变比特速率操作 */
 
/* CSPS_flag:0 */
 
/* CSPS_flag为1表示节目流满足2.7.9中规定的限制 */
 
pDestBuf[9] = 0xFE;
 
/* system_audio_lock_flag:1 */
 
/* system_audio_lock_flag为1表示音频采样速率和系统目标解码器的system_clock_frequency之间存在特定的常量比率关系 */
 
/* system_video_lock_flag:1 */
 
/* system_video_lock_flag为1表示视频时间基和系统目标解码器的系统时钟频率之间存在特定的常量比率关系 */
 
/* video_bound:1 */
 
/* 在解码过程同时被激活的节目流中,video_bound被设置为大于或等于视流的最大数的整数值 */
 
pDestBuf[10] = 0xE1;
 
/* packet_rate_restriction_flag:0 */
 
/* 若CSPS标志设置为1,则packet_rate_restriction_flag指示适用于该包速率的那些限制,如2.7.9 中所指定的 */
 
/* 若CSPS标志设置为0,则packet_rate_restriction_flag的含义未确定 */
 
pDestBuf[11] = 0x7F;
 

/* stream_id:0xE0 */
 
/* stream_id为下面P-STD_buffer_bound_scale和P-STD_buffer_size_bound字段所涉及的流的编码与基本流编号 */
 
pDestBuf[12] = 0xE0;
 
/* P-STD_buffer_bound_scale:1/视频 */
 
/* P-STD_buffer_bound_scale指示用于解释后续P-STD_buffer_size_bound字段的标度因子 */
 
/* 若前导stream_id指示音频流,则P-STD_buffer_bound_scale必为0 */
 
/* 若前导stream_id指示视频流,则P-STD_buffer_bound_scale必为1 */
 
pDestBuf[13] = 0xE0;
 
/* P-STD_buffer_size_bound:0x0D8*/
 
/* P-STD_buffer_size_bound大于或等于节目流中流n的所有包上的最大P-STD输入缓冲器尺寸BSn */
 
/* 若P-STD_buffer_bound_scale为0,那么P-STD_buffer_size_bound以128字节为单位度量该缓冲器尺寸限制 */
 
/* 若P-STD_buffer_bound_scale为1,那么P-STD_buffer_size_bound以1024字节为单位度量该缓冲器尺寸限制 */
 
pDestBuf[14] = 0xD8;
 

/* stream_id:0xC0 */
 
pDestBuf[15] = 0xC0;
 
/* P-STD_buffer_bound_scale:0/音频 */
 
pDestBuf[16] = 0xC0;
 
/* P-STD_buffer_size_bound:0x020*/
 
pDestBuf[17] = 0x20;
 

nLen = 18;
 
return PS_Error_OK;
 
}
 
/* 节目流映射 */
 
int Packet_PS_map(char* pDestBuf, int& nLen, int Compression)
 
{
 
if(NULL == pDestBuf)
 
{
 
return PS_Error_Param;
 
}
 
/* 节目流映射起始码前缀:0x000001BC */
 
memcpy( pDestBuf, PS_Map_Header, PS_Packet_Start_Code_Len);/* PS_Packet_Start_Code_Len:4个字节 */
 
/* 4~5字节为:该字段后的节目流映射长度 */
 
pDestBuf[4] = 0x00;/* 24字节 */
 
pDestBuf[5] = 0x18;
 
/* current_next_indicator:1 */
 
/* 当current_next_indicator为1时表示发送的节目流映射为当前有效,为0时表示发送的节目流映射尚未有效并且下一个节目流映射表将生效 */
 
/* program_stream_map_version:1*/
 
/* 当current_next_indicator为1时,program_stream_map_version是当前有效的节目流映射的版本 */
 
pDestBuf[6] = 0xE1;
 
pDestBuf[7] = 0xFF;
 
/* program_stream_info_length:0*/
 
/* program_stream_info_length表示紧随此字段的描述符的总长 */
 
pDestBuf[8] = 0x00;
 
pDestBuf[9] = 0x00;
 
/* elementary_stream_map_length:8 */
 
/* elementary_stream_map_length表示在此节目流映射中所有基本流信息的总长度(以字节为单位) */
 
/* 它包括stream_type、elementary_stream_id和elementary_stream_info_length字段 */
 
pDestBuf[10] = 0x00;
 
pDestBuf[11] = 0x08;
 
/* stream_type:0x1B */
 
/* stream_type标识PES包中包含的基本流 */
 
pDestBuf[12] = 0x1B; //H.264
 
/* elementary_stream_id:0xE0 */
 
/* elementary_stream_id标识存储此基本流的PES包的PES包头内的stream_id字段的赋值/视频帧PES为0xE0 */
 
pDestBuf[13] = 0xE0;
 
/* elementary_stream_info_length:0x06 */
 
/* elementary_stream_info_length表示紧随此字段的描述符长度,以字节为单位 */
 
pDestBuf[14] = 0x00;
 
pDestBuf[15] = 0x06;
 
/* descriptor */
 
pDestBuf[16] = 0x0A;
 
pDestBuf[17] = 0x04;
 
pDestBuf[18] = 0x65;
 
pDestBuf[19] = 0x6E;
 
pDestBuf[20] = 0x67;
 
pDestBuf[21] = 0x00;
 
/* stream_type:0x90 */
 
/* stream_type标识PES包中包含的基本流 */
 
pDestBuf[22] = 0x90;
 
/* elementary_stream_id:0xC0 */
 
/* elementary_stream_id标识存储此基本流的PES包的PES包头内的stream_id字段的赋值/音频帧PES为0xC0 */
 
pDestBuf[23] = 0xC0;
 
/* elementary_stream_info_length:0 */
 
/* elementary_stream_info_length表示紧随此字段的描述符长度,以字节为单位 */
 
pDestBuf[24] = 0x00;
 
/* elementary_stream_info_length:0 */
 
/* elementary_stream_info_length表示紧随此字段的描述符长度,以字节为单位 */
 
pDestBuf[25] = 0x00;
 
/* CRC32 */
 
pDestBuf[26] = 0x23;
 
pDestBuf[27] = 0xB9;
 
pDestBuf[28] = 0x0F;
 
pDestBuf[29] = 0x3D;
 

nLen = 30;/* 现在PS_Map的长度都是固定的 */
 
return PS_Error_OK;
 
}
 
*/PS->RTP
 
/* 将PS经过RTP打包后添加到队列中 */
 
int sendFU_A( int iCh, unsigned int frameType, char* fui, char* fuh, char* data, int len, bool bMarker)
 
{
 
if((len <= 0)||(NULL == data))
 
{
 
return -1;
 
}
 

PACKET_NODE_T* node = new PACKET_NODE_T;
 
int& rtp_size = node->len;
 
char *pRtpBuf = node->buf;
 

if(m_rtpModel)/* RTP打包模式:1为UDP打包/0为TCP打包 */
 
{
 
RTPHDR_EXT *pszHdr = (RTPHDR_EXT *)(pRtpBuf);/* RTPHDR_EXT:RTP头结构 */
 
memset((char *)pszHdr, 0, RTPHDR_EXT_SIZE);/* RTPHDR_EXT_SIZE:RTP头结构长度 */
 

pszHdr->version = 2;/* 版本号 */
 
pszHdr->padding = 0;/* 填充标识 */
 
pszHdr->extension = 0;/* 扩展标识 */
 
pszHdr->csrccount = 0;/* CSRC计数器 */
 
pszHdr->payloadtype = 96;/* 负载类型 */
 
pszHdr->marker = bMarker;/* 标记 */
 
pszHdr->seqnum = htons((unsigned short)getSequenceNum());/* 序列号 */
 
pszHdr->timestamp = htonl(m_iTimeStep);/* 时间戳 */
 
pszHdr->ssrc = htonl(m_iRtpSsrc);/* SSRC识别符/标识本次会话 */
 

if(NULL == fui)/* 不存在fui */
 
{
 
memcpy(pRtpBuf+RTPHDR_EXT_SIZE, data, len);
 
rtp_size = RTPHDR_EXT_SIZE + len;
 
}
 
else/* 存在fui的话,先添加fui与fuh,之后再添加帧数据 */
 
{
 
memcpy(pRtpBuf + RTPHDR_EXT_SIZE, fui, 1);
 
memcpy(pRtpBuf + RTPHDR_EXT_SIZE+1, fuh, 1);
 
memcpy(pRtpBuf + RTPHDR_EXT_SIZE+2, data, len);
 

rtp_size = RTPHDR_EXT_SIZE + 2 + len;
 
}
 
}
 
else/* TCP打包,需要在RTP包之前加2个字节的长度 */
 
{
 
RTPHDR_EXT pszHdr;/* RTPHDR_EXT:RTP头结构 */
 
memset(&pszHdr, 0, RTPHDR_EXT_SIZE);/* RTPHDR_EXT_SIZE:RTP头结构长度 */
 

pszHdr.version = 2;
 
pszHdr.padding = 0;
 
pszHdr.extension = 1;/* 扩展标识 */
 
pszHdr.csrccount = 0;
 
pszHdr.payloadtype = 96;
 
pszHdr.marker = bMarker;
 
pszHdr.seqnum = htons((unsigned short)getSequenceNum());
 
pszHdr.timestamp = htonl(m_iTimeStep);
 
pszHdr.ssrc = htonl(m_iRtpSsrc);
 

/* extension/扩展字段 */
 
RTP_EXT_Header RTPEXTHeader;/* RTP_EXT_Header:RTP头扩展字段结构 */
 
memset(&RTPEXTHeader, 0, sizeof(RTP_EXT_Header));
 
RTPEXTHeader.profile = htons(1);/* 概况 */
 
RTPEXTHeader.len = htons(1);/* 长度:指示扩展项中32比特字的个数 */
 
if( PACK_TYPE_FRAME_I == frameType )/* 视频I帧 */
 
{
 
RTPEXTHeader.FrameType = 0;
 
}
 
else if( PACK_TYPE_FRAME_P == frameType )/* 视频P帧 */
 
{
 
RTPEXTHeader.FrameType = 1;
 
}
 
else if( PACK_TYPE_FRAME_B == frameType )/* 视频B帧 */
 
{
 
RTPEXTHeader.FrameType = 2;
 
}
 
else if( PACK_TYPE_FRAME_AUDIO == frameType )/* 音频帧 */
 
{
 
RTPEXTHeader.FrameType = 8;
 
}
 
if(bMarker)
 
{
 
RTPEXTHeader.Framemarker = 1;
 
}
 
else
 
{
 
RTPEXTHeader.Framemarker = 0;
 
}
 

unsigned short packetlen = htons((unsigned short)(RTPHDR_EXT_SIZE + sizeof(RTPEXTHeader) + len));
 
memcpy(pRtpBuf, (void *)&packetlen, 2);
 
memcpy(pRtpBuf+2, (void *)&pszHdr, sizeof(RTPHDR_EXT));
 
memcpy(pRtpBuf+2+RTPHDR_EXT_SIZE, (void *)&RTPEXTHeader, sizeof(RTP_EXT_Header));
 
memcpy(pRtpBuf+2+RTPHDR_EXT_SIZE + sizeof(RTPEXTHeader), data, len);
 
rtp_size = RTPHDR_EXT_SIZE + sizeof(RTPEXTHeader) + len + 2;
 
}
 

m_Packet_list.push_back(node);/* 将当前帧数据节点加入队列 */
 
return 0;
 
}
 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
WebRTC是一个实时通信的开源项目,其源码开发涵盖了视频编码、打包以及流程模块等。在这个开发笔记中,我将对WebRTC的视频编码打包流程模块进行图解说明。 WebRTC的视频编码打包流程主要涉及到以下几个模块:视频源模块、视频编码模块、打包模块和传输模块。 首先是视频源模块,它负责从摄像头或者文件中获取视频数据。该模块通过调用底层API来获取视频帧,并将其传递给下一个模块。 接下来是视频编码模块,它负责将获取到的视频帧进行压缩编码。具体来说,该模块会对视频帧进行压缩算法处理,将其转换为可传输的数据格式。WebRTC支持多种视频编码算法,如VP8、H.264等。 然后是打包模块,它负责将编码后的视频数据打包成数据包。该模块会对视频数据进行分片,并为每个片设置头部信息,以便接收端能够正确解析和处理。 最后是传输模块,它负责将打包好的视频数据通过网络传输到接收端。该模块依赖于网络协议,如UDP或TCP等进行数据传输。在传输过程中,还会涉及到网络拥塞控制和丢包重传等机制,以保证传输的稳定性和可靠性。 以上就是WebRTC视频编码打包流程模块的图解说明。通过这些模块的协作,WebRTC能够实现实时的视频传输和通信功能。开发者可以根据需要对各个模块进行定制和扩展,以满足不同的应用场景需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值