`

live555学习笔记-RTSPClient分析

 
阅读更多

八 RTSPClient分析

有RTSPServer,当然就要有RTSPClient。
如果按照Server端的架构,想一下Client端各部分的组成可能是这样:
因为要连接RTSP server,所以RTSPClient要有TCP socket。当获取到server端的DESCRIBE后,应建立一个对应于ServerMediaSession的ClientMediaSession。对应每个Track,ClientMediaSession中应建立ClientMediaSubsession。当建立RTP Session时,应分别为所拥有的Track发送SETUP请求连接,在获取回应后,分别为所有的track建立RTP socket,然后请求PLAY,然后开始传输数据。事实是这样吗?只能分析代码了。


testProgs中的OpenRTSP是典型的RTSPClient示例,所以分析它吧。
main()函数在playCommon.cpp文件中。main()的流程比较简单,跟服务端差别不大:建立任务计划对象--建立环境对象--处理用户输入的参数(RTSP地址)--创建RTSPClient实例--发出第一个RTSP请求(可能是OPTIONS也可能是DESCRIBE)--进入Loop。


RTSP的tcp连接是在发送第一个RTSP请求时才建立的,在RTSPClient的那几个发请求的函数sendXXXXXXCommand()中最终都调用sendRequest(),sendRequest()中会跟据情况建立起TCP连接。在建立连接时马上向任务计划中加入处理从这个TCP接收数据的socket handler:RTSPClient::incomingDataHandler()。
下面就是发送RTSP请求,OPTIONS就不必看了,从请求DESCRIBE开始:

 

[cpp]view plaincopy
 
  1. voidgetSDPDescription(RTSPClient::responseHandler*afterFunc)
  2. {
  3. ourRTSPClient->sendDescribeCommand(afterFunc,ourAuthenticator);
  4. }
  5. unsignedRTSPClient::sendDescribeCommand(responseHandler*responseHandler,
  6. Authenticator*authenticator)
  7. {
  8. if(authenticator!=NULL)
  9. fCurrentAuthenticator=*authenticator;
  10. returnsendRequest(newRequestRecord(++fCSeq,"DESCRIBE",responseHandler));
  11. }

参数responseHandler是调用者提供的回调函数,用于在处理完请求的回应后再调用之。并且在这个回调函数中会发出下一个请求--所有的请求都是这样依次发出的。使用回调函数的原因主要是因为socket的发送与接收不是同步进行的。类RequestRecord就代表一个请求,它不但保存了RTSP请求相关的信息,而且保存了请求完成后的回调函数--就是responseHandler。有些请求发出时还没建立tcp连接,不能立即发送,则加入fRequestsAwaitingConnection队列;有些发出后要等待Server端的回应,就加入fRequestsAwaitingResponse队列,当收到回应后再从队列中把它取出。
由于RTSPClient::sendRequest()太复杂,就不列其代码了,其无非是建立起RTSP请求字符串然后用TCP socket发送之。

 


现在看一下收到DESCRIBE的回应后如何处理它。理论上是跟据媒体信息建立起MediaSession了,看看是不是这样:

 

[cpp]view plaincopy
 
  1. voidcontinueAfterDESCRIBE(RTSPClient*,intresultCode,char*resultString)
  2. {
  3. char*sdpDescription=resultString;
  4. //跟据SDP创建MediaSession。
  5. //CreateamediasessionobjectfromthisSDPdescription:
  6. session=MediaSession::createNew(*env,sdpDescription);
  7. delete[]sdpDescription;
  8. //Then,setupthe"RTPSource"sforthesession:
  9. MediaSubsessionIteratoriter(*session);
  10. MediaSubsession*subsession;
  11. BooleanmadeProgress=False;
  12. charconst*singleMediumToTest=singleMedium;
  13. //循环所有的MediaSubsession,为每个设置其RTPSource的参数
  14. while((subsession=iter.next())!=NULL){
  15. //初始化subsession,在其中会建立RTP/RTCPsocket以及RTPSource。
  16. if(subsession->initiate(simpleRTPoffsetArg)){
  17. madeProgress=True;
  18. if(subsession->rtpSource()!=NULL){
  19. //Becausewe'resavingtheincomingdata,ratherthanplaying
  20. //itinrealtime,allowanespeciallylargetimethreshold
  21. //(1second)forreorderingmisorderedincomingpackets:
  22. unsignedconstthresh=1000000;//1second
  23. subsession->rtpSource()->setPacketReorderingThresholdTime(thresh);
  24. //SettheRTPsource'sOSsocketbuffersizeasappropriate-eitherifwewereexplicitlyasked(using-B),
  25. //orifthedesiredFileSinkbuffersizehappenstobelargerthanthecurrentOSsocketbuffersize.
  26. //(Thelattercaseisaheuristic,ontheassumptionthatiftheuseraskedforalargeFileSinkbuffersize,
  27. //thentheinputdataratemaybelargeenoughtojustifyincreasingtheOSsocketbuffersizealso.)
  28. intsocketNum=subsession->rtpSource()->RTPgs()->socketNum();
  29. unsignedcurBufferSize=getReceiveBufferSize(*env,socketNum);
  30. if(socketInputBufferSize>0 ||fileSinkBufferSize>curBufferSize){
  31. unsignednewBufferSize= socketInputBufferSize>0? 
  32. socketInputBufferSize:fileSinkBufferSize;
  33. newBufferSize=setReceiveBufferTo(*env,socketNum,newBufferSize);
  34. if(socketInputBufferSize>0){//Theuserexplicitlyaskedforthenewsocketbuffersize;announceit:
  35. *env
  36. <<"Changedsocketreceivebuffersizeforthe\""
  37. <<subsession->mediumName()<<"/"
  38. <<subsession->codecName()
  39. <<"\"subsessionfrom"<<curBufferSize
  40. <<"to"<<newBufferSize<<"bytes\n";
  41. }
  42. }
  43. }
  44. }
  45. }
  46. if(!madeProgress)
  47. shutdown();
  48. //Performadditional'setup'oneachsubsession,beforeplayingthem:
  49. //下一步就是发送SETUP请求了。需要为每个Track分别发送一次。
  50. setupStreams();
  51. }

此函数被删掉很多枝叶,所以发现与原版不同请不要惊掉大牙。
的确在DESCRIBE回应后建立起了MediaSession,而且我们发现Client端的MediaSession不叫ClientMediaSesson,SubSession亦不是。我现在很想看看MediaSession与MediaSubsession的建立过程:

 

 

[cpp]view plaincopy
 
  1. MediaSession*MediaSession::createNew(UsageEnvironment&env,charconst*sdpDescription)
  2. {
  3. MediaSession*newSession=newMediaSession(env);
  4. if(newSession!=NULL){
  5. if(!newSession->initializeWithSDP(sdpDescription)){
  6. deletenewSession;
  7. returnNULL;
  8. }
  9. }
  10. returnnewSession;
  11. }


我可以告诉你,MediaSession的构造函数没什么可看的,那么就来看initializeWithSDP():
内容太多,不必看了,我大体说说吧:就是处理SDP,跟据每一行来初始化一些变量。当遇到"m="行时,就建立一个MediaSubsession,然后再处理这一行之下,下一个"m="行之上的行们,用这些参数初始化MediaSubsession的变量。循环往复,直到尽头。然而这其中并没有建立RTP socket。我们发现在continueAfterDESCRIBE()中,创建MediaSession之后又调用了subsession->initiate(simpleRTPoffsetArg),那么socket是不是在它里面创建的呢?look:

 

 

[cpp]view plaincopy
 
  1. BooleanMediaSubsession::initiate(intuseSpecialRTPoffset)
  2. {
  3. if(fReadSource!=NULL)
  4. returnTrue;//hasalreadybeeninitiated
  5. do{
  6. if(fCodecName==NULL){
  7. env().setResultMsg("Codecisunspecified");
  8. break;
  9. }
  10. //创建RTP/RTCPsockets
  11. //CreateRTPandRTCP'Groupsocks'onwhichtoreceiveincomingdata.
  12. //(Groupsockswillworkevenforunicastaddresses)
  13. structin_addrtempAddr;
  14. tempAddr.s_addr=connectionEndpointAddress();
  15. //Thiscouldgetchangedlater,asaresultofaRTSP"SETUP"
  16. if(fClientPortNum!=0){
  17. //当server端指定了建议的client端口
  18. //Thesockets'portnumberswerespecifiedforus.Usethese:
  19. fClientPortNum=fClientPortNum&~1;//even
  20. if(isSSM()){
  21. fRTPSocket=newGroupsock(env(),tempAddr,fSourceFilterAddr,
  22. fClientPortNum);
  23. }else{
  24. fRTPSocket=newGroupsock(env(),tempAddr,fClientPortNum,
  25. 255);
  26. }
  27. if(fRTPSocket==NULL){
  28. env().setResultMsg("FailedtocreateRTPsocket");
  29. break;
  30. }
  31. //SetourRTCPporttobetheRTPport+1
  32. portNumBitsconstrtcpPortNum=fClientPortNum|1;
  33. if(isSSM()){
  34. fRTCPSocket=newGroupsock(env(),tempAddr,fSourceFilterAddr,
  35. rtcpPortNum);
  36. }else{
  37. fRTCPSocket=newGroupsock(env(),tempAddr,rtcpPortNum,255);
  38. }
  39. if(fRTCPSocket==NULL){
  40. chartmpBuf[100];
  41. sprintf(tmpBuf,"FailedtocreateRTCPsocket(port%d)",
  42. rtcpPortNum);
  43. env().setResultMsg(tmpBuf);
  44. break;
  45. }
  46. }else{
  47. //Server端没有指定client端口,我们自己找一个。之所以做的这样复杂,是为了能找到连续的两个端口
  48. //RTP/RTCP的端口号不是要连续吗?还记得不?
  49. //Portnumberswerenotspecifiedinadvance,soweuseephemeralportnumbers.
  50. //Createsocketsuntilwegetaport-numberpair(even:RTP;even+1:RTCP).
  51. //Weneedtomakesurethatwedon'tkeeptryingtousethesamebadportnumbersoverandoveragain.
  52. //sowestorebadsocketsinatable,anddeletethemallwhenwe'redone.
  53. HashTable*socketHashTable=HashTable::create(ONE_WORD_HASH_KEYS);
  54. if(socketHashTable==NULL)
  55. break;
  56. Booleansuccess=False;
  57. NoReusedummy;//ensuresthatournewephemeralportnumberwon'tbeonethat'salreadyinuse
  58. while(1){
  59. //Createanewsocket:
  60. if(isSSM()){
  61. fRTPSocket=newGroupsock(env(),tempAddr,
  62. fSourceFilterAddr,0);
  63. }else{
  64. fRTPSocket=newGroupsock(env(),tempAddr,0,255);
  65. }
  66. if(fRTPSocket==NULL){
  67. env().setResultMsg(
  68. "MediaSession::initiate():unabletocreateRTPandRTCPsockets");
  69. break;
  70. }
  71. //Gettheclientportnumber,andcheckwhetherit'seven(forRTP):
  72. PortclientPort(0);
  73. if(!getSourcePort(env(),fRTPSocket->socketNum(),
  74. clientPort)){
  75. break;
  76. }
  77. fClientPortNum=ntohs(clientPort.num());
  78. if((fClientPortNum&1)!=0){//it'sodd
  79. //Recordthissocketinourtable,andkeeptrying:
  80. unsignedkey=(unsigned)fClientPortNum;
  81. Groupsock*existing=(Groupsock*)socketHashTable->Add(
  82. (charconst*)key,fRTPSocket);
  83. deleteexisting;//incaseitwasn'tNULL
  84. continue;
  85. }
  86. //Makesurewecanusethenext(i.e.,odd)portnumber,forRTCP:
  87. portNumBitsrtcpPortNum=fClientPortNum|1;
  88. if(isSSM()){
  89. fRTCPSocket=newGroupsock(env(),tempAddr,
  90. fSourceFilterAddr,rtcpPortNum);
  91. }else{
  92. fRTCPSocket=newGroupsock(env(),tempAddr,rtcpPortNum,
  93. 255);
  94. }
  95. if(fRTCPSocket!=NULL&&fRTCPSocket->socketNum()>=0){
  96. //Success!Usethesetwosockets.
  97. success=True;
  98. break;
  99. }else{
  100. //Wecouldn'tcreatetheRTCPsocket(perhapsthatportnumber'salreadyinuseelsewhere?).
  101. deletefRTCPSocket;
  102. //Recordthefirstsocketinourtable,andkeeptrying:
  103. unsignedkey=(unsigned)fClientPortNum;
  104. Groupsock*existing=(Groupsock*)socketHashTable->Add(
  105. (charconst*)key,fRTPSocket);
  106. deleteexisting;//incaseitwasn'tNULL
  107. continue;
  108. }
  109. }
  110. //Cleanupthesockethashtable(andcontents):
  111. Groupsock*oldGS;
  112. while((oldGS=(Groupsock*)socketHashTable->RemoveNext())!=NULL){
  113. deleteoldGS;
  114. }
  115. deletesocketHashTable;
  116. if(!success)
  117. break;//afatalerroroccurredtryingtocreatetheRTPandRTCPsockets;wecan'tcontinue
  118. }
  119. //TrytouseabigreceivebufferforRTP-atleast0.1secondof
  120. //specifiedbandwidthandatleast50KB
  121. unsignedrtpBufSize=fBandwidth*25/2;//1kbps*0.1s=12.5bytes
  122. if(rtpBufSize<50*1024)
  123. rtpBufSize=50*1024;
  124. increaseReceiveBufferTo(env(),fRTPSocket->socketNum(),rtpBufSize);
  125. //ASSERT:fRTPSocket!=NULL&&fRTCPSocket!=NULL
  126. if(isSSM()){
  127. //SpecialcaseforRTCPSSM:SendRTCPpacketsbacktothesourceviaunicast:
  128. fRTCPSocket->changeDestinationParameters(fSourceFilterAddr,0,~0);
  129. }
  130. //创建RTPSource的地方
  131. //Create"fRTPSource"and"fReadSource":
  132. if(!createSourceObjects(useSpecialRTPoffset))
  133. break;
  134. if(fReadSource==NULL){
  135. env().setResultMsg("Failedtocreatereadsource");
  136. break;
  137. }
  138. //Finally,createourRTCPinstance.(Itstartsrunningautomatically)
  139. if(fRTPSource!=NULL){
  140. //Ifbandwidthisspecified,useitandadd5%forRTCPoverhead.
  141. //Otherwisemakeaguessat500kbps.
  142. unsignedtotSessionBandwidth=
  143. fBandwidth?fBandwidth+fBandwidth/20:500;
  144. fRTCPInstance=RTCPInstance::createNew(env(),fRTCPSocket,
  145. totSessionBandwidth,(unsignedcharconst*)fParent.CNAME(),
  146. NULL/*we'reaclient*/,fRTPSource);
  147. if(fRTCPInstance==NULL){
  148. env().setResultMsg("FailedtocreateRTCPinstance");
  149. break;
  150. }
  151. }
  152. returnTrue;
  153. }while(0);
  154. //失败时执行到这里
  155. deletefRTPSocket;
  156. fRTPSocket=NULL;
  157. deletefRTCPSocket;
  158. fRTCPSocket=NULL;
  159. Medium::close(fRTCPInstance);
  160. fRTCPInstance=NULL;
  161. Medium::close(fReadSource);
  162. fReadSource=fRTPSource=NULL;
  163. fClientPortNum=0;
  164. returnFalse;
  165. }

是的,在其中创建了RTP/RTCP socket并创建了RTPSource,创建RTPSource在函数createSourceObjects()中,看一下:

 

 

[cpp]view plaincopy
 
  1. BooleanMediaSubsession::createSourceObjects(intuseSpecialRTPoffset)
  2. {
  3. do{
  4. //First,check"fProtocolName"
  5. if(strcmp(fProtocolName,"UDP")==0){
  6. //AUDP-packetizedstream(*not*aRTPstream)
  7. fReadSource=BasicUDPSource::createNew(env(),fRTPSocket);
  8. fRTPSource=NULL;//Note!
  9. if(strcmp(fCodecName,"MP2T")==0){//MPEG-2TransportStream
  10. fReadSource=MPEG2TransportStreamFramer::createNew(env(),
  11. fReadSource);
  12. //thissets"durationInMicroseconds"correctly,basedonthePCRvalues
  13. }
  14. }else{
  15. //Check"fCodecName"againstthesetofcodecsthatwesupport,
  16. //andcreateourRTPsourceaccordingly
  17. //(Latermakethiscodemoreefficient,asthissetgrows#####)
  18. //(Also,addmorefmtsthatcanbeimplementedbySimpleRTPSource#####)
  19. BooleancreateSimpleRTPSource=False;//bydefault;canbechangedbelow
  20. BooleandoNormalMBitRule=False;//defaultbehaviorif"createSimpleRTPSource"isTrue
  21. if(strcmp(fCodecName,"QCELP")==0){//QCELPaudio
  22. fReadSource=QCELPAudioRTPSource::createNew(env(),fRTPSocket,
  23. fRTPSource,fRTPPayloadFormat,fRTPTimestampFrequency);
  24. //NotethatfReadSourcewilldifferfromfRTPSourceinthiscase
  25. }elseif(strcmp(fCodecName,"AMR")==0){//AMRaudio(narrowband)
  26. fReadSource=AMRAudioRTPSource::createNew(env(),fRTPSocket,
  27. fRTPSource,fRTPPayloadFormat,0/*isWideband*/,
  28. fNumChannels,fOctetalign,fInterleaving,
  29. fRobustsorting,fCRC);
  30. //NotethatfReadSourcewilldifferfromfRTPSourceinthiscase
  31. }elseif(strcmp(fCodecName,"AMR-WB")==0){//AMRaudio(wideband)
  32. fReadSource=AMRAudioRTPSource::createNew(env(),fRTPSocket,
  33. fRTPSource,fRTPPayloadFormat,1/*isWideband*/,
  34. fNumChannels,fOctetalign,fInterleaving,
  35. fRobustsorting,fCRC);
  36. //NotethatfReadSourcewilldifferfromfRTPSourceinthiscase
  37. }elseif(strcmp(fCodecName,"MPA")==0){//MPEG-1or2audio
  38. fReadSource=fRTPSource=MPEG1or2AudioRTPSource::createNew(
  39. env(),fRTPSocket,fRTPPayloadFormat,
  40. fRTPTimestampFrequency);
  41. }elseif(strcmp(fCodecName,"MPA-ROBUST")==0){//robustMP3audio
  42. fRTPSource=MP3ADURTPSource::createNew(env(),fRTPSocket,
  43. fRTPPayloadFormat,fRTPTimestampFrequency);
  44. if(fRTPSource==NULL)
  45. break;
  46. //AddafilterthatdeinterleavestheADUsafterdepacketizingthem:
  47. MP3ADUdeinterleaver*deinterleaver=MP3ADUdeinterleaver::createNew(
  48. env(),fRTPSource);
  49. if(deinterleaver==NULL)
  50. break;
  51. //AddanotherfilterthatconvertstheseADUstoMP3frames:
  52. fReadSource=MP3FromADUSource::createNew(env(),deinterleaver);
  53. }elseif(strcmp(fCodecName,"X-MP3-DRAFT-00")==0){
  54. //anon-standardvariantof"MPA-ROBUST"usedbyRealNetworks
  55. //(one'ADU'izedMP3frameperpacket;noheaders)
  56. fRTPSource=SimpleRTPSource::createNew(env(),fRTPSocket,
  57. fRTPPayloadFormat,fRTPTimestampFrequency,
  58. "audio/MPA-ROBUST"/*hack*/);
  59. if(fRTPSource==NULL)
  60. break;
  61. //AddafilterthatconvertstheseADUstoMP3frames:
  62. fReadSource=MP3FromADUSource::createNew(env(),fRTPSource,
  63. False/*noADUheader*/);
  64. }elseif(strcmp(fCodecName,"MP4A-LATM")==0){//MPEG-4LATMaudio
  65. fReadSource=fRTPSource=MPEG4LATMAudioRTPSource::createNew(
  66. env(),fRTPSocket,fRTPPayloadFormat,
  67. fRTPTimestampFrequency);
  68. }elseif(strcmp(fCodecName,"AC3")==0
  69. ||strcmp(fCodecName,"EAC3")==0){//AC3audio
  70. fReadSource=fRTPSource=AC3AudioRTPSource::createNew(env(),
  71. fRTPSocket,fRTPPayloadFormat,fRTPTimestampFrequency);
  72. }elseif(strcmp(fCodecName,"MP4V-ES")==0){//MPEG-4ElemStrvid
  73. fReadSource=fRTPSource=MPEG4ESVideoRTPSource::createNew(
  74. env(),fRTPSocket,fRTPPayloadFormat,
  75. fRTPTimestampFrequency);
  76. }elseif(strcmp(fCodecName,"MPEG4-GENERIC")==0){
  77. fReadSource=fRTPSource=MPEG4GenericRTPSource::createNew(
  78. env(),fRTPSocket,fRTPPayloadFormat,
  79. fRTPTimestampFrequency,fMediumName,fMode,fSizelength,
  80. fIndexlength,fIndexdeltalength);
  81. }elseif(strcmp(fCodecName,"MPV")==0){//MPEG-1or2video
  82. fReadSource=fRTPSource=MPEG1or2VideoRTPSource::createNew(
  83. env(),fRTPSocket,fRTPPayloadFormat,
  84. fRTPTimestampFrequency);
  85. }elseif(strcmp(fCodecName,"MP2T")==0){//MPEG-2TransportStream
  86. fRTPSource=SimpleRTPSource::createNew(env(),fRTPSocket,
  87. fRTPPayloadFormat,fRTPTimestampFrequency,"video/MP2T",
  88. 0,False);
  89. fReadSource=MPEG2TransportStreamFramer::createNew(env(),
  90. fRTPSource);
  91. //thissets"durationInMicroseconds"correctly,basedonthePCRvalues
  92. }elseif(strcmp(fCodecName,"H261")==0){//H.261
  93. fReadSource=fRTPSource=H261VideoRTPSource::createNew(env(),
  94. fRTPSocket,fRTPPayloadFormat,fRTPTimestampFrequency);
  95. }elseif(strcmp(fCodecName,"H263-1998")==0
  96. ||strcmp(fCodecName,"H263-2000")==0){//H.263+
  97. fReadSource=fRTPSource=H263plusVideoRTPSource::createNew(
  98. env(),fRTPSocket,fRTPPayloadFormat,
  99. fRTPTimestampFrequency);
  100. }elseif(strcmp(fCodecName,"H264")==0){
  101. fReadSource=fRTPSource=H264VideoRTPSource::createNew(env(),
  102. fRTPSocket,fRTPPayloadFormat,fRTPTimestampFrequency);
  103. }elseif(strcmp(fCodecName,"DV")==0){
  104. fReadSource=fRTPSource=DVVideoRTPSource::createNew(env(),
  105. fRTPSocket,fRTPPayloadFormat,fRTPTimestampFrequency);
  106. }elseif(strcmp(fCodecName,"JPEG")==0){//motionJPEG
  107. fReadSource=fRTPSource=JPEGVideoRTPSource::createNew(env(),
  108. fRTPSocket,fRTPPayloadFormat,fRTPTimestampFrequency,
  109. videoWidth(),videoHeight());
  110. }elseif(strcmp(fCodecName,"X-QT")==0
  111. ||strcmp(fCodecName,"X-QUICKTIME")==0){
  112. //GenericQuickTimestreams,asdefinedin
  113. //<http://developer.apple.com/quicktime/icefloe/dispatch026.html>
  114. char*mimeType=newchar[strlen(mediumName())
  115. +strlen(codecName())+2];
  116. sprintf(mimeType,"%s/%s",mediumName(),codecName());
  117. fReadSource=fRTPSource=QuickTimeGenericRTPSource::createNew(
  118. env(),fRTPSocket,fRTPPayloadFormat,
  119. fRTPTimestampFrequency,mimeType);
  120. delete[]mimeType;
  121. }elseif(strcmp(fCodecName,"PCMU")==0//PCMu-lawaudio
  122. ||strcmp(fCodecName,"GSM")==0//GSMaudio
  123. ||strcmp(fCodecName,"DVI4")==0//DVI4(IMAADPCM)audio
  124. ||strcmp(fCodecName,"PCMA")==0//PCMa-lawaudio
  125. ||strcmp(fCodecName,"MP1S")==0//MPEG-1SystemStream
  126. ||strcmp(fCodecName,"MP2P")==0//MPEG-2ProgramStream
  127. ||strcmp(fCodecName,"L8")==0//8-bitlinearaudio
  128. ||strcmp(fCodecName,"L16")==0//16-bitlinearaudio
  129. ||strcmp(fCodecName,"L20")==0//20-bitlinearaudio(RFC3190)
  130. ||strcmp(fCodecName,"L24")==0//24-bitlinearaudio(RFC3190)
  131. ||strcmp(fCodecName,"G726-16")==0//G.726,16kbps
  132. ||strcmp(fCodecName,"G726-24")==0//G.726,24kbps
  133. ||strcmp(fCodecName,"G726-32")==0//G.726,32kbps
  134. ||strcmp(fCodecName,"G726-40")==0//G.726,40kbps
  135. ||strcmp(fCodecName,"SPEEX")==0//SPEEXaudio
  136. ||strcmp(fCodecName,"T140")==0//T.140text(RFC4103)
  137. ||strcmp(fCodecName,"DAT12")==0//12-bitnonlinearaudio(RFC3190)
  138. ){
  139. createSimpleRTPSource=True;
  140. useSpecialRTPoffset=0;
  141. }elseif(useSpecialRTPoffset>=0){
  142. //Wedon'tknowthisRTPpayloadformat,buttrytoreceive
  143. //itusinga'SimpleRTPSource'withthespecifiedheaderoffset:
  144. createSimpleRTPSource=True;
  145. }else{
  146. env().setResultMsg(
  147. "RTPpayloadformatunknownornotsupported");
  148. break;
  149. }
  150. if(createSimpleRTPSource){
  151. char*mimeType=newchar[strlen(mediumName())
  152. +strlen(codecName())+2];
  153. sprintf(mimeType,"%s/%s",mediumName(),codecName());
  154. fReadSource=fRTPSource=SimpleRTPSource::createNew(env(),
  155. fRTPSocket,fRTPPayloadFormat,fRTPTimestampFrequency,
  156. mimeType,(unsigned)useSpecialRTPoffset,
  157. doNormalMBitRule);
  158. delete[]mimeType;
  159. }
  160. }
  161. returnTrue;
  162. }while(0);
  163. returnFalse;//anerroroccurred
  164. }

可以看到,这个函数里主要是跟据前面分析出的媒体和传输信息建立合适的Source。

 

socket建立了,Source也创建了,下一步应该是连接Sink,形成一个流。到此为止还未看到Sink的影子,应该是在下一步SETUP中建立,我们看到在continueAfterDESCRIBE()的最后调用了setupStreams(),那么就来探索一下setupStreams():

 

[cpp]view plaincopy
 
  1. voidsetupStreams()
  2. {
  3. staticMediaSubsessionIterator*setupIter=NULL;
  4. if(setupIter==NULL)
  5. setupIter=newMediaSubsessionIterator(*session);
  6. //每次调用此函数只为一个Subsession发出SETUP请求。
  7. while((subsession=setupIter->next())!=NULL){
  8. //Wehaveanothersubsessionlefttosetup:
  9. if(subsession->clientPortNum()==0)
  10. continue;//port#wasnotset
  11. //为一个Subsession发送SETUP请求。请求处理完成时调用continueAfterSETUP(),
  12. //continueAfterSETUP()又调用了setupStreams(),在此函数中为下一个SubSession发送SETUP请求。
[cpp]view plaincopy
 
  1. <spanstyle="white-space:pre"></span>//直到处理完所有的SubSession
  2. setupSubsession(subsession,streamUsingTCP,continueAfterSETUP);
  3. return;
  4. }
  5. //执行到这里时,已循环完所有的SubSession了
  6. //We'redonesettingupsubsessions.
  7. deletesetupIter;
  8. if(!madeProgress)
  9. shutdown();
  10. //创建输出文件,看来是在这里创建Sink了。创建sink后,就开始播放它。这个播放应该只是把socket的handler加入到
  11. //计划任务中,而没有数据的接收或发送。只有等到发出PLAY请求后才有数据的收发。
  12. //Createoutputfiles:
  13. if(createReceivers){
  14. if(outputQuickTimeFile){
  15. //Createa"QuickTimeFileSink",towriteto'stdout':
  16. qtOut=QuickTimeFileSink::createNew(*env,*session,"stdout",
  17. fileSinkBufferSize,movieWidth,movieHeight,movieFPS,
  18. packetLossCompensate,syncStreams,generateHintTracks,
  19. generateMP4Format);
  20. if(qtOut==NULL){
  21. *env<<"FailedtocreateQuickTimefilesinkforstdout:"
  22. <<env->getResultMsg();
  23. shutdown();
  24. }
  25. qtOut->startPlaying(sessionAfterPlaying,NULL);
  26. }elseif(outputAVIFile){
  27. //Createan"AVIFileSink",towriteto'stdout':
  28. aviOut=AVIFileSink::createNew(*env,*session,"stdout",
  29. fileSinkBufferSize,movieWidth,movieHeight,movieFPS,
  30. packetLossCompensate);
  31. if(aviOut==NULL){
  32. *env<<"FailedtocreateAVIfilesinkforstdout:"
  33. <<env->getResultMsg();
  34. shutdown();
  35. }
  36. aviOut->startPlaying(sessionAfterPlaying,NULL);
  37. }else{
  38. //Createandstart"FileSink"sforeachsubsession:
  39. madeProgress=False;
  40. MediaSubsessionIteratoriter(*session);
  41. while((subsession=iter.next())!=NULL){
  42. if(subsession->readSource()==NULL)
  43. continue;//wasnotinitiated
  44. //Createanoutputfileforeachdesiredstream:
  45. charoutFileName[1000];
  46. if(singleMedium==NULL){
  47. //Outputfilenameis
  48. //"<filename-prefix><medium_name>-<codec_name>-<counter>"
  49. staticunsignedstreamCounter=0;
  50. snprintf(outFileName,sizeofoutFileName,"%s%s-%s-%d",
  51. fileNamePrefix,subsession->mediumName(),
  52. subsession->codecName(),++streamCounter);
  53. }else{
  54. sprintf(outFileName,"stdout");
  55. }
  56. FileSink*fileSink;
  57. if(strcmp(subsession->mediumName(),"audio")==0
  58. &&(strcmp(subsession->codecName(),"AMR")==0
  59. ||strcmp(subsession->codecName(),"AMR-WB")
  60. ==0)){
  61. //ForAMRaudiostreams,weuseaspecialsinkthatinsertsAMRframehdrs:
  62. fileSink=AMRAudioFileSink::createNew(*env,outFileName,
  63. fileSinkBufferSize,oneFilePerFrame);
  64. }elseif(strcmp(subsession->mediumName(),"video")==0
  65. &&(strcmp(subsession->codecName(),"H264")==0)){
  66. //ForH.264videostream,weuseaspecialsinkthatinsertstart_codes:
  67. fileSink=H264VideoFileSink::createNew(*env,outFileName,
  68. subsession->fmtp_spropparametersets(),
  69. fileSinkBufferSize,oneFilePerFrame);
  70. }else{
  71. //Normalcase:
  72. fileSink=FileSink::createNew(*env,outFileName,
  73. fileSinkBufferSize,oneFilePerFrame);
  74. }
  75. subsession->sink=fileSink;
  76. if(subsession->sink==NULL){
  77. *env<<"FailedtocreateFileSinkfor\""<<outFileName
  78. <<"\":"<<env->getResultMsg()<<"\n";
  79. }else{
  80. if(singleMedium==NULL){
  81. *env<<"Createdoutputfile:\""<<outFileName
  82. <<"\"\n";
  83. }else{
  84. *env<<"Outputtingdatafromthe\""
  85. <<subsession->mediumName()<<"/"
  86. <<subsession->codecName()
  87. <<"\"subsessionto'stdout'\n";
  88. }
  89. if(strcmp(subsession->mediumName(),"video")==0
  90. &&strcmp(subsession->codecName(),"MP4V-ES")==0&&
  91. subsession->fmtp_config()!=NULL){
  92. //ForMPEG-4videoRTPstreams,the'config'information
  93. //fromtheSDPdescriptioncontainsusefulVOLetc.headers.
  94. //Insertthisdataatthefrontoftheoutputfile:
  95. unsignedconfigLen;
  96. unsignedchar*configData
  97. =parseGeneralConfigStr(subsession->fmtp_config(),configLen);
  98. structtimevaltimeNow;
  99. gettimeofday(&timeNow,NULL);
  100. fileSink->addData(configData,configLen,timeNow);
  101. delete[]configData;
  102. }
  103. //开始传输
  104. subsession->sink->startPlaying(*(subsession->readSource()),
  105. subsessionAfterPlaying,subsession);
  106. //AlsosetahandlertobecalledifaRTCP"BYE"arrives
  107. //forthissubsession:
  108. if(subsession->rtcpInstance()!=NULL){
  109. subsession->rtcpInstance()->setByeHandler(
  110. subsessionByeHandler,subsession);
  111. }
  112. madeProgress=True;
  113. }
  114. }
  115. if(!madeProgress)
  116. shutdown();
  117. }
  118. }
  119. //Finally,startplayingeachsubsession,tostartthedataflow:
  120. if(duration==0){
  121. if(scale>0)
  122. duration=session->playEndTime()-initialSeekTime;//useSDPendtime
  123. elseif(scale<0)
  124. duration=initialSeekTime;
  125. }
  126. if(duration<0)
  127. duration=0.0;
  128. endTime=initialSeekTime;
  129. if(scale>0){
  130. if(duration<=0)
  131. endTime=-1.0f;
  132. else
  133. endTime=initialSeekTime+duration;
  134. }else{
  135. endTime=initialSeekTime-duration;
  136. if(endTime<0)
  137. endTime=0.0f;
  138. }
  139. //发送PLAY请求,之后才能从Server端接收数据
  140. startPlayingSession(session,initialSeekTime,endTime,scale,
  141. continueAfterPLAY);
  142. }

仔细看看注释,应很容易了解此函数。

 

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

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

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics