LIVE555研究之五:RTPServer(二)
接上文,main函数的几行代码创建了RTSPServer类的子类DynamicRTSPServer对象。RTPServer类是服务器类的基类,DynamicRTSPServer代表具体的服务器子类。我们今天介绍的服务器程序就是基于该类实现的。
在创建DynamicRTSPServer时传入了值为554的端口号。这是因为RTSP默认端口号为554,与http默认使用80端口是一样的。
DynamicRTSPServer
继承关系:
Medium是很多类的基类。内部定义了指向环境类的引用和一个char类型媒体名称。并定义了按照媒体名称,查找对应媒体的成员函数lookupByName。由于MediaSink、MediaSouce、MediaSession、RTSPClient、RTPServer均继承自该类,因此在Medium中定义了很多判断该类是哪个媒体类型的函数:
Medium中的实现均是返回false。在对应的子类中均会重定义对应函数,并返回true。
TaskToken fNextTask用来保存延迟任务的ID。保存的任务ID用于被重新调度,或者在该媒体对象被销毁时从延迟队列中取消调度。
RTPServer类是服务器类的基类,代表了服务器对象。在整个服务器运行期间,该对象一直存在。
定义了以下成员变量:
从其成员变量可以看到RTPServer中维护了ServerMediaSession对象、ClientConnection、ClientSession对象的HashTable。
ServerMediaSessionSession对应服务器端一个媒体文件,当客户端请求多个媒体文件时,RTPServer内会维护对应的多个ServerMediaSession对象。ServerMediaSession对象通过媒体文件名进行标识,如客户端请求a.264文件,则服务器就会在保存ServerMediaSession的HashTable中搜索对应文件名为a.264的ServerMediaSession。如未找到,则说明还未为该媒体文件创建对应的ServerMediaSession。并创建一个新的ServerMediaSession与媒体文件名关联后添加到HashTable。
lookupServerMediaSession用于在map中搜索对应媒体文件名对应的ServerMediaSession。
以上三个成员函数分别用来添加、查询和删除对应ServerMediaSession项。
removeServerMediaSession被调用后,在RTPServer中维护的fServerMediaSession的HashTable中,该ServerMediaSession会被删除。但是对应的ServerMediaSession对象并不一定会被释放。因为此时其他客户端还有可能在使用该媒体文件。只有当其他客户端都释放了对该媒体文件的引用后,该对象才会被释放。
closeAllClientSessionsForServerMediaSession用于删除所有客户端对某一个媒体文件的引用。
deleteServerMediaSession在从fServerMediaSession中删除对应项目时同时也会删除所有客户端的引用,此后该对象的引用计数为0可以被安全释放。
在removeServerMediaSession时会检查引用计数,只有当引用计数为0时该对象才会被释放。
ClientConnection对象
ClientConnection对象定义在RTPServer内部,为其内部类。主要用于和客户端的通信。当有新的客户端连接到服务器时,会新建ClientConnection对象。其内部定义了发送、接收socket以及发送和接收缓冲区,并对客户端的命令进行处理和回应。
void handleRequestBytes(int newBytesRead);
用于处理客户端命令,在对RTSP命令进行分析后,提取出各种信息,然后进行分流处理。
对于OPTIONS、DESCRIBE、命令不支持、命令有误等其他错误命令的响应会直接在ClientConnection中进行处理。 而对于SETUP、PLAY、PAUSE、TERARDOWN等命令会传递到ClientSession中进行处理。
以下为分流代码:
ClientSession对象会在客户端请求SETUP命令时在ClientConnection中创建,并分配一个ClientSessionID。对于SETUP之前和对一些出错处理命令会在ClientConnection中进行响应。
ClientConnection维护了RTPServer的指针,可以在新建ClientSession对象后将其加入到RTPServer维护的fClientSessions中。
ClientSession中定义的成员:
ClientSession也维护了对RTPServer的引用。同时也保存了指向ServerMediaSession的指针。在对SETUP的响应中,有这样一句话:
由此我们知道按照目前的实现,每个clientSession只能对应一个ServerMediaSession。即每个客户端只能请求一个媒体文件,不能同时请求两个媒体文件。如果需要同时支持多个媒体文件,就需要在ClientSession中维护一个ServerMediaSession集合。
ClientSession的noteLiveness用于客户端保活。其内部实现如下:
void RTSPServer::RTSPClientSession::noteLiveness()
上述代码向调度器请求重新调度一个延迟任务,在fReclamationTestSeconds后会调用livenessTimeoutTask。其实现很简单仅仅删除自身。
当服务器收到对应客户端的RR包时会调用noteLiveness,重新计时。
fReclamationTestSeconds在RTPServer构造时传入,默认为65s。表示如65s内未收到客户端RTCP包即认为客户端已断开。
如果在fReclamationTestSeconds的时间内再次调用noteLiveness,则该延迟任务会被设置成新的时间,原来的调度不再起作用。
fStreamStates指向一个动态分配的数组。fNumStreamStates表示该数组包含的元素个数。
ServerMediaSession代表一个track(媒体流)。streamToken是void*类型的指针,但它指向StreamState类的对象。StreamState对象代表一个真正流动起来的数据流。这个流从XXXXFileSouce流向RTPSink。
可以看到一个ServerMediaSubSession对应一个StreamState。但ServerMediaSubSession对应一个静态的流,可以被多个客户端重用。如:多个客户端可能会请求同一个媒体文件中的track。StreamState代表一个动态的流。
ServerMediaSession
ServerMediaSession代表服务器端一个媒体文件。
其成员如下:
可以看到其主要成员为fSubsessionsHead、fSubsessionsTail。代表该媒体文件中的多个媒体流track。fStreamName为该媒体文件名。fDescritionSDPString代表SDP字符串。用于在客户端发送DESCRIBE命令时返回给客户端。
fReferenceCount为引用计数。当将fDeleteWhenUnreferenced设置为true,且引用计数为0时,ServerMediaSession会被释放。该值在构造函数中默认赋值为false。即所有ServerMediaSession即使不存在被客户端引用时,也不会被释放。对于长时间运行的服务器程序将会出现内存消耗耗尽的情况。解决方案就是在构造时将fDeleteWhenUnreferenced的默认值赋值为true。
其他成员函数是用来操纵MediaSubSession。
MediaSubSession
如果一个媒体文件中既包含音频流又包含视频流,我们称这个媒体文件中包含两个track。每个track对应一个ServerMediaSubsession。
fParentSession指向该MediaSubSession所属的ServerMediaSession。
fNext指向下一个同属于一个ServerMediaSession的ServerMediaSubsession。如果只包含一个媒体流,则fNext指针为NULL。
fTrackNumber为track号。在客户端发送DESCRIBE命令时,服务器端会为每个媒体流分配一个TrackID。
fTrackId 为字符串指针,该字符串由”track”和fTrackNumber拼接而成。如track1、track2。
ServerMediaSubsession中仅仅定义了空的接口,具体实现均放在其子类。
OnDemandServerMediaSubsession
HashTable* fDestinationsHashTable; 存储sessionID和Destinations的映射。
Destinations为目的地址。每个ClientSession在HashTable中都有与自己对应的项。
Destinations可以维护一对RTP和RTCP的端口和地址信息。
StreamState
前面说过StreamState代表一个真正流动的流,现在让我们看下StreamState的究竟实现了什么功能。
fMaster为对OnDemandServerMediaSubsession或其子类的引用。
fReferenceCount为引用计数。
fServerRTPPort为RTP端口
fServerRTCPPort为RTCP端口
fRTPSink抽象Sink类。
fMediaSource为Souce基类。
可以看到StreamState既维护了Sink,又维护了Souce。其实在StreamState
GroupSock主要用于处理组播,但也可以处理单播。
Groupsock* fRTPgs和 Groupsock* fRTCPgs为RTP和RTCP的地址,用于向RTP和RTCP端口发送数据。
RTCPInstance
RTCPInsance是对RTCP通信的封装。RTCP的功能是统计包的收发,为流量统计提供依据。由于其封装的比较完整,因此RTCPInstance与其他类间的关系不是那么紧密。