live555源码分析---- DESCRIBE命令处理

来源:互联网 发布:2016年php前景 编辑:程序博客网 时间:2024/05/08 07:08
live555 DESCRIBE命令处理比较复杂,详细的处理过程如下:
  1. void RTSPServer::RTSPClientSession  
  2. ::handleCmd_DESCRIBE(char const* cseq,  
  3.              char const* urlPreSuffix, char const* urlSuffix,  
  4.              char const* fullRequestStr) {  
  5. ...  
  6.     //权限验证,默认返回真        
  7.     if (!authenticationOK("DESCRIBE", cseq, urlTotalSuffix, fullRequestStr)) break;  
  8.       
  9.     // We should really check that the request contains an "Accept:" #####  
  10.     // for "application/sdp", because that's what we're sending back #####  
  11.       
  12.     //查找资源对应的session(1.1)  
  13.     // Begin by looking up the "ServerMediaSession" object for the specified "urlTotalSuffix":  
  14.     ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlTotalSuffix);  
  15. ...  
  16.     //获取SDP信息(1.2)  
  17.     // Then, assemble a SDP description for this session:  
  18.     sdpDescription = session->generateSDPDescription();  
  19. ...  
  20.       
  21.     //组装响应包  
  22.     // Also, generate our RTSP URL, for the "Content-Base:" header  
  23.     // (which is necessary to ensure that the correct URL gets used in  
  24.     // subsequent "SETUP" requests).  
  25.     rtspURL = fOurServer.rtspURL(session, fClientInputSocket);  
  26.       
  27.     snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,  
  28.          "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"  
  29.          "%s"  
  30.          "Content-Base: %s/\r\n"  
  31.          "Content-Type: application/sdp\r\n"  
  32.          "Content-Length: %d\r\n\r\n"  
  33.          "%s",  
  34.          cseq,  
  35.          dateHeader(),  
  36.          rtspURL,  
  37.          sdpDescriptionSize,  
  38.          sdpDescription);  
  39.   } while (0);  
  40. ...  
  41. }  
复制代码

调用DynamicRTSPServer::lookupServerMediaSession查找对应资源(URl)的session,  这个过程将实例化服务过程中所需的对像,sink,source等。接着调用session的generateSDPDescription,生成SDP信息。SDP很可能需要从文件中获取部分信息,所以在这其中必定有读文件的部分。然后就是组装响应包了。
2.查找资源对应的session(1.1)

  1. ServerMediaSession*  
  2. DynamicRTSPServer::lookupServerMediaSession(char const* streamName) {  
  3.   // First, check whether the specified "streamName" exists as a local file:  
  4.   FILE* fid = fopen(streamName, "rb");  
  5.   Boolean fileExists = fid != NULL;  
  6.   
  7.   
  8.   // Next, check whether we already have a "ServerMediaSession" for this file:  
  9.   ServerMediaSession* sms = RTSPServer::lookupServerMediaSession(streamName);   //查询对应的session  
  10.   Boolean smsExists = sms != NULL;  
  11.   
  12.   // Handle the four possibilities for "fileExists" and "smsExists":  
  13.   if (!fileExists) {  
  14.     if (smsExists) {  
  15.       // "sms" was created for a file that no longer exists. Remove it:  
  16.       removeServerMediaSession(sms);        //对应的文件已经不存在,从链表中移除session  
  17.     }  
  18.     return NULL;  
  19.   } else {  
  20.     if (!smsExists) {  
  21.       // Create a new "ServerMediaSession" object for streaming from the named file.  
  22.       sms = createNewSMS(envir(), streamName, fid);     //session不存在,则创建(2.2)  
  23.       addServerMediaSession(sms);                       //加入到链表(2.1)  
  24.     }  
  25.     fclose(fid);  
  26.     return sms;  
  27.   }  
  28. }  
复制代码

DynamicRTSPServer是RTSPServer的子类,继承关系为:DynamicRTSPServer->RTSPServerSupportingHTTPStreaming->RTSPServer

