QTSS_Error DoSetup(QTSS_StandardRTSP_Params* inParams) { ReflectorSession* theSession = NULL; UInt32 len = sizeof(theSession); QTSS_GetValue(inParams->inRTSPSession, sRTSPBroadcastSessionAttr, 0, &theSession, &len); if(theSession == NULL) return QTSS_RequestFailed; Bool16 foundSession = false; UInt32 theLen = 0; RTPSessionOutput** theOutput = NULL; QTSS_Error theErr = QTSS_GetValuePtr(inParams->inClientSession, sOutputAttr, 0, (void**)&theOutput, &theLen); if (theLen != sizeof(RTPSessionOutput*)) { if (theErr != QTSS_NoErr) { RTPSessionOutput* theNewOutput = NEW RTPSessionOutput(inParams->inClientSession, theSession, sServerPrefs, sStreamCookieAttr ); theSession->AddOutput(theNewOutput,true); (void)QTSS_SetValue(inParams->inClientSession, sOutputAttr, 0, &theNewOutput, sizeof(theNewOutput)); } } //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(inParams->inRTSPRequest, qtssRTSPReqFileDigit, 0, &theDigitStr); QTSSCharArrayDeleter theDigitStrDeleter(theDigitStr); if (theDigitStr == NULL) { return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest,sExpectedDigitFilenameErr); } UInt32 theTrackID = ::strtol(theDigitStr, NULL, 10); // Get info about this trackID SourceInfo::StreamInfo* theStreamInfo = theSession->GetSourceInfo()->GetStreamInfoByTrackID(theTrackID); // If theStreamInfo is NULL, we don't have a legit track, so return an error if (theStreamInfo == NULL) return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest, sReflectorBadTrackIDErr); StrPtrLen* thePayloadName = &theStreamInfo->fPayloadName; QTSS_RTPPayloadType thePayloadType = theStreamInfo->fPayloadType; StringParser parser(thePayloadName); parser.GetThru(NULL, '/'); theStreamInfo->fTimeScale = parser.ConsumeInteger(NULL); if (theStreamInfo->fTimeScale == 0) theStreamInfo->fTimeScale = 90000; QTSS_RTPStreamObject newStream = NULL; { // Ok, this is completely crazy but I can't think of a better way to do this that's // safe so we'll do it this way for now. Because the ReflectorStreams use this session's // stream queue, we need to make sure that each ReflectorStream is not reflecting to this // session while we call QTSS_AddRTPStream. One brutal way to do this is to grab each // ReflectorStream's mutex, which will stop every reflector stream from running. for (UInt32 x = 0; x < theSession->GetNumStreams(); x++) theSession->GetStreamByIndex(x)->GetMutex()->Lock(); theErr = QTSS_AddRTPStream(inParams->inClientSession, inParams->inRTSPRequest, &newStream, 0); for (UInt32 y = 0; y < theSession->GetNumStreams(); y++) theSession->GetStreamByIndex(y)->GetMutex()->Unlock(); if (theErr != QTSS_NoErr) return theErr; } // Set up dictionary items for this stream theErr = QTSS_SetValue(newStream, qtssRTPStrPayloadName, 0, thePayloadName->Ptr, thePayloadName->Len); Assert(theErr == QTSS_NoErr); theErr = QTSS_SetValue(newStream, qtssRTPStrPayloadType, 0, &thePayloadType, sizeof(thePayloadType)); Assert(theErr == QTSS_NoErr); theErr = QTSS_SetValue(newStream, qtssRTPStrTrackID, 0, &theTrackID, sizeof(theTrackID)); Assert(theErr == QTSS_NoErr); theErr = QTSS_SetValue(newStream, qtssRTPStrTimescale, 0, &theStreamInfo->fTimeScale, sizeof(theStreamInfo->fTimeScale)); Assert(theErr == QTSS_NoErr); // We only want to allow over buffering to dynamic rate clients SInt32 canDynamicRate = -1; theLen = sizeof(canDynamicRate); (void) QTSS_GetValue(inParams->inRTSPRequest, qtssRTSPReqDynamicRateState, 0, (void*) &canDynamicRate, &theLen); if (canDynamicRate < 1) // -1 no rate field, 0 off (void)QTSS_SetValue(inParams->inClientSession, qtssCliSesOverBufferEnabled, 0, &sFalse, sizeof(sFalse)); // Place the stream cookie in this stream for future reference void* theStreamCookie = theSession->GetStreamCookie(theTrackID); Assert(theStreamCookie != NULL); theErr = QTSS_SetValue(newStream, sStreamCookieAttr, 0, &theStreamCookie, sizeof(theStreamCookie)); Assert(theErr == QTSS_NoErr); // Set the number of quality levels. static UInt32 sNumQualityLevels = ReflectorSession::kNumQualityLevels; theErr = QTSS_SetValue(newStream, qtssRTPStrNumQualityLevels, 0, &sNumQualityLevels, sizeof(sNumQualityLevels)); Assert(theErr == QTSS_NoErr); //send the setup response (void)QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssCacheControlHeader, kCacheControlHeader.Ptr, kCacheControlHeader.Len); (void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, newStream, qtssSetupRespDontWriteSSRC); return QTSS_NoErr; }
QTSS_Error DoRequestPreProcessing(ProxyClientInfo* inProxyClientInfo, QTSS_RTSPRequestObject inRequest, QTSS_ClientSessionObject inSession) { QTSS_RTSPMethod* theMethod = NULL; UInt32 theLen = 0; QTSS_Error theErr = QTSS_GetValuePtr(inRequest, qtssRTSPReqMethod, 0, (void**)&theMethod, &theLen); Assert(theErr == QTSS_NoErr); char theTransportHeaderBuf[128]; StrPtrLen theTransportHeader(theTransportHeaderBuf, 0); // // The session ID passed to us is this server's session ID. Make sure to replace it // with the session ID passed to us by the upstream server, or else the upstream // server will not be able to properly handle this request. StrPtrLen* theProxyClientSessionIDPtr = inProxyClientInfo->GetRTSPClient()->GetSessionID(); // // Truncate the \r\n at the end of this StrPtrLen theProxyClientSessionID(*theProxyClientSessionIDPtr); if (theProxyClientSessionID.Len > 0) { Assert(theProxyClientSessionID.Len >= 2); // // Truncate off the EOL if ((theProxyClientSessionID.Ptr[theProxyClientSessionID.Len - 1] == '\r') || (theProxyClientSessionID.Ptr[theProxyClientSessionID.Len - 1] == '\n')) theProxyClientSessionID.Len--; if ((theProxyClientSessionID.Ptr[theProxyClientSessionID.Len - 1] == '\r') || (theProxyClientSessionID.Ptr[theProxyClientSessionID.Len - 1] == '\n')) theProxyClientSessionID.Len--; // // Truncate off "Session: " Assert(theProxyClientSessionID.Len > 9); theProxyClientSessionID.Ptr += 9; theProxyClientSessionID.Len -= 9; } StrPtrLen theRequest; theErr = QTSS_GetValuePtr(inRequest, qtssRTSPReqFullRequest, 0, (void**)&theRequest.Ptr, &theRequest.Len); Assert(theErr == QTSS_NoErr); if (*theMethod == qtssSetupMethod) { // // If this is a SETUP, we need to do some special processing, // and rewrite the Transport header before sending it on. // // First, tell the server to setup a new QTSS_RTPStreamObject // The proxy only supports UDP transport right now, so make sure that that is what we get QTSS_RTPStreamObject theStream = NULL; theErr = QTSS_AddRTPStream(inSession, inRequest, &theStream, qtssASFlagsForceUDPTransport); if (theErr != QTSS_NoErr) { Assert(theErr == QTSS_RequestFailed); // if any other error is returned, // our error handling logic will get confused. return theErr; } // // Tell the ProxyClientInfo object about this new stream. // This will also allocate client ports on this proxy. ProxyDemuxerTask* theProxyDemuxerTask = inProxyClientInfo->AddStream(theStream); // // Build a new Transport header UInt16 theRTPPort = theProxyDemuxerTask->GetSockets()->GetSocketA()->GetLocalPort(); qtss_sprintf(theTransportHeaderBuf, "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n", theRTPPort, theRTPPort + 1); theTransportHeader.Len = ::strlen(theTransportHeaderBuf); } // // Build the new request and send it iovec theNewRequest[5]; UInt32 totalResponseLen = 0; UInt32 theNumVecs = ReplaceSessionAndTransportHeaders(&theRequest, theNewRequest, &theProxyClientSessionID, &theTransportHeader, &totalResponseLen); return inProxyClientInfo->GetRTSPClient()->SendRTSPRequest(theNewRequest, theNumVecs); }
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; }