RTSP 服务器实例 live555 源代码分析

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.0.1/1.mpg) 连接服务器时,select 返回对应的 socket,进而根据前面保存的对应关系,可找到对应处理函数句柄,这里就是前面提到的 incomingConnectionHandler 了。在 incomingConnectionHandler 中创建了 RTSPClientSession,开始对这个客户端的会话进行处理。

2. DESCRIBE 请求消息处理过程

RTSP 服务器收到客户端的 DESCRIBE 请求后,根据请求 URL (rtsp://192.168.0.1/1.mpg),找到对应的流媒体资源,返回响应消息。live555 中的 ServerMediaSession 类用来处理会话中描述,它包含多个(音频或视频)的子会话描述 (ServerMediaSubsession)。

RTSP 服务器收到客户端的连接请求,建立了 RTSPClientSession 类,处理单独的客户会话。在建立 RTSPClientSession 的过程中,将新建立的 socket 句柄(clientSocket)和 RTSP 请求处理函数句柄 RTSPClientSession::incomingRequestHandler 传给任务调度器,由任务调度器对两者进行一对一关联。

当客户端发出 RTSP 请求后,服务器主循环中的 select 调用返回,根据 socket 句柄找到对应的 incomingRequestHandler,开始消息处理。先进行消息的解析,如果发现请求是 DESCRIBE 则进入 handleCmd_DESCRIBE 函数。根据客户端请求 URL 的后缀 (如 1.mpg), 调用成员函数 DynamicRTSPServer::lookupServerMediaSession 查找对应的流媒体信息 ServerMediaSession。如果 ServerMediaSession 不存在,但是本地存在 1.mpg 文件,则创建一个新的 ServerMediaSession。在创建 ServerMediaSession 过程中,根据文件后缀.mpg,创建媒体 MPEG-1or2 的解复用器 (MPEG1or2FileServerDemux)。再由 MPEG1or2FileServerDemux 创建一个子会话描述 MPEG1or2DemuxedServerMediaSubsession。最后由 ServerMediaSession 完成组装响应消息中的 SDP 信息(SDP 组装过程见下面的描述),然后将响应消息发给客户端,完成一次消息交互。

SDP 消息组装过程:

      ServerMediaSession 负责产生会话公共描述信息,子会话描述由 MPEG1or2DemuxedServerMediaSubsession 产生。 MPEG1or2DemuxedServerMediaSubsession 在其父类成员函数 OnDemandServerMediaSubsession::sdpLines () 中生成会话描述信息。在 sdpLines () 实现里面,创建一个虚构 (dummy) 的 FramedSource (具体实现类为 MPEG1or2AudioStreamFramer 和 MPEG1or2VideoStreamFramer) 和 RTPSink(具体实现类为 MPEG1or2AudioRTPSink 和 MPEG1or2VideoRTPSink),最后调用 setSDPLinesFromRTPSink (...) 成员函数生成子会话描述。

Live555 库是一个使用开放标准协议如 RTP/RTCP、RTSP、SIP 等实现多媒体流式传输的开源 C 库集。这些函数库可以在 Unix、Windows、QNX 等操作系统下编译使用,基于此建立 RTSP/SIP 服务器和客户端来实现多媒体流的传输。下面给出具体实现过程 [4]:

(1)客户端发起 RTSP OPTION 请求,目的是得到服务器提供什么方法。RTSP 提供的方法一般包括 OPTIONS、DESCRIBE、SETUP、TEARDOWN、PLAY、PAUSE、SCALE、GET_PARAMETER。

(2)服务器对 RTSP OPTION 回应,服务器实现什么方法就回应哪些方法。在此系统中,我们只对 DESCRIBE、SETUP、TEARDOWN、PLAY、PAUSE 方法做了实现。

(3)客户端发起 RTSP DESCRIBE 请求,服务器收到的信息主要有媒体的名字,解码类型,视频分辨率等描述,目的是为了从服务器那里得到会话描述信息(SDP)。

(4)服务器对 RTSP DESCRIBE 响应,发送必要的媒体参数,在传输 H.264 文件时,主要包括 SPS/PPS、媒体名、传输协议等信息。

(5)客户端发起 RTSP SETUP 请求,目的是请求会话建立并准备传输。请求信息主要包括传输协议和客户端端口号。

(6)服务器对 RTSP SETUP 响应,发出相应服务器端的端口号和会话标识符。

(7)客户端发出了 RTSP PLAY 的请求,目的是请求播放视频流。

(8)服务器对 RTSP PLAY 响应,响应的消息包括会话标识符,RTP 包的序列号,时间戳。此时服务器对 H264 视频流封装打包进行传输。

(9)客户端发出 RTSP TEARDOWN 请求,目的是关闭连接,终止传输。

(10)服务器关闭连接,停止传输。

本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击领取↓↓

3. SETUP 请求消息处理过程

RTSPClientSession 类用于处理单独的客户会话。其类成员函数 handleCmd_SETUP () 处理客户端的 SETUP 请求。调用 parseTransportHeader () 对 SETUP 请求的传输头解析,调用子会话 (这里具体实现类为 OnDemandServerMediaSubsession) 的 getStreamParameters () 函数获取流媒体发送传输参数。将这些参数组装成响应消息,返回给客户端。

获取发送传输参数的过程:调用子会话 (具体实现类 MPEG1or2DemuxedServerMediaSubsession) 的 createNewStreamSource (...) 创建 MPEG1or2VideoStreamFramer,选择发送传输参数,并调用子会话的 createNewRTPSink (...) 创建 MPEG1or2VideoRTPSink。同时将这些信息保存在 StreamState 类对象中,用于记录流的状态。

客户端发送两个 SETUP 请求,分别用于建立音频和视频的 RTP 接收。

4. PLAY 请求消息处理过程

RTSPClientSession 类成员函数 handleCmd_PLAY () 处理客户端的播放请求。首先调用子会话的 startStream (), 内部调用 MediaSink::startPlaying (...),然后是 MultiFramedRTPSink::continuePlaying (),接着调用 MultiFramedRTPSink::buildAndSendPacket (...)。buildAndSendPacke 内部先设置 RTP 包头,内部再调用 MultiFramedRTPSink::packFrame () 填充编码帧数据。

packFrame 内部通过 FramedSource::getNextFrame (), 接着 MPEGVideoStreamFramer::doGetNextFrame (),再接着经过 MPEGVideoStreamFramer::continueReadProcessing (), FramedSource::afterGetting (...), MultiFramedRTPSink::afterGettingFrame (...), MultiFramedRTPSink::afterGettingFrame1 (...) 等一系列繁琐调用,最后到了 MultiFramedRTPSink::sendPacketIfNecessary (), 这里才真正发送 RTP 数据包。然后是计算下一个数据包发送时间,把 MultiFramedRTPSink::sendNext (...) 函数句柄传给任务调度器,作为一个延时事件调度。在主循环中,当 MultiFramedRTPSink::sendNext () 被调度时,又开始调用 MultiFramedRTPSink::buildAndSendPacket (...) 开始新的发送数据过程,这样客户端可以源源不断的收到服务器传来的 RTP 包了。

发送 RTP 数据包的间隔计算方法:

Update the time at which the next packet should be sent, based on the duration of the frame that we just packed into it.

本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击领取↓↓

猜你喜欢

转载自blog.csdn.net/m0_60259116/article/details/126426715