调用RTSPServer::lookupServerMediaSession,查询streamName对应的session是否存在。若不存在,则需要创建新的session,并将其加入到链表中,否则直接返回已经存在的session。


3.session的管理(2.1)

session是通过hash表存放的,由RTSPServer类进行管理,先看session查询函数的定义


  1. ServerMediaSession* RTSPServer::lookupServerMediaSession(char const* streamName) {  
  2.   return (ServerMediaSession*)(fServerMediaSessions->Lookup(streamName));  
  3. }  
复制代码

fServerMediaSessions 为RTSPServer的成员,是一个Hash表,定义为HashTable* fServerMediaSessions。

[p=26, null, left]session添加到Hash表

  1. void RTSPServer::addServerMediaSession(ServerMediaSession* serverMediaSession) {  
  2.   if (serverMediaSession == NULL) return;  
  3.   
  4.   
  5.   char const* sessionName = serverMediaSession->streamName();  
  6.   if (sessionName == NULL) sessionName = "";  
  7.   ServerMediaSession* existingSession  
  8.     = (ServerMediaSession*)(fServerMediaSessions->Add(sessionName, (void*)serverMediaSession));  
  9.   removeServerMediaSession(existingSession); // if any  
  10. }
复制代码
Hash表中已经存在对应的session,则会移除原来的session4.创建session(2.2) 由于不同的媒体类媒,需要创建不同的session,为了便于修改,创建session的代码被放到一个独立的函数createNewSMS中
  1. static ServerMediaSession* createNewSMS(UsageEnvironment& env,  
  2.                     char const* fileName, FILE* /*fid*/) {  
  3.   // Use the file name extension to determine the type of "ServerMediaSession":  
  4.   char const* extension = strrchr(fileName, '.');  
  5.   if (extension == NULL) return NULL;  
  6.   
  7.   ServerMediaSession* sms = NULL;  
  8.   Boolean const reuseSource = False;  
  9.   if (strcmp(extension, ".aac") == 0) {  
  10.     // Assumed to be an AAC Audio (ADTS format) file:  
  11.     NEW_SMS("AAC Audio");  
  12.     sms->addSubsession(ADTSAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));  
  13.   } else if (strcmp(extension, ".amr") == 0) {  
  14.     // Assumed to be an AMR Audio file:  
  15.     NEW_SMS("AMR Audio");  
  16.     sms->addSubsession(AMRAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));  
  17.   } else if (strcmp(extension, ".ac3") == 0) {  
  18.     // Assumed to be an AC-3 Audio file:  
  19.     NEW_SMS("AC-3 Audio");  
  20.     sms->addSubsession(AC3AudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));  
  21.   } else if (strcmp(extension, ".m4e") == 0) {  
  22.     // Assumed to be a MPEG-4 Video Elementary Stream file:  
  23.     NEW_SMS("MPEG-4 Video");  
  24.     sms->addSubsession(MPEG4VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));  
  25.   } else if (strcmp(extension, ".264") == 0) {  
  26.     // Assumed to be a H.264 Video Elementary Stream file:  
  27.     NEW_SMS("H.264 Video");  
  28.     OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.264 frames  
  29.     sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));  
  30.   }   
  31. ...  
  32. else if (strcmp(extension, ".mpg") == 0) {  
  33.     // Assumed to be a MPEG-1 or 2 Program Stream (audio+video) file:  
  34.     NEW_SMS("MPEG-1 or 2 Program Stream");  
  35.     MPEG1or2FileServerDemux* demux  
  36.       = MPEG1or2FileServerDemux::createNew(env, fileName, reuseSource);  
  37.     sms->addSubsession(demux->newVideoServerMediaSubsession());  
  38.     sms->addSubsession(demux->newAudioServerMediaSubsession());  
  39.   }  
  40. //其它媒体格式  
  41. ...  
  42.   return sms;  
  43. }  
