1. 初始化
在dss中runserver.cpp的startserver是整个服务的初始化函数
关于RTSP点播这块的主要初始化内容如下:
1) sServer->Initialize(inPrefsSource,inMessagesSource, inPortOverride,createListeners);
//RunServer.cpp
sServer的类型是QTSServer,在初始化函数中调用了CreateListeners函数,目的是创建两个监听对象
l RTSPListenerSocket
l HTTPListenerSocket
这两个类都继承于TCPListenerSocket类
2) Socket::StartThread();//启动EventThread线程
3)在其他初始化工作完成后,在runserver.cpp中的startserver开始启动任务 sServer->StartTasks();
主要的工作是注册请求事件:fListeners[x]->RequestEvent(EV_RE);
其实就是把sUniqueID与自己类的指针this管理,然后注册到EventThread线程中fRefTable引用表中,然后把文件描述符(socket)加入到select函数监视的集合中
PS:这块设计到dss的网络架构模型,后续会出专门一篇文章进行描述,目前大家知道这个目的就可以了
2. 点播请求
当客户端发出请求时,EventContext.cpp中void EventThread::Entry()函数中select_waitevent函数会检测到有对象可读,然后然后从fRefTable中解析出来关联的对象类,执行对应的ProcessEvent
说明:大家都知道dss的网络模型是select,select_waitevent是检测是否有可读的对象
fRefTable这个是OSRefTable引用表的类,关于这块代码请参考我另一篇blog
http://blog.csdn.net/wq5866280/article/details/50435974
这里fRefTable中解析出来的对象,其实就是1.3节中说明fListeners[x],这里请求的是RTSP,所以对象是RTSPListenerSocket,这里主要是处理RTSPListenerSocket的ProcessEvent
3. RTSPListenerSocket的ProcessEvent
由于RTSPListenerSocket是TCPListenerSocket的派生类,ProcessEvent的执行是在TCPListenerSocket类中
函数的主要作用:accept客户端
每接收一个客户端,就是创建一个TCPSocket* theSocket = NULL;
theTask = this->GetSessionTask(&theSocket);
//创建一个 RTSPSession* theTask = NEW RTSPSession
然后设置客户端theSocket与task关联,然后注册事件
theSocket->Set(osSocket,&addr);
theSocket->InitNonBlocking(osSocket);
theSocket->SetTask(theTask);
theSocket->RequestEvent(EV_RE);
和1.3节一样
把客户端的socket加入到select监视集合中,如果有数据需要读取,这执行TCPSocket的ProcessEvent函数,其实就是基类EventContext中的ProcessEvent,也就是给把task加入到线程池中某一个线程的 OSQueue_Blocking fTaskQueue队列中,等待执行task类中的run函数,这里运行的就是RTSPSession中的run函数。
后面一个章节主要说明RTSPSession的run函数。
一个rtsp请求对应一个RTSPSession,核心处理流程是在run函数switch块中,类似状态机处理
1.当一个新的rtsp请求处理时,首先会经过kReadingFirstRequest分支
1) if ((err =fInputStream.ReadRequest()) == QTSS_NoErr)
{
fInputSocketP->RequestEvent(EV_RE);
return 0;
}
fInputStream.ReadRequest()函数作用,就是读取客户端一个完整的RTSP包,把状态赋值为kHTTPFilteringRequest,然后continue,继续循环,switch到kHTTPFilteringRequest
fInputSocketP->RequestEvent(EV_RE);//根据原注释,应该是fInputStream中的fSocket读取数据出现错误时,才执行的,把fInputSocketP加入事件监视中读取,可能读取的数据不同,实际上fInputSocketP就是指向fSocket的指针
比如我们用vlc播放本地的MP4文件,第一包请求的buffer如下:
2. kHTTPFilteringRequest分支处理
fState = kHaveNonTunnelMessage; // assumeit's not a tunnel setup message
QTSS_Error preFilterErr = this->PreFilterForHTTPProxyTunnel();
if ( preFilterErr == QTSS_NoErr )
{
HTTP_TRACE("RTSPSession::Run kHTTPFilteringRequest\n" )
continue;
}
PreFilterForHTTPProxyTunnel函数是对报文内容进行check,判读是否有HTTP报文的内容
,是则直接进入RTSP-over-HTTP状态处理的下一步:kSocketHasBeenBoundIntoHTTPTunnel(此处因为并没有这种需求,所以没有研究),下一步进入kHaveNonTunnelMessage分支
3. kHaveNonTunnelMessage分支处理
fRequest = NEW RTSPRequest(this);//创建一个新的RTSPRequest对象
fRoleParams.rtspRequestParams.inRTSPRequest= fRequest;
fRoleParams.rtspRequestParams.inRTSPHeaders= fRequest->GetHeaderDictionary();
//给rtspRequestParams赋值
fReadMutex.Lock();
fSessionMutex.Lock();
//加锁,说明RTSP请求已经开始被处理,要确保处理请求不会被竞争
fOutputStream.ResetBytesWritten();//重置输出流
下一步进入kFilteringRequest
4. kFilteringRequest分支处理
1)调用所有注册过kRTSPFilterRole角色的模块
PS:模块的注册流程是在服务初始化的时候注册了,不同模块注册了不同角色,后续会专门写篇文章进行描述
numModules =QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPFilterRole);
for (; (fCurrentModule < numModules)&& ((!fRequest->HasResponseBeenSent())|| fModuleState.eventRequested); fCurrentModule++)
{
…..
}
此处执行的kRTSPFilterRole角色的模块是以下模块,执行相应的FilterRequest函数
l QTSSAdminModule:判断是否是admin请求,此处未进入流程,所以不分析
l QTSSMP3StreamingModule:判断是否是rtsp请求,如果是则退出不处理,所以不分析
注意红色标记代码:如果任何模块赋值了响应的数据,就是准备像客户端发送数据,都会导致跳过其他注册相关角色的模块处理
this->SetupRequest();//解析一个正常的请求,判断是rtsp的哪一步请求,进行处理
if (fRequest->HasResponseBeenSent())
{
fState =kPostProcessingRequest;
break;
}
//执行以上代码,只会在option请求时,或者有错误,这表明在option请求时才会进入kPostProcessingRequest分支,否则进入kRoutingRequest分支,下面我们先以kPostProcessingRequest分支为例
此时fRequest的fOutputStream对象已经准备好了回复客户端,如下图
5. kPostProcessingRequest分支处理
if (fRTPSession != NULL) //在option时,rtpsession还未创建,所以不会执行相关代码,直接进入kSendingResponse分支
6. kSendingResponse处理
该分支的作用就是把fOutputStream对象中的数据发给客户端,然后进入kCleaningUp分支
7. kCleaningUp分支处理
顾名思义,该分支主要做清理工作,此处不再详细分析
华丽分割线=========================
上面分析,在第4步的时候,只有option请求才会进入kPostProcessingRequest分支,其他请求会进入kRoutingRequest分支处理,所以我们再从kRoutingRequest分支分析下
5. kRoutingRequest分支处理
// Invoke router modules
numModules =QTSServerInterface::GetNumModulesInRole(QTSSModule::kRTSPRouteRole);
for (; (fCurrentModule < numModules)&& ((!fRequest->HasResponseBeenSent())|| fModuleState.eventRequested); fCurrentModule++)
{
….
}
此处kFilteringRequest分支处理一样,是调用注册了kRTSPRouteRole角色的模块,同时注意点也和kFilteringRequest分支一样
此处调用的模块只有QTSSReflectorModule模块
QTSSReflectorModule模块执行RedirectBroadcast函数,主要设置RTSPRequest对象的某些属性,由于判断条件未通过,其实没做什么操作
fRequest->SetupAuthLocalPath();
//设置路径属性
if (fRequest->HasResponseBeenSent())
{
fState= kPostProcessingRequest;
break;
}//此处跳过
if(fRequest->SkipAuthorization())//跳过认证,默认为true
{
fState = kPreprocessingRequest;
}
开始进入kPreprocessingRequest分支
6. kPreprocessingRequest分支处理
7.kProcessingRequest分支处理
8.kPostProcessingRequest分支处理
6,7,8分支按顺序执行,这三块的处理主要是调用相应模块注册的kRTSPPreProcessorRole,kRTSPRequestRole,kRTSPPostProcessorRole这三种角色的函数,用于具体处理RTSP的相关命令处理,比如QTSSFileModule.cpp文件中,有对descirbe,play等请求命令的处理,如下
9.kCleaningUp分支处理
同上面的kCleaningUp分支处理
总结:以上说明了RTSPSession中的大致处理请求的流程,后面章节会具体针对不同的命令结合模块中的代码分析具体的处理