分析一个通用的rtsp server实现过程发送模块
2017-08-07 20:59
561 查看
这里主要介绍rtsp之后的发送流程,rtsp到达play之后,通过判断参数是play之后,可以启动一个线程
来发送已经打包好的ts流,于是需要将ts流打包rtp包,
正常从网上信息可以看出:RTP with payload type 33 (MPEG2 TS): The RTP timestamp 表示RTP packet的TRANSMISSION time。(来自RFC 2250)
同时,从这里可以了解到:
http://blog.csdn.net/xiaojun111111/article/details/52208370
TS流是有许多的TS Packet组成的,每个TS Packet的长度固定为188 bytes,每个packet都是以sync_byte:0x47开头。
MTU(Maximum Transmission Unit): 最大传输单元。是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位)。最大传输单元这个参数通常与通信接口有关(网络接口卡、串口等)。例如:以太网无法接收大于1500
字节的数据包。
协议分析 :每一个RTP数据报都由头部(Header)和负载(Payload)两个部分组成,其中头部前 12 个字节的含义是固定的,而负载则可以是音频或者视频数据。
于是定义:
然后定义rtp包的头部
这里的socket中的send函数里面的port和ip是通过前面的rtsp setup阶段确定,并通过设置接口add_dest_socket_info传递到这里,如下:
于是,发送的时候,直接获取就ok了,如下:
当然上面是mp3的例子,ts流也可以借鉴这个方式来同样构造一套,用来发送就ok了。
我们通过后面采集的数据,写入到buffer数据里面,然后此时发送线程,就需要从buffer里面来读取数据了,
参考这里的文章http://www.codeforge.com/read/96791/RtpPacket.cpp__html
可以发现,需要构造rtp packet将前面的header填充,于是这样写:
于是接下来就需要将前面的buffer数据发送到client端了,
可以简单的见下面的逻辑,如下:
对于buffer的长度小于len的情况,采取一个rtp包发送就ok了,序列号需要递增,和ping一样,用来标志一个包,
然后直接通过rtp的send函数发送,这里的socket结构体没有耦合好,建议最好将这里的send函数直接用base 库里面
的sokcet类的继承类udp来实现,这里就比较合理!
那么对于buffer的长度大于len的情况,需要分段发送了,
首先统计多少个mtu的个数,和最后的个数,很容易算出来的,
当然如果是mtu的整数倍的话,需要单独拿出来,作为一个特例存在,
于是可以进行while循环操作了,count用来计数,
如下:
接下来,处理的步骤如下:前面k个mtu包发送,最后一个单独发送,于是可以写成下面的代码:
来发送已经打包好的ts流,于是需要将ts流打包rtp包,
正常从网上信息可以看出:RTP with payload type 33 (MPEG2 TS): The RTP timestamp 表示RTP packet的TRANSMISSION time。(来自RFC 2250)
同时,从这里可以了解到:
http://blog.csdn.net/xiaojun111111/article/details/52208370
TS流是有许多的TS Packet组成的,每个TS Packet的长度固定为188 bytes,每个packet都是以sync_byte:0x47开头。
MTU(Maximum Transmission Unit): 最大传输单元。是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位)。最大传输单元这个参数通常与通信接口有关(网络接口卡、串口等)。例如:以太网无法接收大于1500
字节的数据包。
协议分析 :每一个RTP数据报都由头部(Header)和负载(Payload)两个部分组成,其中头部前 12 个字节的含义是固定的,而负载则可以是音频或者视频数据。
于是定义:
#define TS_PACKET_SIZE 188 #define MTU (7 * TS_PACKET_SIZE) //1316 + 12 < 1500 #define TS_PAYLOAD_TYPE 33
然后定义rtp包的头部
typedef struct { /**//* byte 0 */ unsigned char csrc_len : 4; /**//* expect 0 */ unsigned char extension : 1; /**//* expect 1, see RTP_OP below */ unsigned char padding : 1; /**//* expect 0 */ unsigned char version : 2; /**//* expect 2 */ /**//* byte 1 */ unsigned char payload : 7; /**//* RTP_PAYLOAD_RTSP */ unsigned char marker : 1; /**//* expect 1 */ /**//* bytes 2, 3 */ unsigned short seq_no; /**//* bytes 4-7 */ unsigned long timestamp; /**//* bytes 8-11 */ unsigned long ssrc; /**//* stream number is used here. */ } RTP_HEADER_TS;
这里的socket中的send函数里面的port和ip是通过前面的rtsp setup阶段确定,并通过设置接口add_dest_socket_info传递到这里,如下:
RTP_MP3_SOCKET_INFO* psocketinfo = new RTP_MP3_SOCKET_INFO; psocketinfo->rtp_socket_port = destport; psocketinfo->rtp_socket_ip = destip; m_RTP_SOCKET_INFO_MAP[psessionhandle] = psocketinfo; m_socketinfo_update_flag = true;
于是,发送的时候,直接获取就ok了,如下:
RTP_SOCKET_INFO_List_MP3::iterator iter = m_RTP_SOCKET_INFO_List.begin(); for ( ; iter != m_RTP_SOCKET_INFO_List.end(); iter++ ) { RTP_MP3_SOCKET_INFO& mp3socketinfo = ( *iter ); struct sockaddr_in destsockaddr; destsockaddr.sin_family = AF_INET; destsockaddr.sin_addr.s_addr = inet_addr( mp3socketinfo.rtp_socket_ip.c_str() ); destsockaddr.sin_port = htons( mp3socketinfo.rtp_socket_port ); int ret = sendto( socket, sendbuf, totalsendcountbytes, 0, ( struct sockaddr* )&destsockaddr, sizeof( destsockaddr ) ); if ( ret < 0 ) { printf( "\n mp3 frame send error ! \n " ); } }
当然上面是mp3的例子,ts流也可以借鉴这个方式来同样构造一套,用来发送就ok了。
我们通过后面采集的数据,写入到buffer数据里面,然后此时发送线程,就需要从buffer里面来读取数据了,
参考这里的文章http://www.codeforge.com/read/96791/RtpPacket.cpp__html
可以发现,需要构造rtp packet将前面的header填充,于是这样写:
//1.构造rtp报文 rtp_hdr = ( RTP_HEADER_TS * )&rtpbuf[0];//存的数据 rtp_hdr->payload = TS_PAYLOAD_TYPE; //负载类型号, rtp_hdr->version = 2; //版本号,此版本固定为2 rtp_hdr->ssrc = htonl( tsssrc ); rtp_hdr->marker = 0;
于是接下来就需要将前面的buffer数据发送到client端了,
可以简单的见下面的逻辑,如下:
//传入的ts buffer的长度 unsigned int framelen = pbuffer->length ;
// 每一个RTP包中都有前12个字节定长的头字段 int rtpheadersize = 12;
对于buffer的长度小于len的情况,采取一个rtp包发送就ok了,序列号需要递增,和ping一样,用来标志一个包,
然后直接通过rtp的send函数发送,这里的socket结构体没有耦合好,建议最好将这里的send函数直接用base 库里面
的sokcet类的继承类udp来实现,这里就比较合理!
if(MTU >= framelen){ //小于mtu的情况 //设置rtp M位 rtp_hdr->marker = 0; rtp_hdr->seq_no = htons((*seq_num)++); //序列号,每发送一个RTP包增1 memcpy(rtpbuf + rtpheadersize, pbuffer->buffer, framelen); int bytes = 0; bytes = framelen + rtpheadersize; sendPacketToClients(socket, rtpbuf, bytes); }
那么对于buffer的长度大于len的情况,需要分段发送了,
int k = 0, l = 0; k = framelen / MTU; //需要k个MTU字节的RTP包 l = framelen % MTU; //最后一个RTP包需要装载的字节数
首先统计多少个mtu的个数,和最后的个数,很容易算出来的,
当然如果是mtu的整数倍的话,需要单独拿出来,作为一个特例存在,
if(0 == l){ k = k -1; //k大于等于1 l = MTU; }
于是可以进行while循环操作了,count用来计数,
如下:
int count =0; //用于指示当前发送的是第几个分片RTP包
接下来,处理的步骤如下:前面k个mtu包发送,最后一个单独发送,于是可以写成下面的代码:
while(t <= k){ rtp_hdr->seq_no = htons((*seq_num)++); //序列号,每发送一个RTP包增1 if(t < k){ //发送一个需要分片的buffer的第一个分片 memset(rtpbuf + rtpheadersize, 0, 1500 - rtpheadersize); memcpy(rtpbuf + rtpheadersize, pbuffer->buffer + t * MTU, MTU); int bytes = 0; bytes = MTU + rtpheadersize; sendPacketToClients(socket, rtpbuf, bytes); }else if(t == k){ memset(sendbuf + rtpheadersize, 0, 1500 - rtpheadersize); memcpy(sendbuf + rtpheadersize, pbuffer->buffer + t * MTU, l); int bytes = 0; bytes = l + rtpheadersize; sendPacketToClients(socket, rtpbuf, bytes); } t++; }
相关文章推荐
- 分析一个通用的rtsp server实现过程串联模块
- 分析一个通用的rtsp server实现过程基础模块
- 分析一个通用的rtsp server实现采集模块
- 分析一个通用的rtsp server实现过程总纲
- 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为(转)
- 一个通用的分页存储过程实现-SqlServer(附上sql源码,一键执行即刻搭建运行环境)
- 使用WIN32汇编语言实现一个基本windows窗体的过程分析
- 基于Nginx实现一个自己的HTTP模块--发送磁盘中的文件
- 一个简单的hibernate java project实现过程(MS Sql Server)
- go: 一个通用log模块的实现
- 一个分析“文件夹”选择框实现方法的过程
- 一个RtspServer的设计与实现和RTSP2.0简介
- 一个RtspServer的设计与实现和RTSP2.0简介
- Sql server Insert执行的秘密(上)一个最简单的INSERT分析 执行过程
- OpenStack建立实例完整过程源码详细分析(13)----依据AMQP通信架构实现消息发送机制解析之二
- go: 一个通用log模块的实现
- 使用WIN32汇编语言实现一个基本windows窗口的过程分析
- tomcat架构分析(connector BIO 实现)——serverSocket即在connector模块创建
- 一个分析“文件夹”选择框实现方法的过程
- OpenStack建立实例完整过程源码详细分析(12)----依据AMQP通信架构实现消息发送机制解析之一