复制代码

session的创建被隐藏在宏NEW_SMS里,NEW_SMS定义如下:
  1. #define NEW_SMS(description) do {\  
  2. char const* descStr = description\  
  3.     ", streamed by the LIVE555 Media Server";\  
  4. sms = ServerMediaSession::createNew(env, fileName, fileName, descStr);\  
  5. } while(0)
复制代码

ServerMediaSession::createNew将实例化一个ServerMediaSession对象
5.创建subsession(4)

live555支持的复合容器类型只有*.mpg、*.mkv、webm,可以看到程序为容器中的每一个流建立一个subseesion,然后通过ServerMediaSession::addSubsession函数,将subsession 加入到ServerMediaSession。



先来看subsession是如何管理的


  1. Boolean  
  2. ServerMediaSession::addSubsession(ServerMediaSubsession* subsession) {  
  3.   if (subsession->fParentSession != NULL) return False; // it's already used  
  4.   
  5.   if (fSubsessionsTail == NULL) {  
  6.     fSubsessionsHead = subsession;  
  7.   } else {  
  8.     fSubsessionsTail->fNext = subsession;  
  9.   }  
  10.   fSubsessionsTail = subsession;  
  11.   
  12.   
  13.   subsession->fParentSession = this;  
  14.   subsession->fTrackNumber = ++fSubsessionCounter;  
  15.   return True;
复制代码

从上面的代码中看到,ServerMediaSession 中记录了subsession的链表

从(4)中可以看到,每一种类型的媒体流都有自己的subsession实现,(4)中实例化一个H264的session代码为


  1. H264VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource)  
复制代码

这里有reuseSource参数,,表示将重用source,但其默认值为False。

继承关系:H264VideoFileServerMediaSubsession->FileServerMediaSubsession->OnDemandServerMediaSubsession->ServerMediaSubsession->Medium


6.获取SDP信息(1.2)
  1. char* ServerMediaSession::generateSDPDescription() {  
  2. ...  
  3.   
  4.   char* rangeLine = NULL; // for now  
  5.   char* sdp = NULL; // for now  
  6.   
  7.   do {  
  8. ...  
  9.     // Unless subsessions have differing durations, we also have a "a=range:" line:  
  10.     float dur = duration();  
  11.     if (dur == 0.0) {  
  12.       rangeLine = strDup("a=range:npt=0-\r\n");  
  13.     } else if (dur > 0.0) {  
  14.       char buf[100];  
  15.       sprintf(buf, "a=range:npt=0-%.3f\r\n", dur);  
  16.       rangeLine = strDup(buf);  
  17.     } else { // subsessions have differing durations, so "a=range:" lines go there  
  18.       rangeLine = strDup("");  
  19.     }  
  20.   
  21.     char const* const sdpPrefixFmt =  
  22.       "v=0\r\n"  
  23.       "o=- %ld%06ld %d IN IP4 %s\r\n"  
  24.       "s=%s\r\n"  
  25.       "i=%s\r\n"  
  26.       "t=0 0\r\n"  
  27.       "a=tool:%s%s\r\n"  
  28.       "a=type:broadcast\r\n"  
  29.       "a=control:*\r\n"  
  30.       "%s"  
  31.       "%s"  
  32.       "a=x-qt-text-nam:%s\r\n"  
  33.       "a=x-qt-text-inf:%s\r\n"  
  34.       "%s";  
  35.     sdpLength += strlen(sdpPrefixFmt)  
  36.       + 20 + 6 + 20 + ipAddressStrSize  
  37.       + strlen(fDescriptionSDPString)  
  38.       + strlen(fInfoSDPString)  
  39.       + strlen(libNameStr) + strlen(libVersionStr)  
  40.       + strlen(sourceFilterLine)  
  41.       + strlen(rangeLine)  
  42.       + strlen(fDescriptionSDPString)  
  43.       + strlen(fInfoSDPString)  
  44.       + strlen(fMiscSDPLines);  
  45.     sdp = new char[sdpLength];  
  46.     if (sdp == NULL) break;  
  47.   
  48.     // Generate the SDP prefix (session-level lines):  
  49.     sprintf(sdp, sdpPrefixFmt,  
  50.         fCreationTime.tv_sec, fCreationTime.tv_usec, // o= <session id>  
  51.         1, // o= <version> // (needs to change if params are modified)  
  52.         ipAddressStr, // o= <address>  
  53.         fDescriptionSDPString, // s= <description>  
  54.         fInfoSDPString, // i= <info>  
  55.         libNameStr, libVersionStr, // a=tool:  
  56.         sourceFilterLine, // a=source-filter: incl (if a SSM session)  
  57.         rangeLine, // a=range: line  
  58.         fDescriptionSDPString, // a=x-qt-text-nam: line  
  59.         fInfoSDPString, // a=x-qt-text-inf: line  
  60.         fMiscSDPLines); // miscellaneous session SDP lines (if any)  
  61.   
  62.     //生成SDP的媒体描述部分(6.1)  
  63.     // Then, add the (media-level) lines for each subsession:  
  64.     char* mediaSDP = sdp;  
  65.     for (subsession = fSubsessionsHead; subsession != NULL;  
  66.      subsession = subsession->fNext) {  
  67.       mediaSDP += strlen(mediaSDP);  
  68.       sprintf(mediaSDP, "%s", subsession->sdpLines());  
  69.     }  
  70.   } while (0);  
  71.   
  72.   delete[] rangeLine; delete[] sourceFilterLine; delete[] ipAddressStr;  
  73.   return sdp;  
  74. }
