void QTSSModuleUtils::SendDescribeResponse(QTSS_RTSPRequestObject inRequest, QTSS_ClientSessionObject inSession, iovec* describeData, UInt32 inNumVectors, UInt32 inTotalLength) { //write content size header char buf[32]; qtss_sprintf(buf, "%"_S32BITARG_"", inTotalLength); (void)QTSS_AppendRTSPHeader(inRequest, qtssContentLengthHeader, &buf[0], ::strlen(&buf[0])); (void)QTSS_SendStandardRTSPResponse(inRequest, inSession, 0); // On solaris, the maximum # of vectors is very low (= 16) so to ensure that we are still able to // send the SDP if we have a number greater than the maximum allowed, we coalesce the vectors into // a single big buffer #ifdef __solaris__ if (inNumVectors > IOV_MAX ) { char* describeDataBuffer = QTSSModuleUtils::CoalesceVectors(describeData, inNumVectors, inTotalLength); (void)QTSS_Write(inRequest, (void *)describeDataBuffer, inTotalLength, NULL, qtssWriteFlagsNoFlags); // deleting memory allocated by the CoalesceVectors call delete [] describeDataBuffer; } else (void)QTSS_WriteV(inRequest, describeData, inNumVectors, inTotalLength, NULL); #else (void)QTSS_WriteV(inRequest, describeData, inNumVectors, inTotalLength, NULL); #endif }
QTSS_Error QTSSModuleUtils::SendErrorResponseWithMessage( QTSS_RTSPRequestObject inRequest, QTSS_RTSPStatusCode inStatusCode, StrPtrLen* inErrorMessagePtr) { static Bool16 sFalse = false; //set RTSP headers necessary for this error response message (void)QTSS_SetValue(inRequest, qtssRTSPReqStatusCode, 0, &inStatusCode, sizeof(inStatusCode)); (void)QTSS_SetValue(inRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse)); StrPtrLen theErrorMessage(NULL, 0); if (sEnableRTSPErrorMsg) { Assert(inErrorMessagePtr != NULL); //Assert(inErrorMessagePtr->Ptr != NULL); //Assert(inErrorMessagePtr->Len != 0); theErrorMessage.Set(inErrorMessagePtr->Ptr, inErrorMessagePtr->Len); char buff[32]; qtss_sprintf(buff,"%"_U32BITARG_"",inErrorMessagePtr->Len); (void)QTSS_AppendRTSPHeader(inRequest, qtssContentLengthHeader, buff, ::strlen(buff)); } //send the response header. In all situations where errors could happen, we //don't really care, cause there's nothing we can do anyway! (void)QTSS_SendRTSPHeaders(inRequest); // // Now that we've formatted the message into the temporary buffer, // write it out to the request stream and the Client Session object (void)QTSS_Write(inRequest, theErrorMessage.Ptr, theErrorMessage.Len, NULL, 0); (void)QTSS_SetValue(inRequest, qtssRTSPReqRespMsg, 0, theErrorMessage.Ptr, theErrorMessage.Len); return QTSS_RequestFailed; }
QTSS_Error QTSSModuleUtils::SendErrorResponse( QTSS_RTSPRequestObject inRequest, QTSS_RTSPStatusCode inStatusCode, QTSS_AttributeID inTextMessage, StrPtrLen* inStringArg) { static Bool16 sFalse = false; //set RTSP headers necessary for this error response message (void)QTSS_SetValue(inRequest, qtssRTSPReqStatusCode, 0, &inStatusCode, sizeof(inStatusCode)); (void)QTSS_SetValue(inRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse)); StringFormatter theErrorMsgFormatter(NULL, 0); char *messageBuffPtr = NULL; if (sEnableRTSPErrorMsg) { // Retrieve the specified message out of the text messages dictionary. StrPtrLen theMessage; (void)QTSS_GetValuePtr(sMessages, inTextMessage, 0, (void**)(void*)&theMessage.Ptr, &theMessage.Len); if ((theMessage.Ptr == NULL) || (theMessage.Len == 0)) { // If we couldn't find the specified message, get the default // "No Message" message, and return that to the client instead. (void)QTSS_GetValuePtr(sMessages, qtssMsgNoMessage, 0, (void**)(void*)&theMessage.Ptr, &theMessage.Len); } Assert(theMessage.Ptr != NULL); Assert(theMessage.Len > 0); // Allocate a temporary buffer for the error message, and format the error message // into that buffer UInt32 theMsgLen = 256; if (inStringArg != NULL) theMsgLen += inStringArg->Len; messageBuffPtr = NEW char[theMsgLen]; messageBuffPtr[0] = 0; theErrorMsgFormatter.Set(messageBuffPtr, theMsgLen); // // Look for a %s in the string, and if one exists, replace it with the // argument passed into this function. //we can safely assume that message is in fact NULL terminated char* stringLocation = ::strstr(theMessage.Ptr, "%s"); if (stringLocation != NULL) { //write first chunk theErrorMsgFormatter.Put(theMessage.Ptr, stringLocation - theMessage.Ptr); if (inStringArg != NULL && inStringArg->Len > 0) { //write string arg if it exists theErrorMsgFormatter.Put(inStringArg->Ptr, inStringArg->Len); stringLocation += 2; } //write last chunk theErrorMsgFormatter.Put(stringLocation, (theMessage.Ptr + theMessage.Len) - stringLocation); } else theErrorMsgFormatter.Put(theMessage); char buff[32]; qtss_sprintf(buff,"%"_U32BITARG_"",theErrorMsgFormatter.GetBytesWritten()); (void)QTSS_AppendRTSPHeader(inRequest, qtssContentLengthHeader, buff, ::strlen(buff)); } //send the response header. In all situations where errors could happen, we //don't really care, cause there's nothing we can do anyway! (void)QTSS_SendRTSPHeaders(inRequest); // // Now that we've formatted the message into the temporary buffer, // write it out to the request stream and the Client Session object (void)QTSS_Write(inRequest, theErrorMsgFormatter.GetBufPtr(), theErrorMsgFormatter.GetBytesWritten(), NULL, 0); (void)QTSS_SetValue(inRequest, qtssRTSPReqRespMsg, 0, theErrorMsgFormatter.GetBufPtr(), theErrorMsgFormatter.GetBytesWritten()); delete [] messageBuffPtr; return QTSS_RequestFailed; }
QTSS_Error QTSSModuleUtils::AppendRTPMetaInfoHeader( QTSS_RTSPRequestObject inRequest, StrPtrLen* inRTPMetaInfoHeader, RTPMetaInfoPacket::FieldID* inFieldIDArray) { // // For formatting the response header char tempBuffer[128]; ResizeableStringFormatter theFormatter(tempBuffer, 128); StrPtrLen theHeader(*inRTPMetaInfoHeader); // // For marking which fields were requested by the client Bool16 foundFieldArray[RTPMetaInfoPacket::kNumFields]; ::memset(foundFieldArray, 0, sizeof(Bool16) * RTPMetaInfoPacket::kNumFields); char* theEndP = theHeader.Ptr + theHeader.Len; UInt16 fieldNameValue = 0; while (theHeader.Ptr <= (theEndP - sizeof(RTPMetaInfoPacket::FieldName))) { RTPMetaInfoPacket::FieldName* theFieldName = (RTPMetaInfoPacket::FieldName*)theHeader.Ptr; ::memcpy (&fieldNameValue, theFieldName, sizeof(UInt16)); RTPMetaInfoPacket::FieldIndex theFieldIndex = RTPMetaInfoPacket::GetFieldIndexForName(bswap_16(fieldNameValue)); // // This field is not supported (not in the field ID array), so // don't put it in the response if ((theFieldIndex == RTPMetaInfoPacket::kIllegalField) || (inFieldIDArray[theFieldIndex] == RTPMetaInfoPacket::kFieldNotUsed)) { theHeader.Ptr += 3; continue; } // // Mark that this field has been requested by the client foundFieldArray[theFieldIndex] = true; // // This field is good to go... put it in the response theFormatter.Put(theHeader.Ptr, sizeof(RTPMetaInfoPacket::FieldName)); if (inFieldIDArray[theFieldIndex] != RTPMetaInfoPacket::kUncompressed) { // // If the caller wants this field to be compressed (there // is an ID associated with the field), put the ID in the response theFormatter.PutChar('='); theFormatter.Put(inFieldIDArray[theFieldIndex]); } // // Field separator theFormatter.PutChar(';'); // // Skip onto the next field name in the header theHeader.Ptr += 3; } // // Go through the caller's FieldID array, and turn off the fields // that were not requested by the client. for (UInt32 x = 0; x < RTPMetaInfoPacket::kNumFields; x++) { if (!foundFieldArray[x]) inFieldIDArray[x] = RTPMetaInfoPacket::kFieldNotUsed; } // // No intersection between requested headers and supported headers! if (theFormatter.GetCurrentOffset() == 0) return QTSS_ValueNotFound; // Not really the greatest error! // // When appending the header to the response, strip off the last ';'. // It's not needed. return QTSS_AppendRTSPHeader(inRequest, qtssXRTPMetaInfoHeader, theFormatter.GetBufPtr(), theFormatter.GetCurrentOffset() - 1); }
QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams) { //解析命令 char* theFullPathStr = NULL; QTSS_Error theErr = QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFileName, 0, &theFullPathStr); Assert(theErr == QTSS_NoErr); QTSSCharArrayDeleter theFullPathStrDeleter(theFullPathStr); if (theErr != QTSS_NoErr) return NULL; StrPtrLen theFullPath(theFullPathStr); if (theFullPath.Len != sRelaySuffix.Len ) return NULL; StrPtrLen endOfPath2(&theFullPath.Ptr[theFullPath.Len - sRelaySuffix.Len], sRelaySuffix.Len); if (!endOfPath2.Equal(sRelaySuffix)) { return NULL; } //解析查询字符串 char* theQueryStr = NULL; theErr = QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqQueryString, 0, &theQueryStr); Assert(theErr == QTSS_NoErr); QTSSCharArrayDeleter theQueryStringDeleter(theQueryStr); if (theErr != QTSS_NoErr) return NULL; StrPtrLen theQueryString(theQueryStr); QueryParamList parList(theQueryStr); const char* sName = parList.DoFindCGIValueForParam(QUERY_STREAM_NAME); const char* sChannel = parList.DoFindCGIValueForParam(QUERY_STREAM_CHANNEL); if(sName == NULL && sChannel == NULL) { return NULL; } char szChannelURL[256] = {0,}; char szUsername[32] = {0,}; char szPassword[32] = {0,}; const char* sURL = NULL; char sPushServerAddr[256] = {0,}; if (NULL != sChannel) { //find channel info GetChannelInfoById( (char*)sChannel, szChannelURL, sizeof(szChannelURL), szUsername, sizeof(szUsername), szPassword, sizeof(szPassword), sPushServerAddr, sizeof(sPushServerAddr)); if ( (int)strlen(szChannelURL) < 1 ) return NULL; //not found the channel //sURL = "rtsp://192.168.1.186:8557"; sURL = szChannelURL; } else { sURL = parList.DoFindCGIValueForParam(QUERY_STREAM_URL); } //if(sURL == NULL) return NULL; const char* sCMD = parList.DoFindCGIValueForParam(QUERY_STREAM_CMD); bool bStop = false; if(sCMD) { if(::strcmp(sCMD,QUERY_STREAM_CMD_STOP) == 0) bStop = true; } StrPtrLen streamName(NULL!=sName?(char*)sName:(char*)sChannel); //从接口获取信息结构体 EasyRelaySession* session = NULL; //首先查找Map里面是否已经有了对应的流 OSRef* sessionRef = sRelaySessionMap->Resolve(&streamName); if(sessionRef != NULL) { session = (EasyRelaySession*)sessionRef->GetObject(); } else { if(bStop) return NULL; if(sURL == NULL) return NULL; session = NEW EasyRelaySession((char*)sURL, EasyRelaySession::kRTSPTCPClientType, NULL!=sName?(char*)sName:(char*)sChannel, sPushServerAddr); QTSS_Error theErr = session->RelaySessionStart(); if(theErr == QTSS_NoErr) { OS_Error theErr = sRelaySessionMap->Register(session->GetRef()); Assert(theErr == QTSS_NoErr); } else { session->Signal(Task::kKillEvent); return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientNotFound, 0); } //增加一次对RelaySession的无效引用,后面会统一释放 OSRef* debug = sRelaySessionMap->Resolve(&streamName); Assert(debug == session->GetRef()); } sRelaySessionMap->Release(session->GetRef()); if(bStop) { sRelaySessionMap->UnRegister(session->GetRef()); session->Signal(Task::kKillEvent); return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssSuccessOK, 0); } QTSS_RTSPStatusCode statusCode = qtssRedirectPermMoved; QTSS_SetValue(inParams->inRTSPRequest, qtssRTSPReqStatusCode, 0, &statusCode, sizeof(statusCode)); // Get the ip addr out of the prefs dictionary UInt16 thePort = 554; UInt32 theLen = sizeof(UInt16); theErr = QTSServerInterface::GetServer()->GetPrefs()->GetValue(qtssPrefsRTSPPorts, 0, &thePort, &theLen); Assert(theErr == QTSS_NoErr); //构造本地URL char url[QTSS_MAX_URL_LENGTH] = { 0 }; qtss_sprintf(url,"rtsp://%s:%d/%s.sdp", sLocal_IP_Addr, thePort, NULL!=sName?(char*)sName:(char*)sChannel); StrPtrLen locationRedirect(url); Bool16 sFalse = false; (void)QTSS_SetValue(inParams->inRTSPRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse)); QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssLocationHeader, locationRedirect.Ptr, locationRedirect.Len); return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssRedirectPermMoved, 0); }
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 DoDescribe(QTSS_StandardRTSP_Params* inParams) { char* theUriStr = NULL; QTSS_Error err = QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFileName, 0, &theUriStr); Assert(err == QTSS_NoErr); if(err != QTSS_NoErr) return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest, 0); QTSSCharArrayDeleter theUriStrDeleter(theUriStr); //从接口获取信息结构体 //TODO: DeviceInfo* pDeviceInfo = parseDevice->GetDeviceInfoByIdName(theUriStr); if(pDeviceInfo == NULL) { qtss_printf("ID:%s Not Found\n",theUriStr); return QTSS_RequestFailed; } //信息存在rtsp://59.46.115.84:8554/h264/ch1/sub/av_stream RTSPRelaySession* clientSes = NULL; //首先查找Map里面是否已经有了对应的流 StrPtrLen streamName(theUriStr); OSRef* clientSesRef = sClientSessionMap->Resolve(&streamName); if(clientSesRef != NULL) { clientSes = (RTSPRelaySession*)clientSesRef->GetObject(); } else { clientSes = NEW RTSPRelaySession( SocketUtils::ConvertStringToAddr(pDeviceInfo->m_szIP), pDeviceInfo->m_nPort, pDeviceInfo->m_szSourceUrl, 1, rtcpInterval, 0, theReadInterval, sockRcvBuf, speed, packetPlayHeader, overbufferwindowInK, sendOptions, pDeviceInfo->m_szUser, pDeviceInfo->m_szPassword, theUriStr); OS_Error theErr = clientSes->SendDescribe(); if(theErr == QTSS_NoErr) { OS_Error theErr = sClientSessionMap->Register(clientSes->GetRef()); Assert(theErr == QTSS_NoErr); } else { clientSes->Signal(Task::kKillEvent); return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientNotFound, 0); } //增加一次对RelaySession的无效引用,后面会统一释放 OSRef* debug = sClientSessionMap->Resolve(&streamName); Assert(debug == clientSes->GetRef()); } ReflectorSession* theSession = SetupProxySession(inParams, clientSes); if (theSession == NULL) { sClientSessionMap->Release(clientSes->GetRef()); return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssServerNotImplemented, 0); } QTSS_Error Err = QTSS_SetValue(inParams->inRTSPSession, sRTSPBroadcastSessionAttr, 0, &theSession, sizeof(theSession)); Assert(Err == QTSS_NoErr); clientSes->SetReflectorSession(theSession); theSession->SetRTSPRelaySession((void*)clientSes); sClientSessionMap->Release(clientSes->GetRef()); iovec theDescribeVec[3] = { {0 }}; Assert(theSession->GetLocalSDP()->Ptr != NULL); StrPtrLen theFileData; QTSS_TimeVal outModDate = 0; QTSS_TimeVal inModDate = -1; theFileData.Ptr = theSession->GetLocalSDP()->Ptr; theFileData.Len = theSession->GetLocalSDP()->Len; // -------------- process SDP to remove connection info and add track IDs, port info, and default c= line StrPtrLen theSDPData; SDPSourceInfo tempSDPSourceInfo(theFileData.Ptr, theFileData.Len); // will make a copy and delete in destructor theSDPData.Ptr = tempSDPSourceInfo.GetLocalSDP(&theSDPData.Len); // returns a new buffer with processed sdp OSCharArrayDeleter sdpDeleter(theSDPData.Ptr); // delete the temp sdp source info buffer returned by GetLocalSDP if (theSDPData.Len <= 0) // can't find it on disk or it failed to parse just use the one in the session. { theSDPData.Ptr = theSession->GetLocalSDP()->Ptr; // this sdp isn't ours it must not be deleted theSDPData.Len = theSession->GetLocalSDP()->Len; } // ------------ Clean up missing required SDP lines ResizeableStringFormatter editedSDP(NULL,0); DoDescribeAddRequiredSDPLines2(inParams, theSession, outModDate, &editedSDP, &theSDPData); StrPtrLen editedSDPSPL(editedSDP.GetBufPtr(),editedSDP.GetBytesWritten()); // ------------ Check the headers SDPContainer checkedSDPContainer; checkedSDPContainer.SetSDPBuffer( &editedSDPSPL ); if (!checkedSDPContainer.IsSDPBufferValid()) { return QTSSModuleUtils::SendErrorResponseWithMessage(inParams->inRTSPRequest, qtssUnsupportedMediaType, &sSDPNotValidMessage); } // ------------ Put SDP header lines in correct order Float32 adjustMediaBandwidthPercent = 1.0; Bool16 adjustMediaBandwidth = false; SDPLineSorter sortedSDP(&checkedSDPContainer,adjustMediaBandwidthPercent); // ------------ Write the SDP UInt32 sessLen = sortedSDP.GetSessionHeaders()->Len; UInt32 mediaLen = sortedSDP.GetMediaHeaders()->Len; theDescribeVec[1].iov_base = sortedSDP.GetSessionHeaders()->Ptr; theDescribeVec[1].iov_len = sortedSDP.GetSessionHeaders()->Len; theDescribeVec[2].iov_base = sortedSDP.GetMediaHeaders()->Ptr; theDescribeVec[2].iov_len = sortedSDP.GetMediaHeaders()->Len; (void)QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssCacheControlHeader, kCacheControlHeader.Ptr, kCacheControlHeader.Len); QTSSModuleUtils::SendDescribeResponse(inParams->inRTSPRequest, inParams->inClientSession, &theDescribeVec[0], 3, sessLen + mediaLen ); return QTSS_NoErr; }
QTSS_Error DoPlay(QTSS_StandardRTSP_Params* inParamBlock) { FileSession** theFile = NULL; UInt32 theLen = 0; QTSS_Error theErr = QTSS_GetValuePtr(inParamBlock->inClientSession, sFileSessionAttr, 0, (void**)&theFile, &theLen); if ((theErr != QTSS_NoErr) || (theLen != sizeof(FileSession*))) return QTSS_RequestFailed; Float64* theStartTime = 0; theErr = QTSS_GetValuePtr(inParamBlock->inRTSPRequest, qtssRTSPReqStartTime, 0, (void**)&theStartTime, &theLen); if ((theErr != QTSS_NoErr) || (theLen != sizeof(Float64))) return QTSSModuleUtils::SendErrorResponse( inParamBlock->inRTSPRequest, qtssClientBadRequest, sSeekToNonexistentTimeErr); RTPFileSession::ErrorCode qtFileErr = (*theFile)->fFile.Seek(*theStartTime); if (qtFileErr != RTPFileSession::errNoError) return QTSSModuleUtils::SendErrorResponse( inParamBlock->inRTSPRequest, qtssClientBadRequest, sSeekToNonexistentTimeErr); //make sure to clear the next packet the server would have sent! (*theFile)->fPacketStruct.packetData = NULL; // Set the movie duration and size parameters Float64 movieDuration = (*theFile)->fFile.GetMovieDuration(); (void)QTSS_SetValue(inParamBlock->inClientSession, qtssCliSesMovieDurationInSecs, 0, &movieDuration, sizeof(movieDuration)); UInt64 movieSize = (*theFile)->fFile.GetAddedTracksRTPBytes(); (void)QTSS_SetValue(inParamBlock->inClientSession, qtssCliSesMovieSizeInBytes, 0, &movieSize, sizeof(movieSize)); //UInt32 bitsPerSecond = (*theFile)->fFile.GetBytesPerSecond() * 8; //(void)QTSS_SetValue(inParamBlock->inClientSession, qtssCliSesMovieAverageBitRate, 0, &bitsPerSecond, sizeof(bitsPerSecond)); // // For the purposes of the speed header, check to make sure all tracks are // over a reliable transport Bool16 allTracksReliable = true; // Set the timestamp & sequence number parameters for each track. QTSS_RTPStreamObject* theRef = NULL; for ( UInt32 theStreamIndex = 0; QTSS_GetValuePtr(inParamBlock->inClientSession, qtssCliSesStreamObjects, theStreamIndex, (void**)&theRef, &theLen) == QTSS_NoErr; theStreamIndex++) { UInt32* theTrackID = NULL; theErr = QTSS_GetValuePtr(*theRef, qtssRTPStrTrackID, 0, (void**)&theTrackID, &theLen); Assert(theErr == QTSS_NoErr); Assert(theTrackID != NULL); Assert(theLen == sizeof(UInt32)); SInt16 theSeqNum = (*theFile)->fFile.GetNextTrackSequenceNumber(*theTrackID); SInt32 theTimestamp = (*theFile)->fFile.GetSeekTimestamp(*theTrackID); Assert(theRef != NULL); Assert(theLen == sizeof(QTSS_RTPStreamObject)); theErr = QTSS_SetValue(*theRef, qtssRTPStrFirstSeqNumber, 0, &theSeqNum, sizeof(theSeqNum)); Assert(theErr == QTSS_NoErr); theErr = QTSS_SetValue(*theRef, qtssRTPStrFirstTimestamp, 0, &theTimestamp, sizeof(theTimestamp)); Assert(theErr == QTSS_NoErr); if (allTracksReliable) { QTSS_RTPTransportType theTransportType = qtssRTPTransportTypeUDP; theLen = sizeof(theTransportType); theErr = QTSS_GetValue(*theRef, qtssRTPStrTransportType, 0, &theTransportType, &theLen); Assert(theErr == QTSS_NoErr); if (theTransportType == qtssRTPTransportTypeUDP) allTracksReliable = false; } } //Tell the server to start playing this movie. We do want it to send RTCP SRs, but //we DON'T want it to write the RTP header theErr = QTSS_Play(inParamBlock->inClientSession, inParamBlock->inRTSPRequest, qtssPlayFlagsSendRTCP); if (theErr != QTSS_NoErr) return theErr; // qtssRTPSesAdjustedPlayTimeInMsec is valid only after calling QTSS_Play // theAdjustedPlayTime is a way to delay the sending of data until a time after the // the Play response should have been received. SInt64* theAdjustedPlayTime = 0; theErr = QTSS_GetValuePtr(inParamBlock->inClientSession, qtssCliSesAdjustedPlayTimeInMsec, 0, (void**)&theAdjustedPlayTime, &theLen); Assert(theErr == QTSS_NoErr); Assert(theAdjustedPlayTime != NULL); Assert(theLen == sizeof(SInt64)); (*theFile)->fAdjustedPlayTime = *theAdjustedPlayTime; // // This module supports the Speed header if the client wants the stream faster than normal. Float32 theSpeed = 1; theLen = sizeof(theSpeed); theErr = QTSS_GetValue(inParamBlock->inRTSPRequest, qtssRTSPReqSpeed, 0, &theSpeed, &theLen); Assert(theErr != QTSS_BadArgument); Assert(theErr != QTSS_NotEnoughSpace); if (theErr == QTSS_NoErr) { if (theSpeed > sMaxAllowedSpeed) theSpeed = sMaxAllowedSpeed; if ((theSpeed <= 0) || (!allTracksReliable)) theSpeed = 1; } (*theFile)->fSpeed = theSpeed; if (theSpeed != 1) { // // If our speed is not 1, append the RTSP speed header in the response char speedBuf[32]; qtss_sprintf(speedBuf, "%10.5f", theSpeed); StrPtrLen speedBufPtr(speedBuf); (void)QTSS_AppendRTSPHeader(inParamBlock->inRTSPRequest, qtssSpeedHeader, speedBufPtr.Ptr, speedBufPtr.Len); } // // Record the requested start and stop time. (*theFile)->fStopTime = -1; theLen = sizeof((*theFile)->fStopTime); theErr = QTSS_GetValue(inParamBlock->inRTSPRequest, qtssRTSPReqStopTime, 0, &(*theFile)->fStopTime, &theLen); Assert(theErr == QTSS_NoErr); (*theFile)->fStartTime = 0; theLen = sizeof((*theFile)->fStopTime); theErr = QTSS_GetValue(inParamBlock->inRTSPRequest, qtssRTSPReqStartTime, 0, &(*theFile)->fStartTime, &theLen); Assert(theErr == QTSS_NoErr); //the client doesn't necessarily specify this information in a play, //if it doesn't, fall back on some defaults. if ((*theFile)->fStartTime == -1) (*theFile)->fStartTime = 0; (void)QTSS_SendStandardRTSPResponse(inParamBlock->inRTSPRequest, inParamBlock->inClientSession, qtssPlayRespWriteTrackInfo); return QTSS_NoErr; }
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; }
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; }