Live555源碼分析@njzhujinhua[4]:generateSDPDescription



 [3]generateSDPDescription
有了上述知識,我們繼續看一下RTSPServer::RTSPClientConnection::handleCmd_DESCRIBE的處理.
void RTSPServer::RTSPClientConnection::handleCmd_DESCRIBE(char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr)
{
    char* sdpDescription = NULL;
    char* rtspURL = NULL;
    do {
        char urlTotalSuffix[RTSP_PARAM_STRING_MAX];
        if (strlen(urlPreSuffix) + strlen(urlSuffix) + 2 > sizeof urlTotalSuffix)
        {
            handleCmd_bad();
            break;
        }
        urlTotalSuffix[0] = '\0';
        if (urlPreSuffix[0] != '\0')
        {
            strcat(urlTotalSuffix, urlPreSuffix);
            strcat(urlTotalSuffix, "/");
        }
        strcat(urlTotalSuffix, urlSuffix);  //格式化請求流信息
        if (!authenticationOK("DESCRIBE", urlTotalSuffix, fullRequestStr)) break;  //用戶鑑權
        // We should really check that the request contains an "Accept:" #####
        // for "application/sdp", because that's what we're sending back #####  //可惜現在代碼沒校驗吧
        // Begin by looking up the "ServerMediaSession" object for the specified "urlTotalSuffix":
        ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlTotalSuffix);
//根據請求中指定的流查找ServerMediaSession會話。在目前分析的是testOnDemandRTPServer中使用的是RTSPServer::lookupServerMediaSession, 只從已創建回話中查找。而DynamicRTSPServer的重載了此函數,首先調用父類RTPServer的lookupServerMediaSession,然後檢查存在與否,如果不存在則創建一個新的。
        if (session == NULL)
        {
            handleCmd_notFound();
            break;
        }
        // Then, assemble a SDP description for this session:
        sdpDescription = session->generateSDPDescription();//獲取SDP描述信息,describe命令主要內容
        if (sdpDescription == NULL)
        {
            // This usually means that a file name that was specified for a
            // "ServerMediaSubsession" does not exist.
            setRTSPResponse("404 File Not Found, Or In Incorrect Format");
            break;
        }
        unsigned sdpDescriptionSize = strlen(sdpDescription);
        // Also, generate our RTSP URL, for the "Content-Base:" header
        // (which is necessary to ensure that the correct URL gets used in subsequent "SETUP" requests).
        rtspURL = fOurServer.rtspURL(session, fClientInputSocket); 生成Content-Base內容:RTSP URL
        snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
            "%s"
            "Content-Base: %s/\r\n"
            "Content-Type: application/sdp\r\n"
            "Content-Length: %d\r\n\r\n"
            "%s",
            fCurrentCSeq,
            dateHeader(),
            rtspURL,
            sdpDescriptionSize,
            sdpDescription);                             //拼接DESCRIBE的response
    } while (0);
    delete[] sdpDescription;
    delete[] rtspURL;
}


在對describe命令的處理過程中,主要內容一是鑑權,這個在上一篇Live555源碼分析[2]:RTSPServer中的用戶認證中講過.另一個就是返回請求流的sdp信息. 這個通過generateSDPDescription來實現


在ServerMediaSession::generateSDPDescription()中基本都是固定字符串的拼接,需要我們重點關注的是
   
 // Count the lengths of each subsession's media-level SDP lines.
    // (We do this first, because the call to "subsession->sdpLines()"
    // causes correct subsession 'duration()'s to be calculated later.)
//生成sdpLines並計算其長度,拼接過程在最後,但在此做的目的是順便調用subsession的duration接口以便返回媒體文件的長度
    unsigned sdpLength = 0;
    ServerMediaSubsession* subsession;
    for (subsession = fSubsessionsHead; subsession != NULL;
  subsession = subsession->fNext) {
      char const* sdpLines = subsession->sdpLines();   //此時返回的雖然沒有使用,但內部是保存好了,後面拼接時雖然再次調用,但也是直接返回而已。事實上,live555默認並沒有刪除用完的SMS及SMSS,因而在第二次再次請求此媒體流時,這個也不會再次生成了
      if (sdpLines == NULL) continue; // the media's not available
      sdpLength += strlen(sdpLines);
    }
    if (sdpLength == 0) break; // the session has no usable subsessions


    // Unless subsessions have differing durations, we also have a "a=range:" line:
    float dur = duration();   //返回媒體時長




OnDemandServerMediaSubsession::sdpLines()的實現如下
char const* OnDemandServerMediaSubsession::sdpLines() 
{
    if (fSDPLines == NULL) {
        // We need to construct a set of SDP lines that describe this subsession (as a unicast stream). To do so, we first create
        // dummy (unused) source and "RTPSink" objects, whose parameters we use for the SDP lines:
        unsigned estBitrate;
        FramedSource* inputSource = createNewStreamSource(0, estBitrate);
        if (inputSource == NULL) return NULL; // file not found


        struct in_addr dummyAddr;
        dummyAddr.s_addr = 0;
        Groupsock dummyGroupsock(envir(), dummyAddr, 0, 0);
        unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic
        RTPSink* dummyRTPSink
            = createNewRTPSink(&dummyGroupsock, rtpPayloadType, inputSource);
        if (dummyRTPSink != NULL && dummyRTPSink->estimatedBitrate() > 0) estBitrate = dummyRTPSink->estimatedBitrate();


        setSDPLinesFromRTPSink(dummyRTPSink, inputSource, estBitrate);
        Medium::close(dummyRTPSink);
        closeStreamSource(inputSource);
    }


    return fSDPLines;
}


其中createNewStreamSource與createNewRTPSink均爲虛函數,由具體ServerMediaSubsession負責實現。
其中FramedSource的子類FramedFilter提供一種抽象數據源的功能,其繼承關係MediaSource->FramedSource->FramedFilter, 由其屏蔽不同類型數據源的區別。RTPSource則是負責RTP協議報文封裝之類的處理了
創建完臨時的Source和Sink之後, 從Sink中獲取SDPLine, 其中有的媒體類型讀取文件頭即可, 有的需要先播放一段視頻,如H.264。。。以此才能正確的獲取到SDP信息...具體細節還沒研究,



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章