live555源码分析(四)RTSPServer分析

live555源码分析系列

live555源码分析(一)live555初体验

live555源码分析(二)基本组件上

live555源码分析(三)基本组件下

live555源码分析(四)RTSPServer分析

live555源码分析(五)DESCRIBE请求的处理

live555源码分析(六)SETUP和PLAY请求的处理

live555源码分析(七)播放过程

live555源码分析(八)多播

live555源码分析(四)RTSPServer分析

一、一个简单的Rtsp服务器

这是一个基于live555的RTSP服务器,此RTSP服务器实现了H.264文件的单播

#include <liveMedia.hh>
#include <BasicUsageEnvironment.hh>
#include <GroupsockHelper.hh>

static char const* inputFileName = "test.h264";

int main(int argc, char* argv[])
{
    /* 创建调度器 */
    TaskScheduler* scheduler = BasicTaskScheduler::createNew();
    UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);

    /* 创建rtsp服务器 */
    RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554);
    if (rtspServer == NULL)
    {
        *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
        return -1;
    }

    /* 创建会话 */
    ServerMediaSession* sms = ServerMediaSession::createNew(*env, "live", "test", "test");
    
    /* 添加子会话 */
    sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(*env, inputFileName, True));
    
    /* 向服务器添加会话 */
    rtspServer->addServerMediaSession(sms);

	char* url = rtspServer->rtspURL(sms);
	*env << "Play this stream using the URL \"" << url << "\"\n";
	delete[] url;

    /* 循环处理事件 */
    env->taskScheduler().doEventLoop();

    return 0;
}

将此程序保存为h264_rtsp_server.cpp

执行下面的Makefile,可以得到rtspServer,运行rtspServer,会打印一个url,在vlc输入url,即可看到视频

下面将基于这个程序来讲解

二、源码分析

2.1 RTSPServer的构造

我们从创建RTSPServer开始分析

RTSPServer::createNew会创建一个TCP套接字,绑定好指定的端口,作为RTSP服务器的监听套接字,然后会生成RTSPServer

RTSPServer*
RTSPServer::createNew(UsageEnvironment& env, Port ourPort,
		      UserAuthenticationDatabase* authDatabase,
		      unsigned reclamationSeconds) {
  int ourSocket = setUpOurSocket(env, ourPort);
  if (ourSocket == -1) return NULL;
  
  return new RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationSeconds);
}

RTSPServer继承于GenericMediaServer,我们先看GenericMediaServer的构造函数

GenericMediaServer
::GenericMediaServer(UsageEnvironment& env, int ourSocket, Port ourPort,
                     unsigned reclamationSeconds) {
    env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, incomingConnectionHandler, this);
}

该构造函数会把服务器的套接字加入事件循中,接收客户端请求

看一看incomingConnectionHandler是如何处理客户端连接的

void GenericMediaServer::incomingConnectionHandlerOnSocket(int serverSocket) {
	int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
	createNewClientConnection(clientSocket, clientAddr);
}

该函数会accept接收一个客户端,然后调用createNewClientConnection创建一个客户端连接,createNewClientConnection是一个虚函数,它在RTSPServer中被重写

2.2 客户端连接

由上面可知,当从vlc输入url时,会向服务器发起连接,服务器会调用createNewClientConnection创建一个客户端连接

createNewClientConnectionRTSPServer中被重写,其定义如下

GenericMediaServer::ClientConnection*
RTSPServer::createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr) {
  return new RTSPClientConnection(*this, clientSocket, clientAddr);
}

可知它创建了一个RTSPClientConnection,查看其定义

class RTSPClientConnection: public GenericMediaServer::ClientConnection {
protected:
    virtual void handleRequestBytes(int newBytesRead);
    virtual void handleCmd_OPTIONS();
	virtual void handleCmd_DESCRIBE();
};

RTSPClientConnection继承于ClientConnection,它定义了许多命令请求处理

接下来看RTSPClientConnection如何构造

首先看ClientConnection的构造

GenericMediaServer::ClientConnection
::ClientConnection(GenericMediaServer& ourServer, int clientSocket, struct sockaddr_in clientAddr)
  : fOurServer(ourServer), fOurSocket(clientSocket), fClientAddr(clientAddr) {
  	
    /* 添加到事件循环中 */
  	envir().taskScheduler()
        .setBackgroundHandling(fOurSocket, SOCKET_READABLE|SOCKET_EXCEPTION, incomingRequestHandler, this);
}

它将客户端连接套接字加入事件循环中,监听其可读或者异常

其处理函数为incomingRequestHandler,查看其定义

