GenericMediaServer
先分析下 RTSPServer
的父类 GenericMediaServer
,GenericMediaServer
的功能主要分为三部分。
- 监听服务端端口,收到客户端连接请求时,创建
ClientConnection
实例和客户端交互。
static int setUpOurSocket(UsageEnvironment& env, Port& ourPort);
void incomingConnectionHandlerOnSocket(int serverSocket);
其中ClientConnection
定义handleRequestBytes
抽象方法,用于和客户端交互。
virtual void handleRequestBytes(int newBytesRead) = 0;
- 维护多媒体会话,每一路多媒体会话包含一路或多路音视频流。
void addServerMediaSession(ServerMediaSession* serverMediaSession);
void removeServerMediaSession(ServerMediaSession* serverMediaSession);
ServerMediaSession
维护一路或多路音视频流,包含所有的SDP信息。
char* generateSDPDescription();
Boolean addSubsession(ServerMediaSubsession* subsession);
- 维护
ClientSession
,用于保活。
static void noteClientLiveness(ClientSession* clientSession);
static void livenessTimeoutTask(ClientSession* clientSession);
RTSPServer
RTSP OVER HTTP这里不做分析,RTSPServer的RTSPClientConnection
继承父类的ClientConnection
实现了handleRequestBytes
方法,对客户端请求的数据进行解读,提取RTSP命令,然后调用相应的方法,自身处理以下两条命令,其余交由RTSPClientSession
处理。
virtual void handleCmd_OPTIONS();
virtual void handleCmd_DESCRIBE(char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr);
RTSPClientConnection
处理完OPTIONS, DESCRIBE命令后,收到SETUP命令时,便创建RTSPClientSession
对象,由RTSPClientSession
对象处理SETUP,PLAY,PAUSE,TEARDOWN等与音视频连接传输有关的命令。
virtual void handleCmd_SETUP(RTSPClientConnection* ourClientConnection,
char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr);
virtual void handleCmd_withinSession(RTSPClientConnection* ourClientConnection,
char const* cmdName,
char const* urlPreSuffix, char const* urlSuffix,
char const* fullRequestStr);
virtual void handleCmd_TEARDOWN(RTSPClientConnection* ourClientConnection,
ServerMediaSubsession* subsession);
virtual void handleCmd_PLAY(RTSPClientConnection* ourClientConnection,
ServerMediaSubsession* subsession, char const* fullRequestStr);
virtual void handleCmd_PAUSE(RTSPClientConnection* ourClientConnection,
ServerMediaSubsession* subsession);
接下来分析几个重要的方法:
handleCmd_DESCRIBE
返回请求的ServerMediaSession
的音视频能力,使用SDP协议描述。先找到对应的ServerMediaSession
,然后在ServerMediaSession
中遍历ServerMediaSubsession
。
ServerMediaSession::generateSDPDescription()
->ServerMediaSubsession::sdpLines()=0
handleCmd_SETUP
如果使用RAW_UDP协议,使用serverRTPPort
端口创建 BasicUDPSink
对象。
否则,分别使用serverRTPPort
和serverRTCPPort
创建socket
,然后使用rtpsock
创建RTPSink
对象。
handleCmd_PLAY
主要方法:startStream
对于RTP_UDP
和RTP_TCP
协议,则创建RTCPInstance
对象,并启动运行。
if (fRTCPInstance == NULL && fRTPSink != NULL)
{
// Create (and start) a 'RTCP instance' for this RTP sink:
fRTCPInstance = fMaster.createRTCP(fRTCPgs, fTotalBW, (unsigned char*)fMaster.fCNAME, fRTPSink);
// Note: This starts RTCP running automatically
fRTCPInstance->setAppHandler(fMaster.fAppHandlerTask, fMaster.fAppHandlerClientData);
}
对于RAW_UDP
协议,使用handleCmd_SETUP
中创建的BasicUDPSink
:
fUDPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);
fUDPSink
从fMediaSource
中循环读取音视频帧,然后通过handleCmd_SETUP
中创建的socket
发送:
fGS->output(envir(), fOutputBuffer, frameSize);
对于RTP_UDP
和RTP_TCP
协议,使用handleCmd_SETUP
中创建的RTPSink
:
fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);
RTPSink
从fMediaSource
中循环读取音视频帧,在发送时,RTP_UDP
使用handleCmd_SETUP
中创建的socket
发送:
// Normal case: Send as a UDP packet:
if (!fGS->output(envir(), packet, packetSize)) success = False;
而RTP_TCP
则使用ClientConnection
的tcpsocket发送,并且在发送数据帧之前,先发送4字节帧头,指明通道号和数据包大小。
u_int8_t framingHeader[4];
framingHeader[0] = '$';
framingHeader[1] = streamChannelId;
framingHeader[2] = (u_int8_t) ((packetSize&0xFF00)>>8);
framingHeader[3] = (u_int8_t) (packetSize&0xFF);
if (!sendDataOverTCP(socketNum, framingHeader, 4, False)) break;
if (!sendDataOverTCP(socketNum, packet, packetSize, True)) break;