Exemple #1
0
void RTSPRequest::ParseTransportOptionsHeader()
{
    StringParser theRTPOptionsParser(fHeaderDictionary.GetValue(qtssXTransportOptionsHeader));
    StrPtrLen theRTPOptionsSubHeader;

    do
    {
        static StrPtrLen sLateTolerance("late-tolerance");
        
        if (theRTPOptionsSubHeader.NumEqualIgnoreCase(sLateTolerance.Ptr, sLateTolerance.Len))
        {
            StringParser theLateTolParser(&theRTPOptionsSubHeader);
            theLateTolParser.GetThru(NULL,'=');
            theLateTolParser.ConsumeWhitespace();
            fLateTolerance = theLateTolParser.ConsumeFloat();
            fLateToleranceStr = theRTPOptionsSubHeader;
        }
        
        (void)theRTPOptionsParser.GetThru(&theRTPOptionsSubHeader, ';');
        
    } while(theRTPOptionsSubHeader.Len > 0);
}
QTSS_Error DoSetup(QTSS_StandardRTSP_Params* inParamBlock)
{
    //setup this track in the file object 
    FileSession* theFile = NULL;
    UInt32 theLen = sizeof(FileSession*);
    QTSS_Error theErr = QTSS_GetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, (void*)&theFile, &theLen);
    if ((theErr != QTSS_NoErr) || (theLen != sizeof(FileSession*)))
    {
        // This is possible, as clients are not required to send a DESCRIBE. If we haven't set
        // anything up yet, set everything up
		char* theFullPathStr = NULL;
        theErr = QTSS_GetValueAsString(inParamBlock->inRTSPRequest, qtssRTSPReqLocalPath, 0, &theFullPathStr);
        Assert(theErr == QTSS_NoErr);
		QTSSCharArrayDeleter theFullPathDeleter(theFullPathStr);
		StrPtrLen theFullPath(theFullPathStr);
        	
        if ((theFullPath.Len <= sRTPSuffix.Len) ||
            (!sRTPSuffix.NumEqualIgnoreCase(&theFullPath.Ptr[theFullPath.Len - sRTPSuffix.Len], sRTPSuffix.Len)))
            return QTSS_RequestFailed;

        theErr = CreateRTPFileSession(inParamBlock, theFullPath, &theFile);
        if (theErr != QTSS_NoErr)
            return theErr;

        // Store this newly created file object in the RTP session.
        theErr = QTSS_SetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, &theFile, sizeof(theFile));
    }

    //unless there is a digit at the end of this path (representing trackID), don't
    //even bother with the request
    char* theDigitStr = NULL;
    (void)QTSS_GetValueAsString(inParamBlock->inRTSPRequest, qtssRTSPReqFileDigit, 0, &theDigitStr);
    QTSSCharArrayDeleter theDigitStrDeleter(theDigitStr);
	if (theDigitStr == NULL)
        return QTSSModuleUtils::SendErrorResponse(inParamBlock->inRTSPRequest,
                                                    qtssClientBadRequest, sExpectedDigitFilenameErr);
    
    UInt32 theTrackID = ::strtol(theDigitStr, NULL, 10);
	
    RTPFileSession::ErrorCode qtfileErr = theFile->fFile.AddTrack(theTrackID);
    
    //if we get an error back, forward that error to the client
    if (qtfileErr == RTPFileSession::errTrackDoesntExist)
        return QTSSModuleUtils::SendErrorResponse(inParamBlock->inRTSPRequest,
                                                    qtssClientNotFound, sTrackDoesntExistErr);
    else if (qtfileErr != RTPFileSession::errNoError)
        return QTSSModuleUtils::SendErrorResponse(inParamBlock->inRTSPRequest,
                                                    qtssUnsupportedMediaType, sBadQTFileErr);

    //find the payload for this track ID (if applicable)
    StrPtrLen* thePayload = NULL;
    UInt32 thePayloadType = qtssUnknownPayloadType;
    Float32 bufferDelay = (Float32) 3.0; // FIXME need a constant defined for 3.0 value. It is used multiple places
    
    for (UInt32 x = 0; x < theFile->fFile.GetSourceInfo()->GetNumStreams(); x++)
    {
        SourceInfo::StreamInfo* theStreamInfo = theFile->fFile.GetSourceInfo()->GetStreamInfo(x);
        if (theStreamInfo->fTrackID == theTrackID)
        {
            thePayload = &theStreamInfo->fPayloadName;
            thePayloadType = theStreamInfo->fPayloadType;
            bufferDelay = theStreamInfo->fBufferDelay;
            break;
        }   
    }

    //Create a new RTP stream           
    QTSS_RTPStreamObject newStream = NULL;
    theErr = QTSS_AddRTPStream(inParamBlock->inClientSession, inParamBlock->inRTSPRequest, &newStream, 0);
    if (theErr != QTSS_NoErr)
        return theErr;
    
    // Set the payload type, payload name & timescale of this track
    SInt32 theTimescale = theFile->fFile.GetTrackTimeScale(theTrackID);
    
    theErr = QTSS_SetValue(newStream, qtssRTPStrBufferDelayInSecs, 0, &bufferDelay, sizeof(bufferDelay));
    Assert(theErr == QTSS_NoErr);
    theErr = QTSS_SetValue(newStream, qtssRTPStrPayloadName, 0, thePayload->Ptr, thePayload->Len);
    Assert(theErr == QTSS_NoErr);
    theErr = QTSS_SetValue(newStream, qtssRTPStrPayloadType, 0, &thePayloadType, sizeof(thePayloadType));
    Assert(theErr == QTSS_NoErr);
    theErr = QTSS_SetValue(newStream, qtssRTPStrTimescale, 0, &theTimescale, sizeof(theTimescale));
    Assert(theErr == QTSS_NoErr);
    theErr = QTSS_SetValue(newStream, qtssRTPStrTrackID, 0, &theTrackID, sizeof(theTrackID));
    Assert(theErr == QTSS_NoErr);
    
    // Set the number of quality levels. Allow up to 6
    static UInt32 sNumQualityLevels = 0;
    
    theErr = QTSS_SetValue(newStream, qtssRTPStrNumQualityLevels, 0, &sNumQualityLevels, sizeof(sNumQualityLevels));
    Assert(theErr == QTSS_NoErr);
    
    // Get the SSRC of this track
    UInt32* theTrackSSRC = NULL;
    UInt32 theTrackSSRCSize = 0;
    (void)QTSS_GetValuePtr(newStream, qtssRTPStrSSRC, 0, (void**)&theTrackSSRC, &theTrackSSRCSize);

    // The RTP stream should ALWAYS have an SSRC assuming QTSS_AddStream succeeded.
    Assert((theTrackSSRC != NULL) && (theTrackSSRCSize == sizeof(UInt32)));
    
    //give the file some info it needs.
    theFile->fFile.SetTrackSSRC(theTrackID, *theTrackSSRC);
    theFile->fFile.SetTrackCookie(theTrackID, newStream);
    
    //
    // Our array has now been updated to reflect the fields requested by the client.
    //send the setup response
    //(void)QTSS_AppendRTSPHeader(inParamBlock->inRTSPRequest, qtssLastModifiedHeader,
    //                          theFile->fFile.GetQTFile()->GetModDateStr(), DateBuffer::kDateBufferLen);
    (void)QTSS_AppendRTSPHeader(inParamBlock->inRTSPRequest, qtssCacheControlHeader,
                                kCacheControlHeader.Ptr, kCacheControlHeader.Len);

    //send the setup response
    (void)QTSS_SendStandardRTSPResponse(inParamBlock->inRTSPRequest, newStream, 0);
    return QTSS_NoErr;
}
void    RelaySDPSourceInfo::Parse(StrPtrLen* inSDPData)
{
    // These are the lines of the SDP file that we are interested in
    static StrPtrLen sRelayAddr("a=x-qt-relay-addr");
    static StrPtrLen sRelayPort("a=x-qt-relay-port");
    
    Assert(fOutputArray == NULL);
    Assert(fStreamArray == NULL);
    
    StrPtrLen sdpLine;
    StrPtrLen outputAddrs;
    
    StringParser trackCounter(inSDPData);

    UInt32 theDestIPAddr = 0;
    UInt16 theDestTtl = 0;

    //
    // FIRST WALK THROUGH SDP
    // The first walk is to count up the number of StreamInfo & OutputInfo
    // objects that we will need.
    while (true)
    {
        // grab a line
        trackCounter.ConsumeUntil(&sdpLine, StringParser::sEOLMask);
        
        if (sdpLine.NumEqualIgnoreCase(sRelayAddr.Ptr, sRelayAddr.Len))
        {
            // there is a x-qt-relay-addr line, look for all IP addrs
            StringParser relayAddrParser(&sdpLine);
            relayAddrParser.ConsumeUntil(NULL, StringParser::sDigitMask);
            
            // The first IP addr on this line is the destination IP addr of the
            // source broadcast.
            theDestIPAddr = SDPSourceInfo::GetIPAddr(&relayAddrParser, ' ');
            relayAddrParser.ConsumeWhitespace();
            
            // Store this position so we can later go back to it
            outputAddrs.Ptr = relayAddrParser.GetCurrentPosition();
            outputAddrs.Len = relayAddrParser.GetDataRemaining();
            
            StrPtrLen theTtl;
            while (relayAddrParser.GetDataRemaining() > 0)
            {
                relayAddrParser.ConsumeUntil(&theTtl, ' '); 
                relayAddrParser.ConsumeWhitespace();
                fNumOutputs++;
            }
            fNumOutputs--;// Don't count the ttl as an output!
            
            StringParser ttlParser(&theTtl);
            theDestTtl = (UInt16) ttlParser.ConsumeInteger(NULL);
        }
        // Each x=qt-relay-port line corresponds to one source stream.
        else if (sdpLine.NumEqualIgnoreCase(sRelayPort.Ptr, sRelayPort.Len))
            fNumStreams++;

        //stop when we reach an empty line.
        if (!trackCounter.ExpectEOL())
            break;
    }
    
    // No relay info in this file!
    if ((fNumStreams == 0) || (fNumOutputs == 0))
        return;
        
    // x-qt-relay-port lines should always be in pairs (RTP & RTCP)
    if ((fNumStreams & 1) != 0)
        return;
    fNumStreams /= 2;

    //
    // CONSTRUCT fStreamInfo AND fOutputInfo ARRAYS
    fStreamArray = NEW StreamInfo[fNumStreams];
    fOutputArray = NEW OutputInfo[fNumOutputs];
    
    //
    // FILL IN ARRAYS
    
    // Filling in the output addresses is easy because the outputAddrs
    // StrPtrLen points right at the data we want
    StringParser theOutputAddrParser(&outputAddrs);
    for (UInt32 x = 0; x < fNumOutputs; x++)
    {
        fOutputArray[x].fDestAddr = SDPSourceInfo::GetIPAddr(&theOutputAddrParser, ' ');
        fOutputArray[x].fLocalAddr = INADDR_ANY;
        fOutputArray[x].fTimeToLive = theDestTtl;
        fOutputArray[x].fPortArray = NEW UInt16[fNumStreams];//Each output has one port per stream
        fOutputArray[x].fNumPorts = fNumStreams;
        ::memset(fOutputArray[x].fPortArray, 0, fNumStreams * sizeof(UInt16));
        fOutputArray[x].fAlreadySetup = false;
        theOutputAddrParser.ConsumeWhitespace();
        Assert(fOutputArray[x].fDestAddr > 0);
    }
    
    StringParser sdpParser(inSDPData);
    
    // Now go through and find all the port information on all the x-qt-relay-port lines
    for (UInt32 theStreamIndex = 0; theStreamIndex < fNumStreams; )
    {
        sdpParser.ConsumeUntil(&sdpLine, StringParser::sEOLMask);
        
        // parse through all the x-qt-relay-port lines
        if (sdpLine.NumEqualIgnoreCase(sRelayPort.Ptr, sRelayPort.Len))
        {
            // Begin parsing... find the first port on the line
            StringParser relayAddrParser(&sdpLine);
            relayAddrParser.ConsumeUntil(NULL, StringParser::sDigitMask);
        
            // The first port is the source port for this stream
            fStreamArray[theStreamIndex].fPort = (UInt16) relayAddrParser.ConsumeInteger(NULL);
            if (fStreamArray[theStreamIndex].fPort & 1)
                continue; //we only care about RTP ports
            
            // Fill in all the fields we can for this stream
            fStreamArray[theStreamIndex].fDestIPAddr = theDestIPAddr;
            fStreamArray[theStreamIndex].fTimeToLive = theDestTtl;
            fStreamArray[theStreamIndex].fTrackID = theStreamIndex + 1;
            
            // Now fill in all the output ports for this stream
            for (UInt32 x = 0; x < fNumOutputs; x++)
            {
                relayAddrParser.ConsumeWhitespace();
                fOutputArray[x].fPortArray[theStreamIndex] = (UInt16) relayAddrParser.ConsumeInteger(NULL);
            }
            
            theStreamIndex++;
        }
        //stop when we reach an empty line.
        if (!sdpParser.ExpectEOL())
            break;
    }
}
QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParamBlock)
{
    // Check and see if this is a request we should handle. We handle all requests with URLs that
    // end in a '.rtp'
    char* theFullPathStr = NULL;
    QTSS_Error theError = QTSS_GetValueAsString(inParamBlock->inRTSPRequest, qtssRTSPReqLocalPath, 0, &theFullPathStr);
	QTSSCharArrayDeleter theFullPathDeleter(theFullPathStr);
    Assert(theError == QTSS_NoErr);
    
    StrPtrLen theFullPath(theFullPathStr);
    
    if ((theFullPath.Len <= sRTPSuffix.Len) ||
        (!sRTPSuffix.NumEqualIgnoreCase(&theFullPath.Ptr[theFullPath.Len - sRTPSuffix.Len], sRTPSuffix.Len)))
        return QTSS_RequestFailed;
    
    // It is, so let's set everything up...
    
    //
    // Get the FileSession for this DESCRIBE, if any.
    UInt32 theLen = sizeof(FileSession*);
    FileSession*    theFile = NULL;
    QTSS_Error      theErr = QTSS_NoErr;

    (void)QTSS_GetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, (void*)&theFile, &theLen);

    if ( theFile != NULL )  
    {
        //
        // There is already a file for this session. This can happen if there are multiple DESCRIBES,
        // or a DESCRIBE has been issued with a Session ID, or some such thing.
        
        if ( !theFullPath.Equal( *theFile->fFile.GetMoviePath() ) )
        {
            delete theFile;
            theFile = NULL;
            
            // NULL out the attribute value, just in case.
            (void)QTSS_SetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, &theFile, sizeof(theFile));
        }
    }
    
    if ( theFile == NULL )
    {   
        theErr = CreateRTPFileSession(inParamBlock, theFullPath, &theFile);
        if (theErr != QTSS_NoErr)
        {
            (void)QTSS_Teardown(inParamBlock->inClientSession);
            return theErr;
        }
    
        // Store this newly created file object in the RTP session.
        theErr = QTSS_SetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, &theFile, sizeof(theFile));
    }
    
    //generate the SDP.
    UInt32 totalSDPLength = sSDPHeader1.Len;
    iovec theSDPVec[10];//1 for the RTSP header, 6 for the sdp header, 1 for the sdp body
    theSDPVec[1].iov_base = sSDPHeader1.Ptr;
    theSDPVec[1].iov_len = sSDPHeader1.Len;
    
    //filename goes here
    //(void)QTSS_GetValuePtr(inParamBlock->inRTSPRequest, qtssRTSPReqFilePath, 0, (void**)&theSDPVec[2].iov_base, (UInt32*)&theSDPVec[2].iov_len);
	char* filenameStr = NULL;
	(void)QTSS_GetValueAsString(inParamBlock->inRTSPRequest, qtssRTSPReqFilePath, 0, &filenameStr);
    QTSSCharArrayDeleter filenameStrDeleter(filenameStr);
	theSDPVec[2].iov_base = filenameStr;
	theSDPVec[2].iov_len = ::strlen(filenameStr);
	
    totalSDPLength += theSDPVec[2].iov_len;
    
    //url & admin email goes here
    theSDPVec[3].iov_base = sSDPHeader2.Ptr;
    theSDPVec[3].iov_len = sSDPHeader2.Len;
    totalSDPLength += sSDPHeader2.Len;

    //connection header
    theSDPVec[4].iov_base = sSDPHeader3.Ptr;
    theSDPVec[4].iov_len = sSDPHeader3.Len;
    totalSDPLength += sSDPHeader3.Len;
    
    //append IP addr
    (void)QTSS_GetValuePtr(inParamBlock->inRTSPSession, qtssRTSPSesLocalAddrStr, 0,
                                                (void**)&theSDPVec[5].iov_base, (UInt32*)&theSDPVec[5].iov_len);
    totalSDPLength += theSDPVec[5].iov_len;

    //last static sdp line
    theSDPVec[6].iov_base = sSDPHeader4.Ptr;
    theSDPVec[6].iov_len = sSDPHeader4.Len;
    totalSDPLength += sSDPHeader4.Len;
    
    //now append content-determined sdp
    theSDPVec[7].iov_base = theFile->fFile.GetSDPFile()->Ptr;
    theSDPVec[7].iov_len = theFile->fFile.GetSDPFile()->Len;
    totalSDPLength += theSDPVec[7].iov_len;
    
    Assert(theSDPVec[2].iov_base != NULL);


    // Append the Last Modified header to be a good caching proxy citizen before sending the Describe
    //(void)QTSS_AppendRTSPHeader(inParamBlock->inRTSPRequest, qtssLastModifiedHeader,
    //                              theFile->fFile.GetQTFile()->GetModDateStr(), DateBuffer::kDateBufferLen);
    (void)QTSS_AppendRTSPHeader(inParamBlock->inRTSPRequest, qtssCacheControlHeader,
                                    kCacheControlHeader.Ptr, kCacheControlHeader.Len);

    //ok, we have a filled out iovec. Let's send it!
    QTSSModuleUtils::SendDescribeResponse(inParamBlock->inRTSPRequest, inParamBlock->inClientSession,
                                                                    &theSDPVec[0], 8, totalSDPLength);  
	
    return QTSS_NoErr;
}