复制代码
上面的代码生成了SDP信息,其中媒体描述部分需要从subsession中获取
7.生成SDP的媒体描述部分(6.1)

sdpLines为定义在ServerMediaSubsession中的纯虚函数,在OnDemandServerMediaSubsession::sdpLines()中实现


  1. char const*  
  2. OnDemandServerMediaSubsession::sdpLines() {  
  3.   if (fSDPLines == NULL) {  
  4.     // We need to construct a set of SDP lines that describe this  
  5.     // subsession (as a unicast stream).  To do so, we first create  
  6.     // dummy (unused) source and "RTPSink" objects,  
  7.     // whose parameters we use for the SDP lines:  
  8.     unsigned estBitrate;  
  9.     FramedSource* inputSource = createNewStreamSource(0, estBitrate);   //实例化source(7.1),注意这里的第一个参数(clientSessionId)值为0  
  10.     if (inputSource == NULL) return NULL; // file not found  
  11.   
  12.     struct in_addr dummyAddr;  
  13.     dummyAddr.s_addr = 0;  
  14.     Groupsock dummyGroupsock(envir(), dummyAddr, 0, 0); //实例化一个Groupsock,用于创建RTPSink  
  15.     unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic  //这里的负载类型有什么意义呢?  
  16.     RTPSink* dummyRTPSink  
  17.       = createNewRTPSink(&dummyGroupsock, rtpPayloadType, inputSource); //实例化RTPSink(7.2)  
  18.   
  19.     setSDPLinesFromRTPSink(dummyRTPSink, inputSource, estBitrate); //从RTPSink中获取SDP信息(7.3)  
  20.     Medium::close(dummyRTPSink);    //关闭RTPSink  
  21.     closeStreamSource(inputSource); //关闭source  
  22.   }  
  23.   
  24.   return fSDPLines;  
  25. }
