1,RTSP连接的建立过程RTSPServer类用于构建一个RTSP服务器,该类同时在其内部定义了一个RTSPClientSession类,用于处理单独的客户会话。首先创建RTSP服务器(具体实现类是DynamicRTSPServer),在创建过程中,先建立Socket(ourSocket)在TCP的554端口进行监听,然后把连接处理函数句柄

(RTSPServer:: incomingConnectionHandler)和socket句柄传给任务调度器(taskScheduler)。

任务调度器把socket句柄放入后面select调用中用到的socket句柄集(fReadSet)中,同时将socket句柄和incomingConnectionHandler句柄关联起来。接着,主程序开始进入任务调度器的主循环(doEventLoop),在主循环中调用系统函数select阻塞,等待网络连接。当RTSP客户端输入(rtsp://192.168.1.109/1.mpg)连接服务器时,select返回对应的scoket,进而根据前面保存的对应关系,可找到对应处理函数句柄,这里就是前面提到的incomingConnectionHandler了。在incomingConnectionHandler中创建了RTSPClientSession,开始对这个客户端的会话进行处理。

具体分析如下:

DynamicRTSPServer::creatnew():1.调用继承自RTPSever::setUpOurSocket:1.调用GroupsockHelper 的setupStreamSocket创建一个socket连接,并绑定,2.设置socket的发送缓存大小,3.调用listen开始监听端口,设置同时最大能处理连接数LISTEN_BACKLOG_SIZE=20,如果达到这个上限则client端将收到ECONNERREFUSED的错误4.测试绑定端口是否为0,为0的话重新绑定断口,并返回系统自己选择的新的端口。5.返回建立的socket文件描述符

2.调用自己和RTPSever的构造函数:RTPSever构造函数:

1.用一个UsageEnvironment对象的引用构造其父类Medium

2.设置最大等待回收连接时间reclamationTestSeconds,超过这个时间从客户端没有RTSP命令或者RTSP的RR包则收回其RTSPClientSession

3.建立一个HashTable(实际上是一个BasicHashTable),fServerMediaSessions指向这个表。4.调用参数UsageEnvironment对象env的成员,一个TaskScheduler指针所指对象(实际就是一个BasicTaskScheduler对象)的成员函数turnOnBackgroundReadHandling():1.调用一个HandlerSet::assignHandler()创建一个Handler,把socketNum【此处为服务器监听的socket描述符】和处理函数RTSPServer::incomingConnectionHandler(),还有指向RTSPSever的指针绑定在一起。incomingConnectionHandler作用:1.调用accept返回服务器与客户端连接的socket描述符2.设置客户端描述符为非阻塞3.增加客户端socket描述符的发送缓存为50*1024

4.为此客户端随机分配一个sessionId

5.用客户端socket描述符clientSocket,sessionId,和客户端地址clientAddr调用creatNewClientSession创建一个clientSession。

2,请求消息处理过程上节我们谈到RTSP服务器收到客户端的连接请求,建立了RTSPClientSession类,处理单独的客户会话。在建立 RTSPClientSession的过程中,将新建立的socket句柄(clientSocket)和RTSP请求处理函数句柄RTSPClientSession::incomingRequestHandler传给任务调度器,由任务调度器对两者进行一对一关联。当客户端发出 RTSP请求后,服务器主循环中的select调用返回,根据socket句柄找到对应的incomingRequestHandler,开始消息处理。先进行消息的解析。

RTSPClientSession::RTSPClientSession()构造函数:1.重置请求缓存

2.调用envir().taskScheduler().turnOnBackgroundReadHandling(),这次socketnumber为客户端socket描述符这次的处理函数是RTSPServer::RTSPClientSession::incomingRequestHandler()

RTSPServer::RTSPClientSession::incomingRequestHandler():调用handleAlternativeRequestByte1(uint8_t requestByte):1.fRequestBuffer[fRequestBytesAlreadySeen] =requestByte;把请求字符放入请求缓存fRequestBuffer

2.调用handleRequestBytes(1) 处理请求缓存handleRequestBytes(int newBytesRead):1.调用noteLiveness()查看请求是否到期,如果服务器的reclamationTestSeconds> 0,调用taskScheduler对象的rescheduleDelayedTask函数: 参数为

( fLivenessCheckTask,fOurServer.fReclamationTestSeconds*1000000,(TaskFunc*)livenessTimeoutTask, this )

其中livenessTimeoutTask()函数作用是删除new出来的clientSession.

1.调用unscheduleDelayedTask(TaskToken&prevTask):从DelayQueue中删除prevTask项, prevTask置空.

2.调用scheduleDelayedTask(int64_t microseconds,

TaskFunc* proc, void*clientData):

1.创建一个DelayInterval对象timeToDelay,用microseconds初始化。

2.创建一个AlarmHandler对象,用proc, clientData, timeToDelay初始化

3.调用fDelayQueue.addEntry(),把这个AlarmHandler对象加入到延迟队列中

4.返回AlarmHandler对象的token[long类型]的指针

2.如果请求的的长度超过请求缓存可读长度fRequestBufferBytesLeft,结束这个连接。

3.找到请求消息的结尾:。

4.如果找到消息结尾,调用RTSPServer::RTSPClientSession::handleRequestBytes()[值得关注此函数]把请求字符串转换成命令各部分包括:cmdName[方法],urlPreSuffix[url地址],urlSuffix[要读取的文件名],sceq[消息的Cseq],否则函数返回需要继续从连接中读取请求。分别存入对应的数组。

5.如果转换成功,调用handleCmd_xxx()处理对应的cmdName: xxx[此处实现了:OPTIONS,DESCRIBE,SETUP,TEARDOWN,PLAY,PAUSE,GET_PARAMETER,SET_PARAMETER]

其中PLAY,PAUSE,GET_PARAMETER,SET_PARAMETER调用handleCmd_withinSession (cmdName,urlPreSuffix, urlSuffix,cseq,(char const*)fRequestBuffer);

6.清空 RequestBuffer

比如:消息解析后,如果发现客户端的请求是DESCRIBE则进入handleCmd_DESCRIBE函数。RTSP服务器收到客户端的DESCRIBE请求后,根据请求URL(rtsp://192.168.1.109/1.mpg),找到对应的流媒体资源,返回响应消息。live555中的ServerMediaSession类用来处理会话中描述,它包含多个(音频或视频)的子会话描述(ServerMediaSubsession)。根据客户端请求URL的后缀(例如是1.mpg), 调用成员函数                  DynamicRTSPServer::lookupServerMediaSession查找对应的流媒体信息 ServerMediaSession。(根据urlSuffix查找)。

如果ServerMediaSession不存在,查找文件是否存在,若文件不存在,则判断ServerMediaSession         (即smsExists)是否存在,如果存在则将其remove(调用removeServerMediaSession方法)。但是如果本地存在1.mpg文件,则根据文件名创建一个新的 ServerMediaSession(调用createNewSMS方法,若在该方法中找不到对应的文件扩展名,则将返回NULL)。

如果通过lookupServerMediaSession返回的是NULL,则向客户端发送响应消息并将fSessionIsActive置为FALSE;否则,为该session组装一个SDP描述信息(调用generateSDPDescription方法,该方法在ServerMediaSession类中),组装完成后,生成一个RTSP URL(调用rtspURL方法,该方法在RTSPServer类中)。

在创建ServerMediaSession过程中,根据文件后缀.mpg,创建媒体MPEG-1or2的解复用器                   (MPEG1or2FileServerDemux)。再由MPEG1or2FileServerDemux创建一个子会话描述 MPEG1or2DemuxedServerMediaSubsession。最后由ServerMediaSession完成组装响应消息中的SDP信息(SDP组装过程见下面的描述),然后将响应消息发给客户端,完成一次消息交互。

===================================================================================================================================

RTSP服务器处理客户端点播的基本流程

处理连接请求的基本流程:

l  Step 1:与客户端建立RTSP连接(调用incomingConnectionHandler方法),创建ClientSession并关联fClientSocket与incomingRequestHandler(调用incomingConnectionHandler1)。

l  Step 2:接收客户端请求(调用incomingRequestHandler方法)。

l  Step 3:从客户端Socket读取数据,并对请求数据(即the request string)进行转换(调用parseRTSPRequestString方法,该方法在RTSPCommon类中)。

l  Step 4:根据分离出来的指令进行分别处理:

n  OPTIONS→handleCmd_OPTIONS

n  DESCRIBE→handleCmd_DESCRIBE

handleCmd_DESCRIBE这一个方法比较重要,首先根据urlSuffix查找ServerMediaSession是否存在(调用lookupServerMediaSession方法,该方法中通过HashTable来查找)。

在testOnDemandRTSPServer项目工程中,仅仅是通过streamName来确认session是否为NULL。而在完整的live555MediaServer项目工程中,则是通过DynamicRTSPServer类来处理,其首先是查找文件是否存在,若文件不存在,则判断ServerMediaSession(即smsExists)是否存在,如果存在则将其remove(调用removeServerMediaSession方法);若文件存在,则根据文件名创建一个ServerMediaSession(调用createNewSMS方法,若在该方法中找不到对应的文件扩展名,则将返回NULL)。

如果通过lookupServerMediaSession返回的是NULL,则向客户端发送响应消息并将fSessionIsActive置为FALSE;否则,为该session组装一个SDP描述信息(调用generateSDPDescription方法,该方法在ServerMediaSession类中),组装完成后,生成一个RTSP URL(调用rtspURL方法,该方法在RTSPServer类中)。

n  SETUP→handleCmd_SETUP

handleCmd_SETUP方法中,有两个关键的名词,一个是urlPreSuffix,代表了session name(即stream name);一个是urlSuffix,代表了subsession name(即track name),后面经常用到的streamName和trackId分别与这两个名词有关。

接下来会创建session's state,包括incrementReferenceCount等。紧接着,会针对确定的subsession(track)查找相应的信息。接着,在request string查找一个"Transport:" header,目的是为了从中提取客户端请求的一些参数(调用parseTransportHeader方法,该方法在RTSPServer类中),如clientsDestinationAddressStr、ClientRTPPortNum等。

再接着是getStreamParameters(该方法在ServerMediaSession类中被定义为纯虚函数并在OnDemandServerMediaSubsession类中被重定义),然后通过fIsMulticast和streamingMode来组装不同的响应消息。

n  PLAY→handleCmd_PLAY:处理播放请求,具体的实现流程请参见后面的步骤。

n  PAUSE→handleCmd_PAUSE:处理暂停请求,在执行了该请求后,最终会调用StopPlaying方法,并将fAreCurrentlyPlaying置为FALSE。

n  TEARDOWN→handleCmd_TEARDOWN:处理停止请求,将fSessionIsActive置为FALSE。

n  GET_PARAMETER→handleCmd_GET_PARAMETER:该方法主要是维持客户端与服务器通信的生存状态,just for keep alive。

n  SET_PARAMETER→handleCmd_SET_PARAMETER:该方法未针对SET_PARAMETER作实现,使用该方法会调用handleCmd_notSupported方法,并将最终引发与客户端断开连接。

l  Step 5:根据Step 4的不同指令进行消息响应(调用send方法),该消息响应是即时的。

l  Step 6:处理客户端发送“SETUP”指令后即开始播放的特殊情况。

l  Step 7:将RequestBuffer进行重置,以便于为之后到来的请求做好准备。

l  Step 8:检查fSessionIsActive是否为FALSE,如果是则删除当前的ClientSession。

处理PLAY的基本流程:

l  Step 1:对rtspURL及相关header的处理,涉及较多的细节。

l  Step 2:根据不同的header对流进行缩放比例或定位的处理。

如果为sawScaleHeader,则进行缩放比例的处理(调用setStreamScale方法,该方法在OnDemandServerMediaSubsession类中实现)。

如果为sawRangeHeader,则进行寻找流的处理(即是对流进行定位,调用seekStream方法,该方法在OnDemandServerMediaSubsession类中实现;同时,该方法的调用是在初始播放前及播放过程中由于用户拖动播放进度条而产生的系列请求)。

在OnDemandServerMediaSubsession类中,seekStream方法中调用了seekStreamSource方法,该方法在对应的媒体格式文件的FileServerMediaSubsession类中实现(如针对WAV格式,则在WAVAudioFileServerMediaSubsession类中实现;针对MP3格式,则在MP3AudioFileServerMediaSubsession类中实现)。

同理,OnDemandServerMediaSubsession类中的setStreamScale方法中所调用的setStreamSourceScale方法亦是类似的实现机制。

l  Step 3:开始进行流式播放(调用startStream方法,该方法在OnDemandServerMediaSubsession类中实现)。

n  Step 3.1:根据clientSessionId从fDestinationsHashTable中查找到destinations(包括了客户端的IP地址、RTP端口号、RTCP端口号等信息)。

n  Step 3.2:调用startPlaying方法,在该方法中根据RTPSink或UDPSink分别调用startPlaying方法。

如果是调用RTPSink的startPlaying方法,则接着会调用MediaSink类中的startPlaying方法,并在该方法中调用MultiFramedRTPSink类中的continuePlaying方法,之后便是buildAndSendPacket了。这里已经来到重点了,即是关于不断读取Frame并Send的要点。在MultiFramedRTPSink类中,通过buildAndSendPacket、packFrame、afterGettingFrame、afterGettingFrame1、sendPacketIfNecessary和sendNext构成了一个循环圈,数据包的读取和发送在这里循环进行着。特别注意的是sendPacketIfNecessary方法中的后面代码(nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, (TaskFunc*)sendNext, this);),通过Delay amount of time后,继续下一个Task,并回过来继续调用buildAndSendPacket方法。

在packFrame方法中,正常情况下,需要调用getNextFrame方法(该方法在FramedSource类中,并且对不同媒体格式的Frame的获取出现在FramedSource类的getNextFrame方法中,通过调用doGetNextFrame方法来实现)来获取新的Frame。

如果是调用UDPSink的startPlaying方法,则接着会调用MediaSink类中的startPlaying方法,并在该方法中调用BasicUDPSink类中的continuePlaying方法。在这之后由若干个方法构成了一个循环圈:continuePlaying1、afterGettingFrame、afterGettingFrame1、sendNext。并在afterGettingFrame1方法中实现了packet的发送(fGS->output(envir(), fGS->ttl(),fOutputBuffer, frameSize);)。

Step 3.3:针对RTPSink创建RTCP instance(RTP与RTCP的配套使用决定了其必须这么做,否则可能就跟直接使用UDP发送数据包没什么两样了^_^),创建RTCP instance时,将incomingReportHandler句柄作为BackgroundHandlerProc,以便于处理RTCP的报告,并开始startNetworkReading。这里RTP/RTCP的使用方式有两种,一种建立在TCP之上,一种建立在UDP之上。

rtsp连接断开_live555_RTSP连接建立以及请求消息处理过程相关推荐

  1. rtsp连接断开_live555学习之RTSP连接建立以及请求消息处理过程

    live555 学习之 RTSP 连接建立以及请求消息处理过程 1 , RTSP 连接的建立过程 RTSPServer 类用于构建一个 RTSP 服务器,该类同时在 其内部定义了一个 RTSPClie ...

  2. rtsp连接断开_RTSP详解

    关于 RTSP. RTSP协议是一个非常类似HTTP协议的流控制协议.它们都使用纯文本来发送信息,而且rtsp协议的语法也和HTTP类似.Rtsp一开始这样设计,也是为了能够兼容使用以前写的HTTP协 ...

  3. rtsp连接断开_OpenCV:重新连接断开连接的摄像头的代码工作正常,但前端捕获的视频帧没有加载...

    我编写了这个简单的python代码来重新连接系统所连接的IP摄像机,以防摄像头断开连接.在import numpy as np import cv2 import time def work_with ...

  4. redis 普通客户端请求超过client-output-buffer-limit限制,导致主从连接断开的问题

    超过 client-output-buffer-limit 限制,导致主从连接断开的问题 一.问题 二.查看资料 临时解决 三.对比分析 一.问题 今天收到报警,两个节点之间连接断了,登录节点查看信息 ...

  5. 【Java 网络编程】TCP 连接 断开 机制 ( 三次握手 | 四次挥手 )

    文章目录 I TCP 连接建立流程 ( 三次握手 ) II SYN 和 ACK 中的随机值 III TCP 连接建关闭流程 ( 四次挥手 ) IV TCP 连接断开的保证 V 四次挥手的必要性 I T ...

  6. 【TCP专题】TCP连接断开

    当TCP的连接建立完成后,我们就可以尽情的通过TCP连接所创立的会话来进行数据的传输了.当然,再有意思的话题也有说完的时候,所以,当数据传输完之后,TCP该如何善后呢? TCP的四次挥手 TCP的连接 ...

  7. TCP连接断开原理剖析

    TCP连接三次握手 TCP协议在双方建立连接的时候需要三次握手, 所谓的三次握手即TCP连接的建立.这个连接必须是一方主动打开,另一方被动打开的.以下为客户端主动发起连接的图解: 其中比较重要的字段有 ...

  8. 使用计算机断开终端连接,“由于终端连接目前正在忙于处理一个连接断开连接复位或删除操作...

    "由于终端连接目前正在忙于处理一个连接断开连接复位或删除操作 来源:未知 作者:老黑 时间:09-11-24 [打印] "由于终端连接目前正在忙于处理一个连接断开连接复位或删除操作 ...

  9. OkHttp3的连接池及连接建立过程分析

    如我们前面在 OkHttp3 HTTP请求执行流程分析 中的分析,OkHttp3通过Interceptor链来执行HTTP请求,整体的执行过程大体如下: OkHttp Flow 这些Intercept ...

最新文章

  1. 心路历程:「双非」研究生数据分析春招
  2. mysql手动写的时间_Mysql日期和时间函数
  3. redis module模块简单使用
  4. 优化数据库的思想及SQL语句优化的原则
  5. vue element-ui只有一条信息时默认选中按钮,且不能取消,多条信息时可以手动选择
  6. 数据结构-阶段性理解
  7. 车牌识别sdk android,Android车牌识别sdk
  8. ruby入门教程:入门ruby的方法
  9. 这是三篇影响百度17年的技术博客,作者李彦宏
  10. Android Studio 获取Shal值
  11. 银行服务器linux系统,银行操作系统
  12. 前端工作中碰到的一些小问题总结
  13. centos xfs硬盘扩容
  14. 电脑删除快捷键还有哪些?学会了让工作效率更提升
  15. H3C与阿里云网关建立IPSEC并启用BGP
  16. nsga 的java实现_Java – Scala遗传算法(GA)库中的模拟二进制交叉(SBX)交叉运算符
  17. nginx根据设备类型进行url重定向
  18. WEKA( OneR,过拟合)
  19. 什么是多因子量化选股模型?
  20. HBuilderX网站打包APP

热门文章

  1. 图片维度不匹配_内容审核基础:审核方式、流程与审核维度
  2. mybatis--properties以及别名定义(四)
  3. androidwear的主要UI空间有哪些
  4. matlab 多项式表达,Matlab多项式运算
  5. 阿里云 mysql 表锁死_解决阿里云数据库MySQL实例空间满自动锁问题
  6. python简单实践作业_【Python】:简单爬虫作业
  7. mysql数据库innodb恢复命令_MySQL 如何对InnoDB使用Undrop来恢复InnoDB数据
  8. 久其报表大厅_天下苦“数”久矣,大数据分析平台解决物流数据孤岛
  9. linux4安装gcc,当ubuntu已有gcc4时安装gcc3
  10. Elasticseach 从零开始学习记录(三) - 分布式部署