位于 live555 项目 mediaServer 目录下的是 “LIVE555 Media Server”,它是一个完整的 RTSP 服务器应用程序。它可以把多种媒体文件转为流,提供给请求者。
这里来看一下 “LIVE555 Media Server” 的实现。抛开其中向终端输出应用程序信息的代码, “LIVE555 Media Server” 主程序的代码像下面这样:
“LIVE555 Media Server” 的主程序非常简单,做的事情仅仅如下面这样:
- 创建
TaskScheduler
用于执行任务调度。关于TaskScheduler
的更详细信息可以参考 live555 源码分析:基础设施。 - 创建
UsageEnvironment
用于日志输出等 I/O 操作。关于UsageEnvironment
的更详细信息可以参考 live555 源码分析:基础设施。 - 创建
RTSPServer
用于接受连接、处理请求等。这里会首先尝试使用 554 端口,如果失败,就尝试使用 8554 端口,如果两个端口都没法用,就失败退出程序。 - 为
RTSPServer
设置 HTTP 隧道端口。这里会依次尝试使用 80、8000 和 8080 端口。 - 执行事件循环。
TaskScheduler
用于执行任务调度,但要监听的 socket,以及 socket 上的 I/O 事件的处理程序,则需要 RTSPServer
,也就是 DynamicRTSPServer
提供。DynamicRTSPServer
是 “LIVE555 Media Server” 应用程序的核心,其定义如下:
DynamicRTSPServer
的类层次结构如下图:
要监听的 socket 以及 socket 上的 I/O 事件的处理程序是在 DynamicRTSPServer
创建的过程中注册给 TaskScheduler
的。DynamicRTSPServer
需要通过其静态函数 createNew()
创建,该函数定义如下:
在 DynamicRTSPServer
的 createNew()
中,首先创建一个 socket,然后用这个 socket 创建 DynamicRTSPServer
对象。构造函数顺着类继承层次一级级执行上去。
RTSPServerSupportingHTTPStreaming
构造函数定义如下:
RTSPServer
构造函数定义如下:
GenericMediaServer
构造函数定义如下:
在 GenericMediaServer
的构造函数中,要监听的 Server socket 及该 socket 上的 I/O 事件处理程序,被注册给任务调度器。事件循环中检测到 socket 上出现 I/O 事件时,该处理程序会被调用到。注册的事件处理程序为 GenericMediaServer::incomingConnectionHandler()
。
Medium
构造函数定义如下:
在 Medium
中,主要通过 MediaLookupTable
维护一个 medium name 到 Medium
对象的映射表。MediaLookupTable
可以看作是 BasicHashTable
对值类型为 Medium
对象指针的特化,该类定义如下:
MediaLookupTable
的引用实际由 UsageEnvironment
持有:
medium name 在 Medium
构造过程中分配,创建的 Medium
也在此时被加入 MediaLookupTable
中:
Server socket 的创建及连接建立
在 DynamicRTSPServer
的 createNew()
,创建 DynamicRTSPServer
对象之前,会先通过 setUpOurSocket()
创建一个 socket,setUpOurSocket()
是 GenericMediaServer
的一个静态函数,其定义为:
GenericMediaServer::setUpOurSocket()
主要即是,通过 setupStreamSocket()
函数创建一个 TCP socket,增大该 socket 的发送缓冲区,并 listen 该 socket。
setupStreamSocket()
函数在 groupsock 模块中定义:
在这里基本上就是创建 TCP socket,为 socket 设置 RESUE 选项,将 socket 绑定到目标端口,并根据需要设置 socket 为非阻塞的。
接着来看,在 live555 中是如何启动监听 server socket 上的 I/O 事件,并处理这些事件的。
我们看到,在 GenericMediaServer
的构造函数中,用于接收客户端发起的连接的 server socket,及该 socket 上的 I/O 事件处理程序,被注册给任务调度器。当有客户端发起了到 “LIVE555 Media Server” 的连接时,该处理程序会被调用到。事件处理程序为 GenericMediaServer::incomingConnectionHandler()
,相关的几个函数声明为:
GenericMediaServer::incomingConnectionHandler()
为静态函数,incomingConnectionHandler()
及 incomingConnectionHandlerOnSocket(int serverSocket)
为非虚函数,它们的定义为:
可以看到,当监听的 server socket 上发现有客户端发起的连接请求时,处理过程大体为:
- 通过
accept()
获得新建立的连接的 socket。 设置新 socket 的选项,使它称为非阻塞 socket,并增大该 socket 的发送缓冲区。可以看一下在 live555 中设置 socket 选项的这些函数,它们在 groupsock 模块中定义:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051Boolean makeSocketNonBlocking(int sock) {. . . . . .int curFlags = fcntl(sock, F_GETFL, 0);return fcntl(sock, F_SETFL, curFlags|O_NONBLOCK) >= 0;}. . . . . .void ignoreSigPipeOnSocket(int socketNum) {. . . . . .signal(SIGPIPE, SIG_IGN);}static unsigned getBufferSize(UsageEnvironment& env, int bufOptName,int socket) {unsigned curSize;SOCKLEN_T sizeSize = sizeof curSize;if (getsockopt(socket, SOL_SOCKET, bufOptName,(char*)&curSize, &sizeSize) < 0) {socketErr(env, "getBufferSize() error: ");return 0;}return curSize;}. . . . . .static unsigned increaseBufferTo(UsageEnvironment& env, int bufOptName,int socket, unsigned requestedSize) {// First, get the current buffer size. If it's already at least// as big as what we're requesting, do nothing.unsigned curSize = getBufferSize(env, bufOptName, socket);// Next, try to increase the buffer to the requested size,// or to some smaller size, if that's not possible:while (requestedSize > curSize) {SOCKLEN_T sizeSize = sizeof requestedSize;if (setsockopt(socket, SOL_SOCKET, bufOptName,(char*)&requestedSize, sizeSize) >= 0) {// successreturn requestedSize;}requestedSize = (requestedSize+curSize)/2;}return getBufferSize(env, bufOptName, socket);}unsigned increaseSendBufferTo(UsageEnvironment& env,int socket, unsigned requestedSize) {return increaseBufferTo(env, SO_SNDBUF, socket, requestedSize);}通过
createNewClientConnection()
,创建一个新的客户端连接ClientConnection
。createNewClientConnection()
是一个纯虚函数,它的实现将在类继承层次中实现了该函数的类中层次最低的那个类里,对于DynamicRTSPServer
->RTSPServerSupportingHTTPStreaming
->RTSPServer
->GenericMediaServer
这个继承层次,从DynamicRTSPServer
类开始,逐级向上找,可以发现createNewClientConnection()
函数的实现在RTSPServerSupportingHTTPStreaming
类中。
RTSPServerSupportingHTTPStreaming
类的
createNewClientConnection()
函数定义如下:
这里简单地创建一个 RTSPClientConnectionSupportingHTTPStreaming
类对象。在 live555 中,用 GenericMediaServer
的内部类 GenericMediaServer::ClientConnection
表示一个客户端连接。对于 “LIVE555 Media Server” 而言,这个类的继承层次体系结构如下图所示:
RTSPClientConnectionSupportingHTTPStreaming
类对象构造函数调用其父类 RTSPServer::RTSPClientConnection
的构造函数,后者的实现为:
RTSPServer::RTSPClientConnection
的构造函数继续调用其父类 GenericMediaServer::ClientConnection
的构造函数:
在 live555 中,GenericMediaServer
用于执行通用的媒体服务器相关的管理,包括管理所有的客户端连接。GenericMediaServer::ClientConnection
的构造函数中会将其自身加入 GenericMediaServer
的哈希表 fClientConnections
中。更重要的是,将客户端连接的 socket 及该 socket 上的 I/O 事件处理程序注册给了任务调度器,其中 I/O 事件处理程序为 GenericMediaServer::ClientConnection::incomingRequestHandler()
函数。
也就是说,”LIVE555 Media Server” 中与客户端的交互将由 GenericMediaServer::ClientConnection::incomingRequestHandler()
函数完成,这个函数定义如下:
在 GenericMediaServer::ClientConnection
的 incomingRequestHandler()
中,会从客户端连接的 socket 中读取数据,然后调用 handleRequestBytes()
函数做进一步的处理。
读取数据的动作由 groupsock 模块中的 readSocket()
完成:
这里通过 recvfrom()
函数从 socket 中读取数据。
GenericMediaServer::ClientConnection
中,处理客户端请求的这几个函数声明如下:
handleRequestBytes()
为纯虚函数,需要由子类实现。对于 RTSPServerSupportingHTTPStreaming::RTSPClientConnectionSupportingHTTPStreaming
-> RTSPServer::RTSPClientConnection
-> GenericMediaServer::ClientConnection
这个继承层次体系,该函数的实现实际上位于 RTSPServer::RTSPClientConnection
中。
从客户端接收到的请求,将由 RTSPServer::RTSPClientConnection::handleRequestBytes()
处理。
打赏
Done.
live555 源码分析系列文章
live555 源码分析:简介
live555 源码分析:基础设施
live555 源码分析:MediaSever
Wireshark 抓包分析 RTSP/RTP/RTCP 基本工作过程
live555 源码分析:RTSPServer
live555 源码分析:DESCRIBE 的处理
live555 源码分析:SETUP 的处理
live555 源码分析:PLAY 的处理
live555 源码分析:RTSPServer 组件结构
live555 源码分析:ServerMediaSession
live555 源码分析:子会话 SDP 行生成
live555 源码分析:子会话 SETUP
live555 源码分析:播放启动