void GenericMediaServer::ClientConnection::incomingRequestHandler() {
  	int bytesRead = readSocket(envir(), fOurSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
  	handleRequestBytes(bytesRead);
}

incomingRequestHandler先读取客户发送过来的信息,然后调用handleRequestBytes处理,handleRequestBytes是一个虚函数,在RTSPServer中被重写

RTSPClientConnection的构造函数没有太多内容这里就不分析了

2.3 客户端请求处理过程

接下来分析客户端的请求处理过程,首先先来看一个一般的交互过程

OPTIONS

  • C–>S

    OPTIONS rtsp://192.168.31.115:8554/live RTSP/1.0\r\n
    CSeq: 2\r\n
    \r\n
    
  • S–>C

    RTSP/1.0 200 OK\r\n
    CSeq: 2\r\n
    Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY\r\n
    \r\n
    

DESCRIBE

  • C–>S

    DESCRIBE rtsp://192.168.31.115:8554/live RTSP/1.0\r\n
    CSeq: 3\r\n
    Accept: application/sdp\r\n
    \r\n
    
  • S–>C

    RTSP/1.0 200 OK\r\n
    CSeq: 3\r\n
    Content-length: 146\r\n
    Content-type: application/sdp\r\n
    \r\n
    
    v=0\r\n
    o=- 91565340853 1 in IP4 192.168.31.115\r\n
    t=0 0\r\n
    a=contol:*\r\n
    m=video 0 RTP/AVP 96\r\n
    a=rtpmap:96 H264/90000\r\n
    a=framerate:25\r\n
    a=control:track0\r\n
    

SETUP

  • C–>S

    SETUP rtsp://192.168.31.115:8554/live/track0 RTSP/1.0\r\n
    CSeq: 4\r\n
    Transport: RTP/AVP;unicast;client_port=54492-54493\r\n
    \r\n
    
  • S–>C

    RTSP/1.0 200 OK\r\n
    CSeq: 4\r\n
    Transport: RTP/AVP;unicast;client_port=54492-54493;server_port=56400-56401\r\n
    Session: 66334873\r\n
    \r\n
    

PLAY

  • C–>S

    PLAY rtsp://192.168.31.115:8554/live RTSP/1.0\r\n
    CSeq: 5\r\n
    Session: 66334873\r\n
    Range: npt=0.000-\r\n
    \r\n
    
  • S–>C

    RTSP/1.0 200 OK\r\n
    CSeq: 5\r\n
    Range: npt=0.000-\r\n
    Session: 66334873; timeout=60\r\n
    \r\n
    

根据这个过程,来分析live555的处理过程

首先live555是解析请求,在RTSPClientConnection::handleRequestBytes中定义,代码如下

void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {
    Boolean parseSucceeded = parseRTSPRequestString((char*)fRequestBuffer, fLastCRLF+2 - fRequestBuffer,
                                                    cmdName, sizeof cmdName,
                                                    urlPreSuffix, sizeof urlPreSuffix,
                                                    urlSuffix, sizeof urlSuffix,
                                                    cseq, sizeof cseq,
                                                    sessionIdStr, sizeof sessionIdStr,
                                                    contentLength);

    if (strcmp(cmdName, "OPTIONS") == 0) {
    	handleCmd_OPTIONS();
    } else if (strcmp(cmdName, "DESCRIBE") == 0) {
    	handleCmd_DESCRIBE(urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
    } else if (strcmp(cmdName, "SETUP") == 0) {
        clientSession
    	= (RTSPServer::RTSPClientSession*)fOurRTSPServer.createNewClientSessionWithId();
    	clientSession->handleCmd_SETUP(...);
    } else if (strcmp(cmdName, "PLAY") {
    	clientSession->handleCmd_withinSession(...);
    }
               
	send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
}

首先会解析请求中的命令,序列号,url,是否已经有会话ID

然后根据不同的命令做相应的处理,注意的是在SETUP阶段,会创建一个客户端会话,然后利用它来处理命令,处理命令后会将结果放回给客户端

关于命令如何解析的,这里不做讲解,这就是一个字符串处理

先看一看handleCmd_OPTIONS

void RTSPServer::RTSPClientConnection::handleCmd_OPTIONS() {
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
	   "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sPublic: %s\r\n\r\n",
	   fCurrentCSeq, dateHeader(), fOurRTSPServer.allowedCommandNames());
}

handleCmd_OPTIONS的处理非常简单,就是返回允许请求的方法

其他请求的处理过程较为复杂,将在后面的文章单独讲解,本文就到这里了

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值