复制代码
这个函数代码较少,但却完成了一些重要函数的调用,创建了source、RTPSink、Groupsock等实例。
8.实例化source(7.1)这里调用的createNewStreamSource函数,定义为OnDemandServerMediaSubsession类的一个纯虚函数, 不同类型的媒体有不同的实现,对于H264而言,其实现如下


  1. FramedSource* H264VideoFileServerMediaSubsession::createNewStreamSource(unsigned /*clientSessionId*/, unsigned& estBitrate) {  
  2.   estBitrate = 500; // kbps, estimate  
  3.   
  4.   // Create the video source:  
  5.   ByteStreamFileSource* fileSource = ByteStreamFileSource::createNew(envir(), fFileName);  //创建一个字节流source,用于读取文件  
  6.   if (fileSource == NULL) return NULL;  
  7.   fFileSize = fileSource->fileSize();  
  8.   
  9.   // Create a framer for the Video Elementary Stream:  
  10.   return H264VideoStreamFramer::createNew(envir(), fileSource); //创建一个H264的Frame source  
  11. }  
复制代码

live555中直接从文件中读取, 基本上都是通过类ByteStreamFileSource进行的
最后一句创建了一个H264VideoStreamFramer实例,其继承关系:
H264VideoStreamFramer->MPEGVideoStreamFramer->FramedFilter->FramedSource->MediaSource
9.实例化RTPSink(7.2)函数createNewRTPSink被定义为OnDemandServerMediaSubsession的一个虚函数,对于H264,在H264VideoFileServerMediaSubsession类中实现




  1. RTPSink* H264VideoFileServerMediaSubsession  
  2. ::createNewRTPSink(Groupsock* rtpGroupsock,  
  3.            unsigned char rtpPayloadTypeIfDynamic,  
  4.            FramedSource* /*inputSource*/) {  
  5.   return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);  
  6. }  
复制代码

创建了一个H264VideoRTPSink实例,其继承关系:
H264VideoRTPSink->VideoRTPSink->MultiFramedRTPSink->RTPSink->MediaSink
10.从RTPSink中获取SDP信息(7.3)这里获取的是媒体相关的描述


  1. void OnDemandServerMediaSubsession  
  2. ::setSDPLinesFromRTPSink(RTPSink* rtpSink, FramedSource* inputSource, unsigned estBitrate) {  
  3.   if (rtpSink == NULL) return;  
  4.   
  5.   char const* mediaType = rtpSink->sdpMediaType();  
  6.   unsigned char rtpPayloadType = rtpSink->rtpPayloadType();  
  7.   struct in_addr serverAddrForSDP; serverAddrForSDP.s_addr = fServerAddressForSDP;  
  8.   char* const ipAddressStr = strDup(our_inet_ntoa(serverAddrForSDP));  
  9.   char* rtpmapLine = rtpSink->rtpmapLine();  
  10.   char const* rangeLine = rangeSDPLine();  
  11.   char const* auxSDPLine = getAuxSDPLine(rtpSink, inputSource); //可选的SDP扩展属性行(10.1)  
  12.   if (auxSDPLine == NULL) auxSDPLine = "";  
  13.   
  14.   char const* const sdpFmt =  
  15.     "m=%s %u RTP/AVP %d\r\n"  
  16.     "c=IN IP4 %s\r\n"  
  17.     "b=AS:%u\r\n"  
  18.     "%s"  
  19.     "%s"  
  20.     "%s"  
  21.     "a=control:%s\r\n";  
  22.   unsigned sdpFmtSize = strlen(sdpFmt)  
  23.     + strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */  
  24.     + strlen(ipAddressStr)  
  25.     + 20 /* max int len */  
  26.     + strlen(rtpmapLine)  
  27.     + strlen(rangeLine)  
  28.     + strlen(auxSDPLine)  
  29.     + strlen(trackId());  
  30.   char* sdpLines = new char[sdpFmtSize];  
  31.   sprintf(sdpLines, sdpFmt,  
  32.       mediaType, // m= <media>  
  33.       fPortNumForSDP, // m= <port>  
  34.       rtpPayloadType, // m= <fmt list>  
  35.       ipAddressStr, // c= address  
  36.       estBitrate, // b=AS:<bandwidth>  
  37.       rtpmapLine, // a=rtpmap:... (if present)  
  38.       rangeLine, // a=range:... (if present)  
  39.       auxSDPLine, // optional extra SDP line  
  40.       trackId()); // a=control:<track-id>  
  41.   delete[] (char*)rangeLine; delete[] rtpmapLine; delete[] ipAddressStr;  
  42.   
  43.   
  44.   fSDPLines = strDup(sdpLines);  
  45.   delete[] sdpLines;  
  46. }  
复制代码

上面的代码中,要注意的是获取SDP的扩展行属性,与具体的媒体相关。SDP中的媒体描述说明如下:m = (媒体名称和传输地址)
i = * (媒体标题)
c = * (连接信息 — 如果包含在会话层则该字段可选)
b = * (带宽信息)
k = * (加密密钥)
a = * (0 个或多个会话属性行)
11.获取特定媒体的可选的SDP扩展属性行(10.1)
  1. char const* OnDemandServerMediaSubsession  
  2. ::getAuxSDPLine(RTPSink* rtpSink, FramedSource* /*inputSource*/) {  
  3.   // Default implementation:  
  4.   return rtpSink == NULL ? NULL : rtpSink->auxSDPLine();  
  5. }  
复制代码
RTPSink::auxSDPLine()中的默认实现返回空,如下
  1. char const* RTPSink::auxSDPLine() {  
  2.   return NULL; // by default  
  3. }  
复制代码

对于H264,在H264VideoRTPSink中重新实现:
  1. char const* H264VideoRTPSink::auxSDPLine() {  
  2.   // Generate a new "a=fmtp:" line each time, using parameters from  
  3.   // our framer source (in case they've changed since the last time that  
  4.   // we were called):  
  5.   if (fOurFragmenter == NULL) return NULL; // we don't yet have a fragmenter (and therefore not a source)  
  6.   H264VideoStreamFramer* framerSource = (H264VideoStreamFramer*)(fOurFragmenter->inputSource());    //这里注意H264FUAFragmenter  
  7.   if (framerSource == NULL) return NULL; // we don't yet have a source  
  8.   
  9.   u_int8_t* sps; unsigned spsSize;  
  10.   u_int8_t* pps; unsigned ppsSize;  
  11.   framerSource->getSPSandPPS(sps, spsSize, pps, ppsSize);   //从H264VideoStreamFramer中获取PPS和SPS  
  12.   if (sps == NULL || pps == NULL) return NULL; // our source isn't ready  
  13.   
  14.   u_int32_t profile_level_id;  
  15.   if (spsSize < 4) { // sanity check  
  16.     profile_level_id = 0;  
  17.   } else {  
  18.     profile_level_id = (sps[1]<<16)|(sps[2]<<8)|sps[3]; // profile_idc|constraint_setN_flag|level_idc  
  19.   }  
  20.    
  21.   // Set up the "a=fmtp:" SDP line for this stream:  
  22.   char* sps_base64 = base64Encode((char*)sps, spsSize); //经过base64编码  
  23.   char* pps_base64 = base64Encode((char*)pps, ppsSize);  
  24.   char const* fmtpFmt =  
  25.     "a=fmtp:%d packetization-mode=1"  
  26.     ";profile-level-id=%06X"  
  27.     ";sprop-parameter-sets=%s,%s\r\n";  
  28.   unsigned fmtpFmtSize = strlen(fmtpFmt)  
  29.     + 3 /* max char len */  
  30.     + 6 /* 3 bytes in hex */  
  31.     + strlen(sps_base64) + strlen(pps_base64);  
  32.   char* fmtp = new char[fmtpFmtSize];  
  33.   sprintf(fmtp, fmtpFmt,  
  34.           rtpPayloadType(),  
  35.       profile_level_id,  
  36.           sps_base64, pps_base64);  
  37.   delete[] sps_base64;  
  38.   delete[] pps_base64;  
  39.   
  40.   delete[] fFmtpSDPLine; fFmtpSDPLine = fmtp;  
  41.   return fFmtpSDPLine;  
  42. }
复制代码
对于H264最重要的是PPS(图像参数集)和SPS(序列参数集),经过base64编码。上面的代码中多出了一个H264FUAFragmenter类,继承自FramedFilter,它主要是实现H264RTP的分包操作。
上面的代码中还有一个问题,用H264VideoStreamFramer::getSPSandPPS函数获取SPS与PPS,但查看H264VideoStreamFramer代码发现,其SPS、PPS默认值为空,需要从文件中读取值,显然上面的代码中没有读取文件的操作,读文件操作在哪里?
12.从H264视频文件中获取SPS、PPS(10.1)原来在H264VideoFileServerMediaSubsession中对getAuxSDPLine函数重新实现了


  1. char const* H264VideoFileServerMediaSubsession::getAuxSDPLine(RTPSink* rtpSink, FramedSource* inputSource) {  
  2.   if (fAuxSDPLine != NULL) return fAuxSDPLine; // it's already been set up (for a previous client)  
  3.   
  4.   if (fDummyRTPSink == NULL) { // we're not already setting it up for another, concurrent stream  
  5.     // Note: For H264 video files, the 'config' information ("profile-level-id" and "sprop-parameter-sets") isn't known  
  6.     // until we start reading the file.  This means that "rtpSink"s "auxSDPLine()" will be NULL initially,  
  7.     // and we need to start reading data from our file until this changes.  
  8.     fDummyRTPSink = rtpSink;  
  9.   
  10.     // Start reading the file:  
  11.     fDummyRTPSink->startPlaying(*inputSource, afterPlayingDummy, this); //读取文件  
  12.   
  13.     // Check whether the sink's 'auxSDPLine()' is ready:  
  14.     checkForAuxSDPLine(this);  
  15.   }  
  16.   
  17.   envir().taskScheduler().doEventLoop(&fDoneFlag);      //进入事件循环  
  18.   
  19.   return fAuxSDPLine;  
  20. }  
复制代码

调用RTPSink::startPlaying开始播放(其实只是为了读取文件中的SPS及PPS),然后进行入事件循环,具体过各这里不再说明了,因为比较复杂,将另外讨论。
再来看一下checkForAuxSDPLine的代码:
  1. static void checkForAuxSDPLine(void* clientData) {  
  2.   H264VideoFileServerMediaSubsession* subsess = (H264VideoFileServerMediaSubsession*)clientData;  
  3.   subsess->checkForAuxSDPLine1();  
  4. }  
  5.   
  6. void H264VideoFileServerMediaSubsession::checkForAuxSDPLine1() {  
  7.   char const* dasl;  
  8.   
  9.   if (fAuxSDPLine != NULL) {  
  10.     // Signal the event loop that we're done:  
  11.     setDoneFlag();  
  12.   } else if (fDummyRTPSink != NULL && (dasl = fDummyRTPSink->auxSDPLine()) != NULL) {  
  13.     fAuxSDPLine = strDup(dasl);  
  14.     fDummyRTPSink = NULL;  
  15.   
  16.     // Signal the event loop that we're done:  
  17.     setDoneFlag();  
  18.   } else {  
  19.     // try again after a brief delay:  
  20.     int uSecsToDelay = 100000; // 100 ms  
  21.     nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecsToDelay,  
  22.                   (TaskFunc*)checkForAuxSDPLine, this);  
  23.   }  
  24. }  
复制代码

每一100ms检查一个fAuxSDPLine字符串的值
13.总结DECRIBE命令的处理过程比较复杂,这里简单的概括一下
1)创建了session及subsession,一个媒体文件将对应一个session,媒体文件中的每一个流对应一个subssion。session中,记录了一个subsession的链表。
2)为了获取SDP信息,做了大量的工作,不但创建了sink、source等实例, 还需要从媒体文件中获取信息。需要注意的是,这里创建的sink、source只是临时的,只是为了获取SDP信息而存在。
原创粉丝点击