live555 使用 RTSP/RTP/RTCP 协议来实现流媒体的传输,其中使用 RTSP 来建立流媒体会话,并对流媒体会话进行控制。在 live555 中,通过类 RTSPServerSupportingHTTPStreaming::RTSPClientConnectionSupportingHTTPStreaming
来处理 RTSP 请求。客户端发送过来的请求在其父类 GenericMediaServer::ClientConnection
的 incomingRequestHandler(void*, int /*mask*/)
函数中接收,并在其父类 RTSPServer::RTSPClientConnection
的函数 handleRequestBytes()
中处理。
RTSP 消息格式
在具体的看 GenericMediaServer::ClientConnection
的 incomingRequestHandler(void*, int /*mask*/)
函数的实现之前,我们先来看一下 RTSP 消息的格式。
RTSP 消息分为请求消息和响应消息,从客户端发向服务器的请求消息的格式如下:
请求消息的第一行是请求行,其中包含了要对资源应用的方法,资源的标识符,也就是 URL,以及使用的协议。请求行的具体格式如下:
上面格式中的 SP 表示空格,即请求行中不同元素以空格分隔,并以 \n\r
,即 CRLF 结束。RTSP 支持的 Method 主要有如下这些:
URI 的格式为:
即可以是 “*” 或一个完整的 URI,其中前者表示操作应用于所有资源。
RTSP 版本号的格式为:
即字符串 “RTSP/“ 后面加上以点号 (“.”) 分隔的两个整数。
请求行之后的是头部。头部分为三类,分别是通用头部、请求头部和实体头部。通用头部主要包含如下这些:
请求头部有如下这些:
实体头部包括这些:
每个头部也以 CRLF 结束。在所有头部之后,需要添加另外一个 CRLF 来将头部与消息体隔开。消息体最后也要添加 CRLF 作为结束。
RTSP 的请求消息结构与 HTTP 的非常类似。
OPTIONS
请求消息示例:
DESCRIBE
请求消息示例:
SETUP
请求消息示例:
PLAY
请求消息示例:
响应消息格式如下:
响应消息的第一行是状态行,由协议版本号,数字状态码,和状态码关联的文本形式说明,具体格式定义如下:
状态行之后的是头部,它与请求消息的头部类似,只是那里的请求头部替换为了响应头部。响应头部主要包括如下这些:
每个头部也以 CRLF 结束。在所有头部之后,需要添加另外一个 CRLF 来将头部与消息体隔开。消息体最后也要添加 CRLF 作为结束。
OPTIONS
响应消息示例:
DESCRIBE
响应消息示例:
在这个响应中,消息体为 SDP 消息。
SETUP
响应消息示例:
PLAY
响应消息示例:
RTSP 消息格式基本上就是这样。
RTSP 请求的处理
有了 RTSP 消息格式的认识之后,来看 live555 RTSPServer
中对于 RTSP 消息的处理函数 RTSPServer::RTSPClientConnection::handleRequestBytes()
。
这个函数的定义有点长,抛去 HTTP 的处理,RTSP 的处理逻辑如下:
这个函数会通过一个 do-while 循环逐个处理从 socket 中读取的在缓冲区存放的完整的 RTSP 消息。在循环体的开始部分,RTSPServer::RTSPClientConnection::handleRequestBytes()
先找到请求消息头部的结束位置,这通过如下这段代码来实现:
由前面 RTSP 消息格式的部分,我们知道,RTSP 请求消息头部都以连续的两个 “\n\r”,即 <CR><LF><CR><LF>
结束,寻找消息的结束位置即是找到这个序列的位置,fLastCRLF
指向这个序列的开始位置。
如果找不到请求消息头部的结束位置的话 ,也就是说从 socket 中读取的数据不包含完整的消息头部,消息有一部分数据还没有读到,此时会跳出循环结束处理。
找到了请求消息头部的结束位置之后,就是从消息头部中解析出不同的元素了。这主要通过 parseRTSPRequestString()
函数完成:
这里并不是解析出 RTSP 消息头部中所有的元素,而是仅仅解析出 RTSP 消息头部中决定后续如何处理的几个元素,包括请求的 Method,URL,CSeq 和 SessionId,消息体长度 ContentLength 等。解析成功说明消息是一个有效的 RTSP 消息,否则是一个 HTTP 消息。这里仅看解析成功情况下的处理。
解析成功之后,会先判断消息体是否完整:此时 (ptr + newBytesRead) 指向缓冲区中读取的数据的结束位置,tmpPtr
指向消息头部结束位置出的两个 “\n\r” 序列中后面的那个,即消息头部中的最后一个 <CR><LF>
,这里通过比较 (ptr + newBytesRead) 和 (tmpPtr + 2 + contentLength)
来判断缓冲区中的消息是否完整。
同样的消息不完整时,跳出循环,结束处理。
随后,在消息的处理需要已经建立流媒体会话时,则先查找建立的流媒体会话,并为该会话执行保活操作。
随后即是根据 RTSP 请求消息类型的不同,执行分别的处理。
OPTIONS 请求的处理
OPTIONS
请求的处理如下:
根据规范,OPTIONS
请求可以在流媒体会话的任何阶段发起,当请求头部中包含了 SessionId 但又找不到对应的客户端会话结构时,通过 handleCmd_sessionNotFound()
来处理:
其中 dateHeader()
返回 RTSP Date
头部,其中的时间为 RTSP 头部时间格式的当前时间字符串:
handleCmd_sessionNotFound()
的处理即是向 fResponseBuffer
填充一个 454 的 RTSP 错误响应消息。
正常情况下,通过 handleCmd_OPTIONS()
对请求进行处理:
这里是向 fResponseBuffer
填充一个成功的 RTSP 响应消息,其中的 Public
头部包含了 RTSP 服务器支持的所有 RTSP Methods。
应用于整个服务器的请求的处理
特殊的 URL “*” 表示,请求的操作应用于整个服务器。按照 RTSP 的规范,只有两种操作可以应用于整个服务器,即 GET_PARAMETER
和 SET_PARAMETER
。
应用于整个服务器 的 GET_PARAMETER
和 SET_PARAMETER
请求的处理如下:
这里分别通过调用 handleCmd_GET_PARAMETER()
和 handleCmd_SET_PARAMETER()
来处理 GET_PARAMETER
和 SET_PARAMETER
请求:
live555 liveMedia 库中的 RTSPServer::RTSPClientConnection
本身并没有实现 GET_PARAMETER
和 SET_PARAMETER
请求。如果应用程序需要提供这两个方法的实现,需要继承 RTSPServer
和 RTSPServer::RTSPClientConnection
,并实现相应的虚函数。
默认情况下,在处理 GET_PARAMETER
时,RTSPServer::RTSPClientConnection
把库的版本返回给客户端,对于 SET_PARAMETER
请求,则总是返回成功。
DESCRIBE 请求的处理
DESCRIBE
请求的处理如下:
这里通过 handleCmd_DESCRIBE()
函数处理 DESCRIBE
消息。
SETUP 请求的处理
SETUP 请求的处理如下:
如果 SETUP
请求中不带有 SessionID,即表示流媒体会话还没有创建,则这里会先尝试为请求创建流媒体会话。创建的过程如下:
首先对请求进行认证。认证需要 URL 的完整路径部分,请求头部解析之后,
urlPreSuffix
保存请求 URL 的路径部分,最后的路径分量前面的所有部分,而urlSuffix
则保存 URL 路径部分的最后一个分量。比如 URL 为rtsp://10.240.248.20:8554/raw_h264_stream.264
,urlPreSuffix
为空字符串""
,urlSuffix
为"raw_h264_stream.264"
,URL 为rtsp://10.240.248.20:8000/video/raw_h264_stream.264
,urlPreSuffix
为空字符串"video"
,urlSuffix
为"raw_h264_stream.264"
。因而在认证之前,会先重新拼出 URL 的完整路径部分,然后再通过authenticationOK()
执行认证。
“LIVE555 Media Server” 不提供认证,认证将总是成功。这里不再详细分析认证过程。请求认证成功时,则通过
GenericMediaServer::createNewClientSessionWithId()
创建RTSPServer::RTSPClientSession
对象。GenericMediaServer
中ClientSession
相关的几个函数声明如下:123456789virtual ClientSession* createNewClientSession(u_int32_t sessionId) = 0;ClientSession* createNewClientSessionWithId();// Generates a new (unused) random session id, and calls the "createNewClientSession()"// virtual function with this session id as parameter.// Lookup a "ClientSession" object by sessionId (integer, and string):ClientSession* lookupClientSession(u_int32_t sessionId);ClientSession* lookupClientSession(char const* sessionIdStr);
createNewClientSession()
为纯虚函数,createNewClientSessionWithId()
和两个 lookupClientSession()
为非虚函数。
GenericMediaServer::createNewClientSessionWithId()
函数的定义如下:
创建 GenericMediaServer::ClientSession
时,GenericMediaServer::createNewClientSessionWithId()
首先找到一个非零,且还没有被占用的随机值作为 SessionID;然后通过 createNewClientSession()
函数创建对象;最后将创建的对象缓存起来并返回给调用者。
createNewClientSession()
是一个纯虚函数,对于DynamicRTSPServer
-> RTSPServerSupportingHTTPStreaming
-> RTSPServer
-> GenericMediaServer
这个继承层次,它是在 RTSPServer
中定义的:
live555 中 GenericMediaServer::ClientSession
的继承层次结构如下:
有了 RTSPServer::RTSPClientSession
之后,则通过它的 handleCmd_SETUP()
处理 SETUP
请求。同时根据会话的设置,设置 playAfterSetup
。
当无法创建 RTSPServer::RTSPClientSession
,但认证成功时,通过 handleCmd_sessionNotFound()
返回 454 错误响应给客户端。
但如果认证失败的话,则似乎不会给客户端任何响应。
TEARDOWN、PLAY、PAUSE、GET_PARAMETER、SET_PARAMETER请求的处理
TEARDOWN
、PLAY
、PAUSE
、GET_PARAMETER
、SET_PARAMETER
请求的处理如下:
这些请求都需要在流媒体会话创建之后才能发起,因而直接调用客户端会话结构 RTSPServer::RTSPClientSession
的 handleCmd_withinSession()
来处理。当会话结构不存在时,通过 handleCmd_sessionNotFound()
返回错误消息给客户端。
这里的 GET_PARAMETER
和 SET_PARAMETER
操作仅应用于由传入的 URL 标识的特定资源,在 RTSPServer::RTSPClientSession
中对这两个方法的处理,与前面看到的,应用于整个服务器的操作相同:
RTSPServer::RTSPClientSession
的 setRTSPResponse()
操作都是直接委托给 RTSPServer::RTSPClientConnection
的对应函数来完成的:
REGISTER、DEREGISTER 请求的处理
REGISTER
和 DEREGISTER
不是规范中明确定义的标准 RTSP 方法。这里简单地看一下,live 555 对于它们的处理:
错误方法的处理
当 RTSP 消息的方法无法识别时,RTSPServer::RTSPClientConnection
通过 handleCmd_notSupported()
来处理:
这里向 fResponseBuffer
填充一个 405 的 RTSP 错误响应消息。
根据 RTSP 请求消息类型的不同,执行分别的处理的过程,都是为对应的请求产生一个 RTSP 响应消息,并放进响应缓冲区中。处理完之后,这个缓冲区中的内容,会被发送给客户端:
如果流媒体会话被配置为在 SETUP 之后立即启动播放的话,则在会立即启动播放:
在循环体的最后,会将已经处理的消息的内容移除出请求缓冲区,并将剩余的还未处理的数据移动到缓冲区的开头部分:
其中 resetRequestBuffer()
的定义如下:
ClientConnection::resetRequestBuffer()
的定义如下:
即在 resetRequestBuffer()
中会复位 fRequestBytesAlreadySeen
、fRequestBufferBytesLeft
、fLastCRLF
和 fBase64RemainderCount
等状态。注意,newBytesRead 得到了更新,因而如果在缓冲区还存在未处理的不完整的消息的数据,这些状态会在下一次循环迭代的开始处得到正确的更新。
最后,在 RTSPServer::RTSPClientConnection::handleRequestBytes()
函数的最后,根据需要会关闭 socket:
打赏
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 源码分析:播放启动