`

live555学习笔记-RTSP服务运作

 
阅读更多

RTSP服务运作

基础基本搞明白了,那么RTSP,RTP等这些协议又是如何利用这些基础机制运作的呢?
首先来看RTSP.

RTSP首先需建立TCP侦听socket。可见于此函数:

[cpp]view plaincopy
 
  1. DynamicRTSPServer*DynamicRTSPServer::createNew(UsageEnvironment&env,PortourPort,
  2. UserAuthenticationDatabase*authDatabase,
  3. unsignedreclamationTestSeconds){
  4. intourSocket=setUpOurSocket(env,ourPort);//建立TCPsocket
  5. if(ourSocket==-1)
  6. returnNULL;
  7. returnnewDynamicRTSPServer(env,ourSocket,ourPort,authDatabase,
  8. reclamationTestSeconds);
  9. }


要帧听客户端的连接,就需要利用任务调度机制了,所以需添加一个socket handler。可见于此函数:

[cpp]view plaincopy
 
  1. RTSPServer::RTSPServer(UsageEnvironment&env,
  2. intourSocket,
  3. PortourPort,
  4. UserAuthenticationDatabase*authDatabase,
  5. unsignedreclamationTestSeconds):
  6. Medium(env),
  7. fRTSPServerSocket(ourSocket),
  8. fRTSPServerPort(ourPort),
  9. fHTTPServerSocket(-1),
  10. fHTTPServerPort(0),
  11. fClientSessionsForHTTPTunneling(NULL),
  12. fAuthDB(authDatabase),
  13. fReclamationTestSeconds(reclamationTestSeconds),
  14. fServerMediaSessions(HashTable::create(STRING_HASH_KEYS))
  15. {
  16. #ifdefUSE_SIGNALS
  17. //IgnoretheSIGPIPEsignal,sothatclientsonthesamehostthatarekilled
  18. //don'talsokillus:
  19. signal(SIGPIPE,SIG_IGN);
  20. #endif
  21. //Arrangetohandleconnectionsfromothers:
  22. env.taskScheduler().turnOnBackgroundReadHandling(
  23. fRTSPServerSocket,
  24. (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP,
  25. this);
  26. }



当收到客户的连接时需保存下代表客户端的新socket,以后用这个socket与这个客户通讯。每个客户将来会对应一个rtp会话,而且各客户的RTSP请求只控制自己的rtp会话,那么最好建立一个会话类,代表各客户的rtsp会话。于是类RTSPServer::RTSPClientSession产生,它保存的代表客户的socket。下为RTSPClientSession的创建过程

 

[cpp]view plaincopy
 
  1. voidRTSPServer::incomingConnectionHandler(intserverSocket)
  2. {
  3. structsockaddr_inclientAddr;
  4. SOCKLEN_TclientAddrLen=sizeofclientAddr;
  5. //接受连接
  6. intclientSocket=accept(serverSocket,
  7. (structsockaddr*)&clientAddr,
  8. &clientAddrLen);
  9. if(clientSocket<0){
  10. interr=envir().getErrno();
  11. if(err!=EWOULDBLOCK){
  12. envir().setResultErrMsg("accept()failed:");
  13. }
  14. return;
  15. }
  16. //设置socket的参数
  17. makeSocketNonBlocking(clientSocket);
  18. increaseSendBufferTo(envir(),clientSocket,50*1024);
  19. #ifdefDEBUG
  20. envir()<<"accept()edconnectionfrom"<<our_inet_ntoa(clientAddr.sin_addr)<<"\n";
  21. #endif
  22. //产生一个sessonid
  23. //CreateanewobjectforthisRTSPsession.
  24. //(Choosearandom32-bitintegerforthesessionid(itwillbeencodedasa8-digithexnumber).Wedon'tbothercheckingfor
  25. //acollision;theprobabilityoftwoconcurrentsessionsgettingthesamesessionidisverylow.)
  26. //(Wedo,however,avoidchoosingsessionid0,becausethathasaspecialuse(by"OnDemandServerMediaSubsession").)
  27. unsignedsessionId;
  28. do{
  29. sessionId=(unsigned)our_random();
  30. }while(sessionId==0);
  31. //创建RTSPClientSession,注意传入的参数
  32. (void)createNewClientSession(sessionId,clientSocket,clientAddr);
  33. }

 

RTSPClientSession要提供什么功能呢?可以想象:需要监听客户端的rtsp请求并回应它,需要在DESCRIBE请求中返回所请求的流的信息,需要在SETUP请求中建立起RTP会话,需要在TEARDOWN请求中关闭RTP会话,等等...

RTSPClientSession要侦听客户端的请求,就需把自己的socket handler加入计划任务。证据如下:

[cpp]view plaincopy
 
  1. RTSPServer::RTSPClientSession::RTSPClientSession(
  2. RTSPServer&ourServer,
  3. unsignedsessionId,
  4. intclientSocket,
  5. structsockaddr_inclientAddr):
  6. fOurServer(ourServer),
  7. fOurSessionId(sessionId),
  8. fOurServerMediaSession(NULL),
  9. fClientInputSocket(clientSocket),
  10. fClientOutputSocket(clientSocket),
  11. fClientAddr(clientAddr),
  12. fSessionCookie(NULL),
  13. fLivenessCheckTask(NULL),
  14. fIsMulticast(False),
  15. fSessionIsActive(True),
  16. fStreamAfterSETUP(False),
  17. fTCPStreamIdCount(0),
  18. fNumStreamStates(0),
  19. fStreamStates(NULL),
  20. fRecursionCount(0)
  21. {
  22. //Arrangetohandleincomingrequests:
  23. resetRequestBuffer();
  24. envir().taskScheduler().turnOnBackgroundReadHandling(fClientInputSocket,
  25. (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler,
  26. this);
  27. noteLiveness();
  28. }



下面重点讲一下下RTSPClientSession响应DESCRIBE请求的过程:

[cpp]view plaincopy
 
  1. voidRTSPServer::RTSPClientSession::handleCmd_DESCRIBE(
  2. charconst*cseq,
  3. charconst*urlPreSuffix,
  4. charconst*urlSuffix,
  5. charconst*fullRequestStr)
  6. {
  7. char*sdpDescription=NULL;
  8. char*rtspURL=NULL;
  9. do{
  10. //整理一下下RTSP地址
  11. charurlTotalSuffix[RTSP_PARAM_STRING_MAX];
  12. if(strlen(urlPreSuffix)+strlen(urlSuffix)+2
  13. >sizeofurlTotalSuffix){
  14. handleCmd_bad(cseq);
  15. break;
  16. }
  17. urlTotalSuffix[0]='\0';
  18. if(urlPreSuffix[0]!='\0'){
  19. strcat(urlTotalSuffix,urlPreSuffix);
  20. strcat(urlTotalSuffix,"/");
  21. }
  22. strcat(urlTotalSuffix,urlSuffix);
  23. //验证帐户和密码
  24. if(!authenticationOK("DESCRIBE",cseq,urlTotalSuffix,fullRequestStr))
  25. break;
  26. //Weshouldreallycheckthattherequestcontainsan"Accept:"#####
  27. //for"application/sdp",becausethat'swhatwe'resendingback#####
  28. //Beginbylookingupthe"ServerMediaSession"objectforthespecified"urlTotalSuffix":
  29. //跟据流的名字查找ServerMediaSession,如果找不到,会创建一个。每个ServerMediaSession中至少要包含一个
  30. //ServerMediaSubsession。一个ServerMediaSession对应一个媒体,可以认为是Server上的一个文件,或一个实时获取设备。其包含的每个ServerMediaSubSession代表媒体中的一个Track。所以一个ServerMediaSession对应一个媒体,如果客户请求的媒体名相同,就使用已存在的ServerMediaSession,如果不同,就创建一个新的。一个流对应一个StreamState,StreamState与ServerMediaSubsession相关,但代表的是动态的,而ServerMediaSubsession代表静态的。
  31. ServerMediaSession*session=fOurServer.lookupServerMediaSession(urlTotalSuffix);
  32. if(session==NULL){
  33. handleCmd_notFound(cseq);
  34. break;
  35. }
  36. //Then,assembleaSDPdescriptionforthissession:
  37. //获取SDP字符串,在函数内会依次获取每个ServerMediaSubSession的字符串然连接起来。
  38. sdpDescription=session->generateSDPDescription();
  39. if(sdpDescription==NULL){
  40. //Thisusuallymeansthatafilenamethatwasspecifiedfora
  41. //"ServerMediaSubsession"doesnotexist.
  42. snprintf((char*)fResponseBuffer,sizeoffResponseBuffer,
  43. "RTSP/1.0404FileNotFound,OrInIncorrectFormat\r\n"
  44. "CSeq:%s\r\n"
  45. "%s\r\n",cseq,dateHeader());
  46. break;
  47. }
  48. unsignedsdpDescriptionSize=strlen(sdpDescription);
  49. //Also,generateourRTSPURL,forthe"Content-Base:"header
  50. //(whichisnecessarytoensurethatthecorrectURLgetsusedin
  51. //subsequent"SETUP"requests).
  52. rtspURL=fOurServer.rtspURL(session,fClientInputSocket);
  53. //形成响应DESCRIBE请求的RTSP字符串。
  54. snprintf((char*)fResponseBuffer,sizeoffResponseBuffer,
  55. "RTSP/1.0200OK\r\nCSeq:%s\r\n"
  56. "%s"
  57. "Content-Base:%s/\r\n"
  58. "Content-Type:application/sdp\r\n"
  59. "Content-Length:%d\r\n\r\n"
  60. "%s",cseq,dateHeader(),rtspURL,sdpDescriptionSize,
  61. sdpDescription);
  62. }while(0);
  63. delete[]sdpDescription;
  64. delete[]rtspURL;
  65. //返回后会被立即发送(没有把socketwrite操作放入计划任务中)。
  66. }




fOurServer.lookupServerMediaSession(urlTotalSuffix)中会在找不到同名ServerMediaSession时新建一个,代表一个RTP流的ServerMediaSession们是被RTSPServer管理的,而不是被RTSPClientSession拥有。为什么呢?因为ServerMediaSession代表的是一个静态的流,也就是可以从它里面获取一个流的各种信息,但不能获取传输状态。不同客户可能连接到同一个流,所以ServerMediaSession应被RTSPServer所拥有。创建一个ServerMediaSession过程值得一观:

[cpp]view plaincopy
 
  1. staticServerMediaSession*createNewSMS(UsageEnvironment&env,charconst*fileName,FILE*/*fid*/)
  2. {
  3. //Usethefilenameextensiontodeterminethetypeof"ServerMediaSession":
  4. charconst*extension=strrchr(fileName,'.');
  5. if(extension==NULL)
  6. returnNULL;
  7. ServerMediaSession*sms=NULL;
  8. BooleanconstreuseSource=False;
  9. if(strcmp(extension,".aac")==0){
  10. //AssumedtobeanAACAudio(ADTSformat)file:
  11. NEW_SMS("AACAudio");
  12. sms->addSubsession(
  13. ADTSAudioFileServerMediaSubsession::createNew(env,fileName,
  14. reuseSource));
  15. }elseif(strcmp(extension,".amr")==0){
  16. //AssumedtobeanAMRAudiofile:
  17. NEW_SMS("AMRAudio");
  18. sms->addSubsession(
  19. AMRAudioFileServerMediaSubsession::createNew(env,fileName,
  20. reuseSource));
  21. }elseif(strcmp(extension,".ac3")==0){
  22. //AssumedtobeanAC-3Audiofile:
  23. NEW_SMS("AC-3Audio");
  24. sms->addSubsession(
  25. AC3AudioFileServerMediaSubsession::createNew(env,fileName,
  26. reuseSource));
  27. }elseif(strcmp(extension,".m4e")==0){
  28. //AssumedtobeaMPEG-4VideoElementaryStreamfile:
  29. NEW_SMS("MPEG-4Video");
  30. sms->addSubsession(
  31. MPEG4VideoFileServerMediaSubsession::createNew(env,fileName,
  32. reuseSource));
  33. }elseif(strcmp(extension,".264")==0){
  34. //AssumedtobeaH.264VideoElementaryStreamfile:
  35. NEW_SMS("H.264Video");
  36. OutPacketBuffer::maxSize=100000;//allowforsomepossiblylargeH.264frames
  37. sms->addSubsession(
  38. H264VideoFileServerMediaSubsession::createNew(env,fileName,
  39. reuseSource));
  40. }elseif(strcmp(extension,".mp3")==0){
  41. //AssumedtobeaMPEG-1or2Audiofile:
  42. NEW_SMS("MPEG-1or2Audio");
  43. //Tostreamusing'ADUs'ratherthanrawMP3frames,uncommentthefollowing:
  44. //#defineSTREAM_USING_ADUS1
  45. //ToalsoreorderADUsbeforestreaming,uncommentthefollowing:
  46. //#defineINTERLEAVE_ADUS1
  47. //(FormoreinformationaboutADUsandinterleaving,
  48. //see<http://www.live555.com/rtp-mp3/>)
  49. BooleanuseADUs=False;
  50. Interleaving*interleaving=NULL;
  51. #ifdefSTREAM_USING_ADUS
  52. useADUs=True;
  53. #ifdefINTERLEAVE_ADUS
  54. unsignedcharinterleaveCycle[]={0,2,1,3};//orchooseyourown...
  55. unsignedconstinterleaveCycleSize
  56. =(sizeofinterleaveCycle)/(sizeof(unsignedchar));
  57. interleaving=newInterleaving(interleaveCycleSize,interleaveCycle);
  58. #endif
  59. #endif
  60. sms->addSubsession(
  61. MP3AudioFileServerMediaSubsession::createNew(env,fileName,
  62. reuseSource,useADUs,interleaving));
  63. }elseif(strcmp(extension,".mpg")==0){
  64. //AssumedtobeaMPEG-1or2ProgramStream(audio+video)file:
  65. NEW_SMS("MPEG-1or2ProgramStream");
  66. MPEG1or2FileServerDemux*demux=MPEG1or2FileServerDemux::createNew(env,
  67. fileName,reuseSource);
  68. sms->addSubsession(demux->newVideoServerMediaSubsession());
  69. sms->addSubsession(demux->newAudioServerMediaSubsession());
  70. }elseif(strcmp(extension,".ts")==0){
  71. //AssumedtobeaMPEGTransportStreamfile:
  72. //Useanindexfilenamethat'sthesameastheTSfilename,exceptwith".tsx":
  73. unsignedindexFileNameLen=strlen(fileName)+2;//allowfortrailing"x\0"
  74. char*indexFileName=newchar[indexFileNameLen];
  75. sprintf(indexFileName,"%sx",fileName);
  76. NEW_SMS("MPEGTransportStream");
  77. sms->addSubsession(
  78. MPEG2TransportFileServerMediaSubsession::createNew(env,
  79. fileName,indexFileName,reuseSource));
  80. delete[]indexFileName;
  81. }elseif(strcmp(extension,".wav")==0){
  82. //AssumedtobeaWAVAudiofile:
  83. NEW_SMS("WAVAudioStream");
  84. //Toconvert16-bitPCMdatato8-bitu-law,priortostreaming,
  85. //changethefollowingtoTrue:
  86. BooleanconvertToULaw=False;
  87. sms->addSubsession(
  88. WAVAudioFileServerMediaSubsession::createNew(env,fileName,
  89. reuseSource,convertToULaw));
  90. }elseif(strcmp(extension,".dv")==0){
  91. //AssumedtobeaDVVideofile
  92. //First,makesurethattheRTPSinks'bufferswillbelargeenoughtohandlethehugesizeofDVframes(asbigas288000).
  93. OutPacketBuffer::maxSize=300000;
  94. NEW_SMS("DVVideo");
  95. sms->addSubsession(
  96. DVVideoFileServerMediaSubsession::createNew(env,fileName,
  97. reuseSource));
  98. }elseif(strcmp(extension,".mkv")==0){
  99. //AssumedtobeaMatroskafile
  100. NEW_SMS("Matroskavideo+audio+(optional)subtitles");
  101. //CreateaMatroskafileserverdemultiplexorforthespecifiedfile.(Weentertheeventlooptowaitforthistocomplete.)
  102. newMatroskaDemuxWatchVariable=0;
  103. MatroskaFileServerDemux::createNew(env,fileName,
  104. onMatroskaDemuxCreation,NULL);
  105. env.taskScheduler().doEventLoop(&newMatroskaDemuxWatchVariable);
  106. ServerMediaSubsession*smss;
  107. while((smss=demux->newServerMediaSubsession())!=NULL){
  108. sms->addSubsession(smss);
  109. }
  110. }
  111. returnsms;
  112. }

 

可以看到NEW_SMS("AMR Audio")会创建新的ServerMediaSession,之后马上调用sms->addSubsession()为这个ServerMediaSession添加一个 ServerMediaSubSession 。看起来ServerMediaSession应该可以添加多个ServerMediaSubSession,但这里并没有这样做。如果可以添加多个 ServerMediaSubsession 那么ServerMediaSession与流名字所指定与文件是没有关系的,也就是说它不会操作文件,而文件的操作是放在 ServerMediaSubsession中的。具体应改是在ServerMediaSubsession的sdpLines()函数中打开。

 

原文地址:http://blog.csdn.net/niu_gao/article/details/6911130

live555源代码(VC6):http://download.csdn.net/detail/leixiaohua1020/6374387

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics