Live555MediaServer
1、初始化
BasicTaskschedular
BasicUsageEnvironment
RTSPServer
|--------new RTSPServer
|------setupOurSocket创建监听客户端连接用的socket
|------turnOnBackgroundhandling(socket)将监听socket加入计划任务,等待连接
2、接受客户端连接 incomingConnectionHandlerRTSP
在RTSPServer将监听socket加入计划任务后,调度机制会不断查询,如果收到连接请求,就会调用该回掉函数
(Tips:在Live555中监听任务的执行都是通过相应的静态全局回掉函数,在回掉函数内部再强制转换到相应的类函数)
在incomingConnectionHandlerRTSP中会clientSocket = accept()得到连接的socket、地址,设置参数等
之后创建一个客户连接管理createNewClientConnection()
|----new RTSPClientConnection
|---setBackgroundHandling将连接的socket加入计划任务
(Tips:turnonBackgroundHandling只让socket可读,内部会调用setBackgroundHandling进行设置,如需其他属性,如可写、异常等都需要通过setBackgroundHandling设置)
3、响应请求过程
3.1 一般过程
incomingRequestHandler
|---readSocket() 读取数据
|---handleRequestBytes() 解析数据并做相应的处理
|---parseRTSPRequestString() 解析请求
|---handleCmd_XXX() 根据不同的RTSP协议命令去处理
|---send(fResponseBuffer) 构造好回应字段后,发送回应
3.2具体过程
3.2.1 handleCmd_DESCRIBE
handleCmd_DESCRIBE
|---urlTotalSuffix 提取streamName
|---authenticationOK 验证用户,这里只保留了接口,未进行实现
|---fOurServer.lookupServerMediaSession(streamName)
这个用来获取ServerMediaSession,一般不同类型的服务器会有不同的实现策略,如Live555MediaServer只用来流化本地文件,所以有了继承类DynamicRTSPServer,重写lookupServerMediaSession方法,本来找不到session实例会返回NULL,而在这里将查找当前目录下对应的本地文件,然后去创建相应的session,这也就是为什么要将文件与程序放在同一目录下的原因。
这里的ServerMediaSession在创建时会同时添加ServerMediasubsession,因为是服务器,所以一定知道自己应该创建些什么subsession
|---sdpDescription =session->generateSDPDescription() 获取SDP描述信息
|---snprintf() 组建回复字符串
3.2.1.1 generateSDPDescription过程
generateSDPDescription
|---foreach subsession sdpLines =subsession->sdpLines() 获取每个subsessiond的sdp描述
|---sdpLines() 过程
|---onDemandServerMediasubsession 为点播式流媒体服务创建的中间继承类,Live555Mediasever中的subsession都继承自此类
|---FrameSource createNewStreamSource 创建一个数据源的Souce
|---RTPSink createNewRTPSink 创建RTPSink
|---setSDPLinesFromRTPSink(source, sink)从临时的source与sink中获取sdp
(Tips:createNewStreamSource与createNewRTPSink都是抽象方法,需要子类去实现,之后会从setSDPLinesFromRTPSink(source, sink)中得到sdp信息,从这里也可以看出sdp信息由RTPSink获得。Live555服务器的机制是sink从source中获取数据,创建sink的时候会将source作为参数传入,因为服务器知道创建了什么类型的subSession,所以对一般的媒体信息sink都会具备,唯一需要获取的是getAuxSDPLine(),OnDemandServerMediasubsession默认的处理方式是从RTPSink中的auxSDPLine获取,如果没有就返回NULL,对Live555MediaServer来说,传输H264文件时是无法得到sps、pps信息的,必须通过读取文件,所以在H264subsession中就选择创建Source和Sink读取一段信息后解析获得)
3.2.2 handleCmd_SETUP
handleCmd_SETUP
|---sessionID 找一个唯一的sessionID,用于标识当前的subsession
|---fourServer.createNewClientSession(sessionID)
|---clientSession.handleCmd_SETUP() 转到RTSPClientSession中去处理
|---fOurServerMediaSubsession = sms =fourSrever.lookupServerMediasession
|---创建streamState结构
|---fStreamStates = new struct streamState[fNumStreamStates]
|---foreach subSessionfstreamState[i].subsession = subsession 将subsession装进streamState结构
|---trackID,subsession->trackID 通过trackID寻找对应的subSession
|---parseTransportHeader 解析Transport参数
|---subsession->getStreamParameters 在这里将建立真正的FrameSource与RTPSink
|---FrameSource *mediaSource = createNewStreamSource()
|---rtoGroupsock = new GroupSock(serverRTPPort)
|---rtcpGroupsock = new Groupsock(serverRTPPort)
|---rtpSink = creatNewRTPSink(rtpGroupsock, mediaSource)
|---streamToken = new StreamState(rtpSink, mediaSource, rtpsock,rtcpsock)
|---fDestinationHashTable->add(sessionId, destination)
3.2.3handleCmd_PLAY
handleCmd_PLAY
|---在RTSPClientSession中,处理PLAY、PAUSE、TEARDOWN用同一个函数接口handle_CmdwithinSession,只是在里面又进行了区分
|---找到对应的subsession->startStream()这里将启动流传输数据
|---streamState.startPlaying() 开始传输
|---如果没有fRTCPInstance就创建一个RTCPInstance::createNew(fRTPSink)
|---fRTPgs->addDestination()
|---fRTCPgs->addDestination()
|---fRTPSink->startPlaying()
|---MediaSink::continuePlay() 到这里就是各个子类实现了,纯虚函数
|---MuliFramedRTPSink::continuePlaying()这里针对的是H264文件的解析
|---buildAndSendPacket()
|---准备RTP包头
|---packFrame 打包帧数据
|---分两种情况:一是上一次没打包完,还有数据;二是一个全新的帧
|---对情况二,会调用fSource->getNextFrame()里面加入的回调函数afterGettingFrame,实际是调用sink->afterGettingFrame1
|---最终会依情况打包
|---sendPacketIfNecessary()
在sendPacketIfNecessary中会调用fRTPInterface.sendPacket()发送数据,之后安排一个延时任务回调sendNext函数,在sendNext中又会调用buildAndSendPacket,从而形成一个回路来不断发送数据,知道检测到fNoFrameLeft