testOnDemandRTSPServer.cpp 是一个简单的点播服务器,可以点播各种媒体文件类型,本文主要分析.264类型文件。
把其他类型的源码使用#if 0 ....#endif 进行屏蔽,这样不相关的代码就可以不看了,也显得简洁了很多。
1 int main(int argc, char** argv) { 2 // Begin by setting up our usage environment: 3 TaskScheduler* scheduler = BasicTaskScheduler::createNew(); 4 env = BasicUsageEnvironment::createNew(*scheduler); 5 6 UserAuthenticationDatabase* authDB = NULL; 7 #ifdef ACCESS_CONTROL 8 // To implement client access control to the RTSP server, do the following: 9 authDB = new UserAuthenticationDatabase; 10 authDB->addUserRecord("username1", "password1"); // replace these with real strings 11 // Repeat the above with each <username>, <password> that you wish to allow 12 // access to the server. 13 #endif 14 15 // Create the RTSP server: 16 RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB); 17 if (rtspServer == NULL) { 18 *env << "Failed to create RTSP server: " << env->getResultMsg() << " "; 19 exit(1); 20 } 21 22 char const* descriptionString 23 = "Session streamed by "testOnDemandRTSPServer""; 24 25 // Set up each of the possible streams that can be served by the 26 // RTSP server. Each such stream is implemented using a 27 // "ServerMediaSession" object, plus one or more 28 // "ServerMediaSubsession" objects for each audio/video substream. 29 // A H.264 video elementary stream: 30 { 31 char const* streamName = "h264ESVideoTest"; //gxq 流名字,媒体名 32 char const* inputFileName = "test.264"; //gxq 文件名,当客户端请求h264ESVideoTest时,实际上打开的是test.264文件 33 34 //创建一个会话 35 ServerMediaSession* sms 36 = ServerMediaSession::createNew(*env, streamName, streamName, 37 descriptionString); 38 39 //在会话中添加一个视频流子会话 40 sms->addSubsession(H264VideoFileServerMediaSubsession 41 ::createNew(*env, inputFileName, reuseFirstSource)); 42 43 //gxq 将此会话加入到哈希表fServerMediaSessions中 44 rtspServer->addServerMediaSession(sms); 45 46 announceStream(rtspServer, sms, streamName, inputFileName); 47 } 48 49 if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) { 50 *env << " (We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling.) "; 51 } else { 52 *env << " (RTSP-over-HTTP tunneling is not available.) "; 53 } 54 55 //gxq 执行循环方法,对sock的读取任务和对媒体文件的延时发送操作都在这个循环中 56 env->taskScheduler().doEventLoop(); // does not return 57 58 return 0; // only to prevent compiler warning 59 }
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
建立一个任务调度器,并使用该调度器创建一个全局的环境对象,BasicTaskScheduler父类BasicTaskScheduler0中包含一个sock任务双向链表成员变量HandlerSet后面对sock的处理主要是处理此链表
1 // To implement background reads: 2 HandlerSet* fHandlers; //gxq 一个双向链表,用于保存sock相关任务,SingleStep循环中主要对此链表进行操作 3 int fLastHandledSocketNum;
下面开始正式创建一个服务对象代码如下
// Create the RTSP server: RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB); if (rtspServer == NULL) { *env << "Failed to create RTSP server: " << env->getResultMsg() << " "; exit(1); }
创建服务器对象的同时,创建了监听套接字,并将socket与相关处理连接函数放入sock任务链表fHandlers中。
我们来分析一下RTSPServer::createNew()函数,
1 RTSPServer* 2 RTSPServer::createNew(UsageEnvironment& env, Port ourPort, 3 UserAuthenticationDatabase* authDatabase, 4 unsigned reclamationSeconds) { 5 int ourSocket = setUpOurSocket(env, ourPort); //gxq 使用groupsockHelper创建监听sock 6 if (ourSocket == -1) return NULL; 7 8 //gxq 在父类GenericMediaServer的构造函数中完成sock与处理监听函数的映射,加入到sock任务队列 9 return new RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationSeconds); 10 }
我们发现在createNew()函数中,创建了监听套接字ourSocket,
继续往下看RTSPServer的构造函数
1 RTSPServer::RTSPServer(UsageEnvironment& env, 2 int ourSocket, Port ourPort, 3 UserAuthenticationDatabase* authDatabase, 4 unsigned reclamationSeconds) 5 : GenericMediaServer(env, ourSocket, ourPort, reclamationSeconds), 6 fHTTPServerSocket(-1), fHTTPServerPort(0), 7 fClientConnectionsForHTTPTunneling(NULL), // will get created if needed 8 fTCPStreamingDatabase(HashTable::create(ONE_WORD_HASH_KEYS)), 9 fPendingRegisterOrDeregisterRequests(HashTable::create(ONE_WORD_HASH_KEYS)), 10 fRegisterOrDeregisterRequestCounter(0), fAuthDB(authDatabase), fAllowStreamingRTPOverTCP(True) { 11 }
发现其构造函数调用了父类GenericMediaServer的构造函数,咱们继续往下看看GenericMediaServer的构造函数
1 GenericMediaServer 2 ::GenericMediaServer(UsageEnvironment& env, int ourSocket, Port ourPort, 3 unsigned reclamationSeconds) 4 : Medium(env), 5 fServerSocket(ourSocket), fServerPort(ourPort), fReclamationSeconds(reclamationSeconds), 6 fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)), 7 fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)), 8 fClientSessions(HashTable::create(STRING_HASH_KEYS)) { 9 ignoreSigPipeOnSocket(fServerSocket); // so that clients on the same host that are killed don't also kill us 10 11 // Arrange to handle connections from others: /gxq 将sock任务加入任务队列,当监听到有连接请求时调用incomingConnectionHandler 12 env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, incomingConnectionHandler, this); 13 }
发现在GenericMediaServer构造函数中调用任务调度器将监听套接字fServerSocket与处理函数incomingConnectionHandler()放入了sock任务队列。当服务监听到有客户端连接时会调用 incomingConnectionHandler进行处理。
这下分析下来你会发现,这个live RTSP服务器跟咱们平常建立一个普通的服务器的流程没什么区别:
都是创建socket,绑定bi nd,监听listen,监听后使用accept进行处理
(实际incomingConnectionHandler内部就是调用 的accept);
到这里服务器的部署基本已经完成了,但此时还没媒体文件的信息,不要着急咱们接着往下分析。
咱们回到testOnDemandRTSPServer.cpp中的main函数,分析264媒体文件信息
1 // A H.264 video elementary stream: 2 { 3 char const* streamName = "h264ESVideoTest"; //gxq 流名字,媒体名 4 char const* inputFileName = "test.264"; //gxq 文件名,当客户端请求h264ESVideoTest时,实际上打开的是test.264文件 5 6 //创建一个会话 7 ServerMediaSession* sms 8 = ServerMediaSession::createNew(*env, streamName, streamName, 9 descriptionString); 10 11 //在会话中添加一个视频流子会话 12 sms->addSubsession(H264VideoFileServerMediaSubsession 13 ::createNew(*env, inputFileName, reuseFirstSource)); 14 15 //gxq 将此会话加入到哈希表fServerMediaSessions中 16 rtspServer->addServerMediaSession(sms); 17 18 announceStream(rtspServer, sms, streamName, inputFileName); 19 }
代码的注释已经很详细,不再重复。
接下来执行到
1 env->taskScheduler().doEventLoop(); // does not return
此时服务器就已经进入等客户端连接状态了。
使用vlc进行测试
OK,使用live 点播服务器的过程大概就是这个样子。