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* 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 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; }