OS_Error HTTPClientSocket::Read(void* inBuffer, const UInt32 inLength, UInt32* outRcvLen) { // // Bring up the GET connection if we need to if (!fGetSocket.IsConnected()) { #if CLIENT_SOCKET_DEBUG qtss_printf("HTTPClientSocket::Read: Sending GET\n"); #endif qtss_sprintf(fSendBuffer.Ptr, "GET %s HTTP/1.0\r\nX-SessionCookie: %" _U32BITARG_ "\r\nAccept: application/x-rtsp-rtp-interleaved\r\nUser-Agent: QTSS/2.0\r\n\r\n", fURL.Ptr, fCookie); fSendBuffer.Len = ::strlen(fSendBuffer.Ptr); Assert(fSentLength == 0); } OS_Error theErr = this->Connect(&fGetSocket); if (theErr != OS_NoErr) return theErr; if (fSendBuffer.Len > 0) { theErr = this->SendSendBuffer(&fGetSocket); if (theErr != OS_NoErr) return theErr; fSentLength = 1; // So we know to execute the receive code below. } // We are done sending the GET. If we need to receive the GET response, do that here if (fSentLength > 0) { *outRcvLen = 0; do { // Loop, trying to receive the entire response. theErr = fGetSocket.Read(&fSendBuffer.Ptr[fGetReceived], kSendBufferLen - fGetReceived, outRcvLen); fGetReceived += *outRcvLen; // Check to see if we've gotten a \r\n\r\n. If we have, then we've received // the entire GET fSendBuffer.Ptr[fGetReceived] = '\0'; char* theGetEnd = ::strstr(fSendBuffer.Ptr, "\r\n\r\n"); if (theGetEnd != NULL) { // We got the entire GET response, so we are ready to move onto // real RTSP response data. First skip past the \r\n\r\n theGetEnd += 4; #if CLIENT_SOCKET_DEBUG qtss_printf("HTTPClientSocket::Read: Received GET response\n"); #endif // Whatever remains is part of an RTSP request, so move that to // the beginning of the buffer and blow away the GET *outRcvLen = fGetReceived - (theGetEnd - fSendBuffer.Ptr); ::memcpy(inBuffer, theGetEnd, *outRcvLen); fGetReceived = fSentLength = 0; return OS_NoErr; } Assert(fGetReceived < inLength); } while (*outRcvLen > 0); #if CLIENT_SOCKET_DEBUG qtss_printf("HTTPClientSocket::Read: Waiting for GET response\n"); #endif // Message wasn't entirely received. Caller should wait for a read event on the GET socket Assert(theErr != OS_NoErr); fSocketP = &fGetSocket; fEventMask = EV_RE; return theErr; } theErr = fGetSocket.Read(&((char*)inBuffer)[fGetReceived], inLength - fGetReceived, outRcvLen); if (theErr != OS_NoErr) { #if CLIENT_SOCKET_DEBUG //qtss_printf("HTTPClientSocket::Read: Waiting for data\n"); #endif fSocketP = &fGetSocket; fEventMask = EV_RE; } #if CLIENT_SOCKET_DEBUG //else //qtss_printf("HTTPClientSocket::Read: Got some data\n"); #endif return theErr; }
int main(int argc, char * argv[]) { extern char* optarg;//命令参数,例如执行:CMS.exe -d -v int ch; char* theXMLFilePath = "./config.xml"; Bool16 notAService = false; Bool16 theXMLPrefsExist = true; Bool16 dontFork = false; #if _DEBUG char* compileType = "Compile_Flags/_DEBUG; "; #else char* compileType = "Compile_Flags/_RELEASE;"; #endif qtss_printf("%s/%s ( Build/%s; Platform/%s; %s%s) Built on: %s\n", QTSServerInterface::GetServerName().Ptr, QTSServerInterface::GetServerVersion().Ptr, QTSServerInterface::GetServerBuild().Ptr, QTSServerInterface::GetServerPlatform().Ptr, compileType, QTSServerInterface::GetServerComment().Ptr, QTSServerInterface::GetServerBuildDate().Ptr); while ((ch = getopt(argc,argv, "vdp:c:irsS:I")) != EOF) //opt: means requires option { switch(ch) { case 'v': qtss_printf("%s/%s ( Build/%s; Platform/%s; %s%s) Built on: %s\n", QTSServerInterface::GetServerName().Ptr, QTSServerInterface::GetServerVersion().Ptr, QTSServerInterface::GetServerBuild().Ptr, QTSServerInterface::GetServerPlatform().Ptr, compileType, QTSServerInterface::GetServerComment().Ptr, QTSServerInterface::GetServerBuildDate().Ptr); qtss_printf("usage: %s [ -d | -p port | -v | -c /myconfigpath.xml | -S numseconds | -I | -h ]\n", QTSServerInterface::GetServerName().Ptr); qtss_printf("-d: Don't run as a Win32 Service\n"); qtss_printf("-p 60000: Specify the default listening port of the server\n"); qtss_printf("-c c:\\myconfigpath.xml: Specify a config file path\n"); qtss_printf("-i: Install the CMS service\n"); qtss_printf("-r: Remove the CMS service\n"); qtss_printf("-s: Start the CMS service\n"); qtss_printf("-S n: Display server stats in the console every \"n\" seconds\n"); qtss_printf("-I: Start the server in the idle state\n"); break; case 'd': notAService = true; break; case 'p': Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt. sPort = ::atoi(optarg); break; case 'c': Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt. theXMLFilePath = optarg; break; case 'S': Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt. sStatsUpdateInterval = ::atoi(optarg); break; case 'i': qtss_printf("Installing the CMS Server service...\n"); ::InstallService("CMS"); qtss_printf("Starting the CMS Server service...\n"); ::RunAsService("CMS"); ::exit(0); break; case 'r': qtss_printf("Removing the CMS Server service...\n"); ::RemoveService("CMS"); ::exit(0); case 's': qtss_printf("Starting the CMS Server service...\n"); ::RunAsService("CMS"); ::exit(0); case 'I': sInitialState = qtssIdleState; break; default: break; } } //检测软件是否过期 QTSSExpirationDate::PrintExpirationDate(); if (QTSSExpirationDate::IsSoftwareExpired()) { qtss_printf("CMS Server Has Expired\n"); ::exit(0); } //读取xml配置文件 sXMLParser = new XMLPrefsParser(theXMLFilePath); //检测xml文件是否是以目录形式存在 if (sXMLParser->DoesFileExistAsDirectory()) { qtss_printf("Directory located at location where config prefs file should be.\n"); ::exit(0); } //检测xml文件是否可Write if (!sXMLParser->CanWriteFile()) { qtss_printf("Cannot write to the config prefs file.\n"); ::exit(0); } // If we aren't forced to create a new XML prefs file, whether // we do or not depends solely on whether the XML prefs file exists currently. if (theXMLPrefsExist) theXMLPrefsExist = sXMLParser->DoesFileExist(); //解析具体的xml文件 int xmlParseErr = sXMLParser->Parse(); if (xmlParseErr) { qtss_printf("Fatal Error: Could not load configuration file at %s. (%d)\n", theXMLFilePath, OSThread::GetErrno()); ::exit(-1); } //根据qtssmessages.txt配置系统输出的本地化log sMessagesSource.InitFromConfigFile("qtssmessages.txt"); // // Start Win32 DLLs WORD wsVersion = MAKEWORD(1, 1); WSADATA wsData; (void)::WSAStartup(wsVersion, &wsData); if (notAService) { // If we're running off the command-line, don't do the service initiation crap. ::StartServer(sXMLParser, &sMessagesSource, sPort, sStatsUpdateInterval, sInitialState, false,0, kRunServerDebug_Off); // No stats update interval for now ::RunServer(); ::exit(0); } SERVICE_TABLE_ENTRY dispatchTable[] = { { "", ServiceMain }, { NULL, NULL } }; // // In case someone runs the server improperly, print out a friendly message. qtss_printf("CMS must either be started from the DOS Console\n"); qtss_printf("using the -d command-line option, or using the Service Control Manager\n\n"); qtss_printf("Waiting for the Service Control Manager to start CMS...\n"); BOOL theErr = ::StartServiceCtrlDispatcher(dispatchTable); if (!theErr) { qtss_printf("Fatal Error: Couldn't start Service\n"); ::exit(-1); } return (0); }
void QTSSExpirationDate::PrintExpirationDate() { if (sIsExpirationEnabled) qtss_printf("Software expires on: %s\n", sExpirationDate); }
QTSS_Error RTSPRequestStream::ReadRequest() { while (true) { UInt32 newOffset = 0; //If this is the case, we already HAVE a request on this session, and we now are done //with the request and want to move onto the next one. The first thing we should do //is check whether there is any lingering data in the stream. If there is, the parent //session believes that is part of a new request if (fRequestPtr != NULL) { fRequestPtr = NULL;//flag that we no longer have a complete request // Take all the retreated leftover data and move it to the beginning of the buffer if ((fRetreatBytes > 0) && (fRequest.Len > 0)) ::memmove(fRequest.Ptr, fRequest.Ptr + fRequest.Len + fRetreatBytesRead, fRetreatBytes); // if we are decoding, we need to also move over the remaining encoded bytes // to the right position in the fRequestBuffer if (fEncodedBytesRemaining > 0) { //Assert(fEncodedBytesRemaining < 4); // The right position is at fRetreatBytes offset in the request buffer. The reason for this is: // 1) We need to find a place in the request buffer where we know we have enough space to store // fEncodedBytesRemaining. fRetreatBytes + fEncodedBytesRemaining will always be less than // kRequestBufferSize because all this data must have been in the same request buffer, together, at one point. // // 2) We need to make sure that there is always more data in the RequestBuffer than in the decoded // request buffer, otherwise we could overrun the decoded request buffer (we bounds check on the encoded // buffer, not the decoded buffer). Leaving fRetreatBytes as empty space in the request buffer ensures // that this principle is maintained. ::memmove(&fRequestBuffer[fRetreatBytes], &fRequestBuffer[fCurOffset - fEncodedBytesRemaining], fEncodedBytesRemaining); fCurOffset = fRetreatBytes + fEncodedBytesRemaining; Assert(fCurOffset < kRequestBufferSizeInBytes); } else fCurOffset = fRetreatBytes; newOffset = fRequest.Len = fRetreatBytes; fRetreatBytes = fRetreatBytesRead = 0; } // We don't have any new data, so try and get some if (newOffset == 0) { if (fRetreatBytes > 0) { // This will be true if we've just snarfed another input stream, in which case the encoded data // is copied into our request buffer, and its length is tracked in fRetreatBytes. // If this is true, just fall through and decode the data. newOffset = fRetreatBytes; fRetreatBytes = 0; Assert(fEncodedBytesRemaining == 0); } else { // We don't have any new data, get some from the socket... QTSS_Error sockErr = fSocket->Read(&fRequestBuffer[fCurOffset], (kRequestBufferSizeInBytes - fCurOffset) - 1, &newOffset); //assume the client is dead if we get an error back if (sockErr == EAGAIN) return QTSS_NoErr; if (sockErr != QTSS_NoErr) { Assert(!fSocket->IsConnected()); return sockErr; } } if (fDecode) { // If we need to decode this data, do it now. Assert(fCurOffset >= fEncodedBytesRemaining); QTSS_Error decodeErr = this->DecodeIncomingData(&fRequestBuffer[fCurOffset - fEncodedBytesRemaining], newOffset + fEncodedBytesRemaining); // If the above function returns an error, it is because we've // encountered some non-base64 data in the stream. We can process // everything up until that point, but all data after this point will // be ignored. if (decodeErr == QTSS_NoErr) Assert(fEncodedBytesRemaining < 4); } else fRequest.Len += newOffset; Assert(fRequest.Len < kRequestBufferSizeInBytes); fCurOffset += newOffset; } Assert(newOffset > 0); // See if this is an interleaved data packet if ('$' == *(fRequest.Ptr)) { if (fRequest.Len < 4) continue; UInt16* dataLenP = (UInt16*)fRequest.Ptr; UInt32 interleavedPacketLen = ntohs(dataLenP[1]) + 4; if (interleavedPacketLen > fRequest.Len) continue; //put back any data that is not part of the header fRetreatBytes += fRequest.Len - interleavedPacketLen; fRequest.Len = interleavedPacketLen; fRequestPtr = &fRequest; fIsDataPacket = true; return QTSS_RequestArrived; } fIsDataPacket = false; if (fPrintRTSP) { DateBuffer theDate; DateTranslator::UpdateDateBuffer(&theDate, 0); // get the current GMT date and time qtss_printf("\n\n#C->S:\n#time: ms=%lu date=%s\n", (UInt32) OS::StartTimeMilli_Int(), theDate.GetDateBuffer()); if (fSocket != NULL) { UInt16 serverPort = fSocket->GetLocalPort(); UInt16 clientPort = fSocket->GetRemotePort(); StrPtrLen* theLocalAddrStr = fSocket->GetLocalAddrStr(); StrPtrLen* theRemoteAddrStr = fSocket->GetRemoteAddrStr(); if (theLocalAddrStr != NULL) { qtss_printf("#server: ip="); theLocalAddrStr->PrintStr(); qtss_printf(" port=%u\n" , serverPort ); } else { qtss_printf("#server: ip=NULL port=%u\n" , serverPort ); } if (theRemoteAddrStr != NULL) { qtss_printf("#client: ip="); theRemoteAddrStr->PrintStr(); qtss_printf(" port=%u\n" , clientPort ); } else { qtss_printf("#client: ip=NULL port=%u\n" , clientPort ); } } StrPtrLen str(fRequest); str.PrintStrEOL("\n\r\n", "\n");// print the request but stop on \n\r\n and add a \n afterwards. } //use a StringParser object to search for a double EOL, which signifies the end of //the header. Bool16 weAreDone = false; StringParser headerParser(&fRequest); UInt16 lcount = 0; while (headerParser.GetThruEOL(NULL)) { lcount++; if (headerParser.ExpectEOL()) { //The legal end-of-header sequences are \r\r, \r\n\r\n, & \n\n. NOT \r\n\r! //If the packets arrive just a certain way, we could get here with the latter //combo, and not wait for a final \n. if ((headerParser.GetDataParsedLen() > 2) && (memcmp(headerParser.GetCurrentPosition() - 3, "\r\n\r", 3) == 0)) continue; weAreDone = true; break; } else if (lcount == 1) { // if this request is actually a ShoutCast password it will be // in the form of "xxxxxx\r" where "xxxxx" is the password. // If we get a 1st request line ending in \r with no blanks we will // assume that this is the end of the request. UInt16 flag = 0; UInt16 i = 0; for (i=0; i<fRequest.Len; i++) { if (fRequest.Ptr[i] == ' ') flag++; } if (flag == 0) { weAreDone = true; break; } } } //weAreDone means we have gotten a full request if (weAreDone) { //put back any data that is not part of the header fRequest.Len -= headerParser.GetDataRemaining(); fRetreatBytes += headerParser.GetDataRemaining(); fRequestPtr = &fRequest; return QTSS_RequestArrived; } //check for a full buffer if (fCurOffset == kRequestBufferSizeInBytes - 1) { fRequestPtr = &fRequest; return E2BIG; } } }
void Task::Signal(EventFlags events) { if (!this->Valid()) return; //Fancy no mutex implementation. We atomically mask the new events into //the event mask. Because atomic_or returns the old state of the mask, //we only schedule this task once. events |= kAlive; EventFlags oldEvents = atomic_or(&fEvents, events); if ((!(oldEvents & kAlive)) && (TaskThreadPool::sNumTaskThreads > 0)) { if (fDefaultThread != NULL && fUseThisThread == NULL) fUseThisThread = fDefaultThread; if (fUseThisThread != NULL)// Task needs to be placed on a particular thread. { if (TASK_DEBUG) { if (fTaskName[0] == 0) ::strcpy(fTaskName, " _Corrupt_Task"); qtss_printf("Task::Signal EnQueue TaskName=%s fUseThisThread=%p q_elem=%p enclosing=%p\n", fTaskName, (void *)fUseThisThread, (void *)&fTaskQueueElem, (void *) this); if (TaskThreadPool::sTaskThreadArray[0] == fUseThisThread) qtss_printf("Task::Signal RTSP Thread running TaskName=%s \n", fTaskName); } fUseThisThread->fTaskQueue.EnQueue(&fTaskQueueElem); } else { //find a thread to put this task on unsigned int theThreadIndex = atomic_add((unsigned int *)pickerToUse, 1); if (&Task::sShortTaskThreadPicker == pickerToUse) { theThreadIndex %= TaskThreadPool::sNumShortTaskThreads; if (TASK_DEBUG) qtss_printf("Task::Signal EnQueue TaskName=%s using Task::sShortTaskThreadPicker=%u numShortTaskThreads=%" _U32BITARG_ " short task range=[0-%" _U32BITARG_ "] thread index =%u \n", fTaskName, Task::sShortTaskThreadPicker, TaskThreadPool::sNumShortTaskThreads, TaskThreadPool::sNumShortTaskThreads - 1, theThreadIndex); } else if (&Task::sBlockingTaskThreadPicker == pickerToUse) { theThreadIndex %= TaskThreadPool::sNumBlockingTaskThreads; theThreadIndex += TaskThreadPool::sNumShortTaskThreads; //don't pick from lower non-blocking (short task) threads. if (TASK_DEBUG) qtss_printf("Task::Signal EnQueue TaskName=%s using Task::sBlockingTaskThreadPicker=%u numBlockingThreads=%" _U32BITARG_ " blocking thread range=[%" _U32BITARG_ "-%" _U32BITARG_ "] thread index =%u \n", fTaskName, Task::sBlockingTaskThreadPicker, TaskThreadPool::sNumBlockingTaskThreads, TaskThreadPool::sNumShortTaskThreads, TaskThreadPool::sNumBlockingTaskThreads + TaskThreadPool::sNumShortTaskThreads - 1, theThreadIndex); } else { if (TASK_DEBUG) if (fTaskName[0] == 0) ::strcpy(fTaskName, " _Corrupt_Task"); return; } if (TASK_DEBUG) if (fTaskName[0] == 0) ::strcpy(fTaskName, " _Corrupt_Task"); if (TASK_DEBUG) qtss_printf("Task::Signal EnQueue B TaskName=%s theThreadIndex=%u thread=%p fTaskQueue.GetLength(%" _U32BITARG_ ") q_elem=%p enclosing=%p\n", fTaskName, theThreadIndex, (void *)TaskThreadPool::sTaskThreadArray[theThreadIndex], TaskThreadPool::sTaskThreadArray[theThreadIndex]->fTaskQueue.GetQueue()->GetLength(), (void *)&fTaskQueueElem, (void *) this); TaskThreadPool::sTaskThreadArray[theThreadIndex]->fTaskQueue.EnQueue(&fTaskQueueElem); if (TASK_DEBUG) qtss_printf("Task::Signal EnQueue A TaskName=%s theThreadIndex=%u thread=%p fTaskQueue.GetLength(%" _U32BITARG_ ") q_elem=%p enclosing=%p\n", fTaskName, theThreadIndex, (void *)TaskThreadPool::sTaskThreadArray[theThreadIndex], TaskThreadPool::sTaskThreadArray[theThreadIndex]->fTaskQueue.GetQueue()->GetLength(), (void *)&fTaskQueueElem, (void *) this); } } else if (TASK_DEBUG) qtss_printf("Task::Signal Sent to dead TaskName=%s q_elem=%p enclosing=%p\n", fTaskName, (void *)&fTaskQueueElem, (void *) this); }
void PrintStatus(Bool16 printHeader) { char* thePrefStr = NULL; UInt32 theLen = 0; if ( printHeader ) { qtss_printf(" RTP-Conns RTSP-Conns HTTP-Conns kBits/Sec Pkts/Sec TotConn TotBytes TotPktsLost Time\n"); } (void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurConn, 0, &thePrefStr); qtss_printf( "%11s", thePrefStr); delete [] thePrefStr; thePrefStr = NULL; (void)QTSS_GetValueAsString(sServer, qtssRTSPCurrentSessionCount, 0, &thePrefStr); qtss_printf( "%11s", thePrefStr); delete [] thePrefStr; thePrefStr = NULL; (void)QTSS_GetValueAsString(sServer, qtssRTSPHTTPCurrentSessionCount, 0, &thePrefStr); qtss_printf( "%11s", thePrefStr); delete [] thePrefStr; thePrefStr = NULL; UInt32 curBandwidth = 0; theLen = sizeof(curBandwidth); (void)QTSS_GetValue(sServer, qtssRTPSvrCurBandwidth, 0, &curBandwidth, &theLen); qtss_printf("%11"_U32BITARG_, curBandwidth/1024); (void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurPackets, 0, &thePrefStr); qtss_printf( "%11s", thePrefStr); delete [] thePrefStr; thePrefStr = NULL; (void)QTSS_GetValueAsString(sServer, qtssRTPSvrTotalConn, 0, &thePrefStr); qtss_printf( "%11s", thePrefStr); delete [] thePrefStr; thePrefStr = NULL; UInt64 totalBytes = sServer->GetTotalRTPBytes(); char displayBuff[32] = ""; FormattedTotalBytesBuffer(displayBuff, sizeof(displayBuff),totalBytes); qtss_printf( "%17s", displayBuff); qtss_printf( "%11"_64BITARG_"u", sServer->GetTotalRTPPacketsLost()); char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes]; (void) QTSSRollingLog::FormatDate(theDateBuffer, false); qtss_printf( "%25s",theDateBuffer); qtss_printf( "\n"); }
BroadcasterSession::BroadcasterSession( UInt32 inAddr, UInt16 inPort, char* inURL, BroadcasterType inClientType, UInt32 inDurationInSec, UInt32 inStartPlayTimeInSec, UInt32 inRTCPIntervalInSec, UInt32 inOptionsIntervalInSec, UInt32 inHTTPCookie, Bool16 inAppendJunkData, UInt32 inReadInterval, UInt32 inSockRcvBufSize, StrPtrLen *sdpSPLPtr, char *namePtr, char *passwordPtr, Bool16 deepDebug, Bool16 burst) : fSocket(NULL), fRTSPClient(NULL), fTimeoutTask(NULL, kIdleTimeoutInMsec), fDurationInSec(inDurationInSec), fStartPlayTimeInSec(inStartPlayTimeInSec), fRTCPIntervalInSec(inRTCPIntervalInSec), fOptionsIntervalInSec(inOptionsIntervalInSec), fState(kSendingAnnounce), fDeathState(kSendingAnnounce), fDeathReason(kDiedNormally), fNumSetups(0), fUDPSocketArray(NULL), fPlayTime(0), fTotalPlayTime(0), fLastRTCPTime(0), fTeardownImmediately(false), fAppendJunk(inAppendJunkData), fReadInterval(inReadInterval), fSockRcvBufSize(inSockRcvBufSize), fBurst(burst), fBurstTime(10), fStats(NULL), fPacketLen(0), fChannel(0) // fPacket(NULL) { fTimeoutTask.SetTask(this); StrPtrLen theURL(inURL); fSDPParser.Parse(sdpSPLPtr->Ptr, sdpSPLPtr->Len); if (fBurst && deepDebug) printf("Burst Mode enabled: broadcast will be delayed for %"_U32BITARG_" seconds before starting\n", fBurstTime); #if BROADCAST_SESSION_DEBUG qtss_printf("Connecting to: %s, port %d\n", inURL, inPort); #endif // // Construct the appropriate ClientSocket type depending on what type of client we are supposed to be switch (inClientType) { case kRTSPUDPBroadcasterType: { fControlType = kRawRTSPControlType; fTransportType = kUDPTransportType; fSocket = NEW TCPClientSocket(Socket::kNonBlockingSocketType); break; } case kRTSPTCPBroadcasterType: { fControlType = kRawRTSPControlType; fTransportType = kTCPTransportType; fSocket = NEW TCPClientSocket(Socket::kNonBlockingSocketType); break; } case kRTSPHTTPBroadcasterType: { fControlType = kRTSPHTTPControlType; fTransportType = kTCPTransportType; fSocket = NEW HTTPClientSocket(theURL, inHTTPCookie, Socket::kNonBlockingSocketType); break; } case kRTSPHTTPDropPostBroadcasterType: { fControlType = kRTSPHTTPDropPostControlType; fTransportType = kTCPTransportType; fSocket = NEW HTTPClientSocket(theURL, inHTTPCookie, Socket::kNonBlockingSocketType); break; } case kRTSPReliableUDPBroadcasterType: { Assert(0); break; } default: { qtss_printf("BroadcasterSession: Attempt to create unsupported client type.\n"); ::exit(-1); } } fSocket->Set(inAddr, inPort); fSocket->GetSocket()->SetTask(this); int sndBufSize = 32 * 1024; int rcvBufSize=1024; ((TCPClientSocket*)fSocket)->SetOptions(sndBufSize,rcvBufSize); // // Construct the client object using this socket. Bool16 verbose = deepDebug; fRTSPClient = NEW RTSPClient(fSocket, verbose); fRTSPClient->Set(theURL); fRTSPClient->SetTransportMode(RTSPClient::kPushMode); fRTSPClient->SetName(namePtr); fRTSPClient->SetPassword(passwordPtr); // // Start the connection process going this->Signal(Task::kStartEvent); }
static Bool16 CallAuthorizeSession(QTSS_ClientSessionObject* theClientSession, QTSS_RTSPSessionObject* theRTSPSession, QTSS_RTSPRequestObject* theRTSPRequest, char* username, char* password) { qtss_printf("QTSSIcecastAuthModule::CallAuthorizeSession called\n"); //{ :action => "listener_add", :server => "server", :port => "8000", :client => "sessionidone", // :mount => "somemount.sdp", :user => "lstoll", :pass => @working_hash, :ip => "127.0.0.1", :agent => "RSPEC"} // generate the client session id (and save in client) format <start time millis>-<rtsp session id> char ice_sessid[128]; QTSS_TimeVal clientSessCreateTime = NULL; UInt32 createTimeLen = sizeof (clientSessCreateTime); QTSS_GetValue(*theClientSession, qtssCliSesCreateTimeInMsec, 0, (void*) & clientSessCreateTime, &createTimeLen); char* qtssRTSPSesIDString = NULL; (void) QTSS_GetValueAsString(*theRTSPSession, qtssRTSPSesID, 0, &qtssRTSPSesIDString); sprintf(ice_sessid, "%lld-%s", clientSessCreateTime, qtssRTSPSesIDString); printf("QTSSIcecastAuthModule::CallAuthorizeSession generated session id: %s\n", ice_sessid); (void) QTSS_SetValue(*theClientSession, attrClientSessionFullSessionID, 0, &ice_sessid, sizeof (ice_sessid)); // get the user agent char* userAgentString = NULL; (void) QTSS_GetValueAsString(*theClientSession, qtssCliSesFirstUserAgent, 0, &userAgentString); printf("QTSSIcecastAuthModule::CallAuthorizeSession: request user agent: %s\n", userAgentString); // get the client IP address char remoteAddress[20] = {0}; StrPtrLen theClientIPAddressStr(remoteAddress,sizeof(remoteAddress)); (void)QTSS_GetValue(*theRTSPSession, qtssRTSPSesRemoteAddrStr, 0, (void*)theClientIPAddressStr.Ptr, &theClientIPAddressStr.Len); // get the mount point char mountPoint[128] = {0}; StrPtrLen mountPointStr(mountPoint,sizeof(mountPoint)); (void)QTSS_GetValue(*theRTSPRequest, qtssRTSPReqURI, 0, (void*)mountPointStr.Ptr, &mountPointStr.Len); printf("QTSSIcecastAuthModule::CallAuthorizeSession: mount point: %s\n", mountPoint); // and set it in the client for use on session end (void) QTSS_SetValue(*theClientSession, attrClientSessionMountPoint, 0, mountPointStr.Ptr, mountPointStr.Len); char postdata[512]; qtss_sprintf(postdata, "action=listener_add&server=%s&port=554&client=%s&mount=%s&user=%s&pass=%s&ip=%s&agent%s", hostname, ice_sessid, mountPoint, username, password, remoteAddress, userAgentString); printf("QTSSIcecastAuthModule::CallAuthorizeSession: generated postdata: %s\n", postdata); printf("QTSSIcecastAuthModule::CallAuthorizeSession: i would post this to: %s\n", sStartSessionEndpoint); return true; // // CURL *easyhandle = NULL; // easyhandle = curl_easy_init(); // CURLcode curl_code; // // // curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, postdata); // curl_easy_setopt(easyhandle, CURLOPT_URL, "http://posthere.com/"); // curl_easy_perform(easyhandle); /* post away! */ // // long http_code = 0; // curl_easy_getinfo(easyhandle, CURLINFO_HTTP_CODE, &http_code); // if (http_code == 200 && curl_code != CURLE_ABORTED_BY_CALLBACK) { // // the call to the remote server was OK. pass. // return true; // } else { // return false; // } // return false; }
void EasyCameraSource::doStopGettingFrames() { qtss_printf("doStopGettingFrames()\n"); netDevStopStream(); }
/* * This method is called because it is the only one that gets the full URL params, to capture * the username and password. We don't have access to the client session here, so save * the user and pass in the session for later. This is called BEFORE PostProcess */ QTSS_Error RTSPFilter(QTSS_StandardRTSP_Params* inParams) { QTSS_Error theErr = QTSS_NoErr; QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest; QTSS_RTSPSessionObject theRTSPSession = inParams->inRTSPSession; // FAIL - can't do this here. need to finally move the auth to preprocess, and use this // just for param capture. // On the flip site, it seems that everything is really early here - if query params // don't work, can embed them in the URL path, extract here, and rewrite the URL in the request // and it should work fine. this could be handy if proxy's become an issue, because each request // would have a unique path. // // see if the method is bypassable. if it is, skip processing // qtss_printf("QTSSIcecastAuthModule::RTSPFilter: about to check if the method is bypassable\n"); // if (IsRequestMethodBypassable(&theRTSPRequest)) return QTSS_NoErr; // see if the client is in the bypass list. if they are, skip all processing if (IsClientInBypassList(&theRTSPSession)) return QTSS_NoErr; // check to see if the session is already auth'd. If it is, skip processing if (IsRTSPSessionAuthenticated(&theRTSPSession)) { printf("QTSSIcecastAuthModule::RTSPFilter RTSP session is authenticated, do nothing.\n"); return QTSS_NoErr; // we are authenticated, don't do anything } char* qtssRTSPSesIDString = NULL; (void) QTSS_GetValueAsString(theRTSPSession, qtssRTSPSesID, 0, &qtssRTSPSesIDString); printf("QTSSIcecastAuthModule::RTSPFilter session qtssRTSPSesID: %s\n", qtssRTSPSesIDString); char* qtssRTSPReqFullRequestString = NULL; (void)QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqFullRequest, 0, &qtssRTSPReqFullRequestString); qtss_printf("QTSSIcecastAuthModule::RTSPFilter: request qtssRTSPReqFullRequest: %s\n", qtssRTSPReqFullRequestString); /* will want to modify this for proper tokenization, but it works for now */ char username[255]; bool usernameset = false; char password[255]; bool passwordset = false; Bool16 requiredAuthParametersProvided = false; if(index(qtssRTSPReqFullRequestString, '?')){ char buf[512]; snprintf(buf, 512, qtssRTSPReqFullRequestString); char* queryString; char* progress1; // split off everything after the first line, we don't need it. queryString = ::strtok_r(buf, "\n", &progress1); // split around the ?, ignore the first part ::strtok_r(buf, "?", &progress1); // get the second part of the previous split queryString = ::strtok_r(NULL, "?", &progress1); // split working around the space queryString = ::strtok_r(queryString, " ", &progress1); //printf("queryString: %s\n", queryString); // we should now have our url char* tmp = strtok(queryString, "=&"); int iters; for (iters=0; (tmp != NULL); iters++) { char name[255]; // I'm asking for a buffer overflow, aren't I? TODO - check this. if ((iters % 2) != 1) { // even - its a name. this will always be 'first' strcpy(name, tmp); //printf("name: %s\n", tmp); } else { // non-even, its a value. this will always come second //printf("value: %s\n", tmp); if (strcmp(name, "u") == 0) { // this value is the username //printf("name is currently: %s. username being set to %s\n", name, tmp); strcpy(username, tmp); usernameset = true; } else if (strcmp(name, "p") == 0) { // this value is the username //printf("name is currently: %s. password being set to %s\n", name, tmp); strcpy(password, tmp); passwordset = true; } } tmp = strtok(NULL, "=&"); } //printf("username: %s, password: %s\n\n", username, password); if (usernameset && passwordset) { printf("QTSSIcecastAuthModule::RTSPFilter username and password have been provided.\n"); requiredAuthParametersProvided = true; } } if (requiredAuthParametersProvided) { // we have a username and password. set them on the RTSP session, so they can be validated later. QTSS_Error setErr = QTSS_SetValue(theRTSPSession, attrRtspSessionProvidedUsername, 0, &username, sizeof(username)); QTSS_SetValue(theRTSPSession, attrRtspSessionProvidedPassword, 0, &password, sizeof(password)); PrintQTSSError("QTSSIcecastAuthModule::RTSPFilter", "after username set", setErr); char* providedUsername = NULL; (void)QTSS_GetValueAsString(theRTSPSession, attrRtspSessionProvidedUsername, 0, &providedUsername); printf("QTSSIcecastAuthModule::RTSPFilter: Provided username extracted from session right after set: %s\n", providedUsername); char* providedPassword = NULL; (void) QTSS_GetValueAsString(theRTSPSession, attrRtspSessionProvidedPassword, 0, &providedPassword); printf("QTSSIcecastAuthModule::RTSPFilter: Provided password extracted from session right after set: %s\n", providedPassword); } else { // WRONG. username and password weren't provided, do nothing - we will handle later. } // TODO - we should be cleaning up more things here, I think. QTSS_Delete(qtssRTSPReqFullRequestString); return QTSS_NoErr; }
/* * This method is used to capture the full session ID details, and to reject the session. * The username and password from the query string can't be grabbed here, we need to * do that in the Filter. This is called AFTER Filter */ QTSS_Error RTSPPreProcess(QTSS_StandardRTSP_Params* inParams) { QTSS_Error theErr = QTSS_NoErr; QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest; QTSS_RTSPSessionObject theRTSPSession = inParams->inRTSPSession; QTSS_RTSPHeaderObject theRTSPHeader = inParams->inRTSPHeaders; QTSS_ClientSessionObject theClientSession = inParams->inClientSession; Bool16 sessionValid = false; // see if the method is bypassable. if it is, skip processing qtss_printf("QTSSIcecastAuthModule::RTSPPreProcess: about to check if the method is bypassable\n"); if (IsRequestMethodBypassable(&theRTSPRequest)) return QTSS_NoErr; // see if the client is in the bypass list. if they are, skip all processing if (IsClientInBypassList(&theRTSPSession)) return QTSS_NoErr; // check to see if the session is already auth'd. If it is, skip processing if (IsRTSPSessionAuthenticated(&theRTSPSession)) { printf("QTSSIcecastAuthModule::RTSPPreProcess RTSP session is authenticated, do nothing.\n"); return QTSS_NoErr; // we are authenticated, don't do anything } char* providedUsername = NULL; (void)QTSS_GetValueAsString(theRTSPSession, attrRtspSessionProvidedUsername, 0, &providedUsername); printf("QTSSIcecastAuthModule::RTSPPreProcess: Provided username extracted from session: %s\n", providedUsername); char* providedPassword = NULL; (void)QTSS_GetValueAsString(theRTSPSession, attrRtspSessionProvidedPassword, 0, &providedPassword); printf("QTSSIcecastAuthModule::RTSPPreProcess: Provided password extracted from session: %s\n", providedPassword); // check to see if the username and password have been provided. If they are, process. if not // do nothing, we will default to an invalid session if (providedUsername != NULL && providedPassword != NULL) { printf("QTSSIcecastAuthModule::RTSPPreProcess: about to call authorize session\n"); // if we get to this point we have credentials that need to be validated, so validate them. sessionValid = CallAuthorizeSession(&theClientSession, &theRTSPSession, &theRTSPRequest, providedUsername, providedPassword); // request, qtssRTSPReqFilePath - the mount point (?) // request, qtssRTSPReqURI - the request URI (query params parsed from here?) // request, qtssRTSPReqAbsoluteURL - the request url with RTSP. // request, qtssRTSPReqFullRequest - the full request // session, qtssRTSPSesID - the session id // note - QTSS_GetValueAsString is the least efficent method - should move to one of the more efficent methods. // // char* qtssRTSPReqFilePathString = NULL; // (void)QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqFilePath, 0, &qtssRTSPReqFilePathString); // printf("QTSSIcecastAuthModule::RTSPPreProcess: request qtssRTSPReqFilePath: %s\n", qtssRTSPReqFilePathString); // // char* qtssRTSPReqURIString = NULL; // (void)QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqURI, 0, &qtssRTSPReqURIString); // printf("QTSSIcecastAuthModule::RTSPPreProcess: request qtssRTSPReqURI: %s\n", qtssRTSPReqURIString); // // char* qtssRTSPReqAbsoluteURLString = NULL; // (void)QTSS_GetValueAsString(theRTSPRequest, qtssRTSPReqAbsoluteURL, 0, &qtssRTSPReqAbsoluteURLString); // printf("QTSSIcecastAuthModule::RTSPPreProcess: request qtssRTSPReqAbsoluteURL: %s\n", qtssRTSPReqAbsoluteURLString); // //QTSS_Delete(qtssRTSPReqFilePathString); // QTSS_Delete(qtssRTSPReqURIString); // //QTSS_Delete(qtssRTSPReqAbsoluteURLString); // QTSS_Delete(qtssRTSPSesIDString); } else { printf("QTSSIcecastAuthModule::RTSPPreProcess: username and/or password are NULL\n"); } // set the auth status on the RTSP session (void)QTSS_SetValue(theRTSPSession, attrRtspSessionAuthenticated, 0, &sessionValid, sizeof(sessionValid)); if (sessionValid) { // valid session, return return QTSS_NoErr; } else { // not a valid session, error char* accessDeniedMessage = "Access DENIED"; StrPtrLen accessDeniedMessageStr(accessDeniedMessage,sizeof(accessDeniedMessage)); (void)QTSSModuleUtils::SendErrorResponseWithMessage(theRTSPRequest, qtssClientForbidden, &accessDeniedMessageStr); return QTSS_NoErr; } }
QTSS_Error Register() { QTSS_Error theErr; // Do role & attribute setup // for when the server starts theErr = QTSS_AddRole(QTSS_Initialize_Role); //PrintQTSSError("QTSSIcecastAuthModule::Register", "register for initialize role", theErr); // for when asked to re-read the prefs file qtss_printf("QTSSIcecastAuthModule::Register about to register for reread prefs role\n"); (void)QTSS_AddRole(QTSS_RereadPrefs_Role); qtss_printf("QTSSIcecastAuthModule::Register after register for reread prefs role, about to register for filter\n"); // can't find doc on these - apparently deprecated as of v3?? //(void)QTSS_AddRole(QTSS_RTSPAuthenticate_Role); //(void)QTSS_AddRole(QTSS_RTSPAuthorize_Role); // the earliest call on a RTSP request - needed to get the full query including // query params. (void)QTSS_AddRole(QTSS_RTSPFilter_Role); // called to pre-process a RTSP request - has full client info, including timestamp // for completely unique session ID's (rather then the increment) (void)QTSS_AddRole(QTSS_RTSPPreProcessor_Role); // the shutdown role, for cleanup stuff (void)QTSS_AddRole(QTSS_Shutdown_Role); // The client close role, to send the end message (void)QTSS_AddRole(QTSS_ClientSessionClosing_Role); qtss_printf("QTSSIcecastAuthModule::Register all roles registered, about to register attributes\n"); // add the attribute to hold the username when provided (void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, "ProvidedUsername", NULL, qtssAttrDataTypeCharArray); (void)QTSS_IDForAttr(qtssRTSPSessionObjectType, "ProvidedUsername", &attrRtspSessionProvidedUsername); // add the attribute to hold the username when provided (void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, "ProvidedPassword", NULL, qtssAttrDataTypeCharArray); (void)QTSS_IDForAttr(qtssRTSPSessionObjectType, "ProvidedPassword", &attrRtspSessionProvidedPassword); // add the attribute that holds the flag to show if the session has been authenticated // we check this first, if the session is authenticated we can skip everything theErr = QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, "SessionAuthenticated", NULL, qtssAttrDataTypeBool16); //PrintQTSSError("QTSSIcecastAuthModule::Register", "add session authenticated attribute to rtsp session", theErr); theErr = QTSS_IDForAttr(qtssRTSPSessionObjectType, "SessionAuthenticated", &attrRtspSessionAuthenticated); //PrintQTSSError("QTSSIcecastAuthModule::Register", "get ID for session authenticated attribute on rtsp session", theErr); // add to hold the 'full' session ID on the RTSP Session (start time millis CONCAT sever session id, to ensure uniqueness) (void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, "FullSessionID", NULL, qtssAttrDataTypeCharArray); (void)QTSS_IDForAttr(qtssClientSessionObjectType, "FullSessionID", &attrClientSessionFullSessionID); // the mount point needs to be stashed in the client session, for reporting on teardown (void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, "MountPoint", NULL, qtssAttrDataTypeCharArray); (void)QTSS_IDForAttr(qtssClientSessionObjectType, "MountPoint", &attrClientSessionMountPoint); qtss_printf("QTSSIcecastAuthModule::Register end of method\n"); return QTSS_NoErr; // need to return. should do something with any errors captured above. }
// // 解析HTTPRequest对象fRequest报文 QTSS_Error EasyCMSSession::ProcessRequest() { if(NULL == fRequest) return QTSS_BadArgument; //解析HTTPRequest报文 QTSS_Error theErr = fRequest->Parse(); if (theErr != QTSS_NoErr) return QTSS_BadArgument; //获取具体Content json数据部分 StrPtrLen* lengthPtr = fRequest->GetHeaderValue(httpContentLengthHeader); StringParser theContentLenParser(lengthPtr); theContentLenParser.ConsumeWhitespace(); UInt32 content_length = theContentLenParser.ConsumeInteger(NULL); if (content_length) { qtss_printf("EasyCMSSession::ProcessRequest read content-length:%d \n", content_length); // 检查content的fContentBuffer和fContentBufferOffset是否有值存在,如果存在,说明我们已经开始 // 进行content请求处理,如果不存在,我们需要创建并初始化fContentBuffer和fContentBufferOffset if (fContentBuffer == NULL) { fContentBuffer = NEW char[content_length + 1]; memset(fContentBuffer,0,content_length + 1); fContentBufferOffset = 0; } UInt32 theLen = 0; // 读取HTTP Content报文数据 theErr = fInputStream.Read(fContentBuffer + fContentBufferOffset, content_length - fContentBufferOffset, &theLen); Assert(theErr != QTSS_BadArgument); if (theErr == QTSS_RequestFailed) { OSCharArrayDeleter charArrayPathDeleter(fContentBuffer); fContentBufferOffset = 0; fContentBuffer = NULL; return QTSS_RequestFailed; } qtss_printf("EasyCMSSession::ProcessRequest() Add Len:%d \n", theLen); if ((theErr == QTSS_WouldBlock) || (theLen < ( content_length - fContentBufferOffset))) { // // Update our offset in the buffer fContentBufferOffset += theLen; Assert(theErr == QTSS_NoErr); return QTSS_WouldBlock; } Assert(theErr == QTSS_NoErr); // 处理完成报文后会自动进行Delete处理 OSCharArrayDeleter charArrayPathDeleter(fContentBuffer); EasyProtocol protocol(fContentBuffer); int nNetMsg = protocol.GetMessageType(); switch (nNetMsg) { case MSG_DEV_CMS_REGISTER_RSP: //{ // EasyDarwinRegisterRSP rsp_parse(fContentBuffer); // qtss_printf("session id = %s\n", rsp_parse.GetBodyValue("SessionID").c_str()); // qtss_printf("device serial = %s\n", rsp_parse.GetBodyValue("DeviceSerial").c_str()); //} break; case MSG_CMS_DEV_STREAM_START_REQ: { //EasyDarwinDeviceStreamReq startStreamReq(fContentBuffer); //qtss_printf("DeviceSerial = %s\n", startStreamReq.GetBodyValue("DeviceSerial").c_str()); //qtss_printf("CameraSerial = %s\n", startStreamReq.GetBodyValue("CameraSerial").c_str()); //qtss_printf("DssIP = %s\n", startStreamReq.GetBodyValue("DssIP").c_str()); //char szIP[16] = {0,}; //strcpy(szIP, (char*)startStreamReq.GetBodyValue("DssIP").c_str()); //qtss_printf("szIP = %s\n", szIP); //char *ip = (char*)startStreamReq.GetBodyValue("DssIP").c_str(); //qtss_printf("ip = %s\n", ip); //QTSS_RoleParams packetParams; //memset(&packetParams, 0x00, sizeof(QTSS_RoleParams)); //strcpy(packetParams.easyNVRParams.inNVRSerialNumber, (char*)sEasy_Serial); //strcpy(packetParams.easyNVRParams.inDeviceSerial, (char*)startStreamReq.GetBodyValue("DeviceSerial").c_str()); //strcpy(packetParams.easyNVRParams.inCameraSerial, (char*)startStreamReq.GetBodyValue("CameraSerial").c_str()); //packetParams.easyNVRParams.inStreamID = atoi(startStreamReq.GetBodyValue("StreamID").c_str()); //strcpy(packetParams.easyNVRParams.inProtocol, (char*)startStreamReq.GetBodyValue("Protocol").c_str()); //strcpy(packetParams.easyNVRParams.inDssIP, (char*)startStreamReq.GetBodyValue("DssIP").c_str()); //packetParams.easyNVRParams.inDssPort =atoi(startStreamReq.GetBodyValue("DssPort").c_str()); //QTSS_Error errCode = QTSS_NoErr; //UInt32 fCurrentModule=0; //UInt32 numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kStartStreamRole); //for (; fCurrentModule < numModules; fCurrentModule++) //{ // QTSSModule* theModule = QTSServerInterface::GetModule(QTSSModule::kStartStreamRole, fCurrentModule); // errCode = theModule->CallDispatch(Easy_NVRStartStream_Role, &packetParams); //} //fCurrentModule = 0; //EasyJsonValue body; //body["DeviceSerial"] = packetParams.easyNVRParams.inDeviceSerial; //body["CameraSerial"] = packetParams.easyNVRParams.inCameraSerial; //body["StreamID"] = packetParams.easyNVRParams.inStreamID; //EasyDarwinDeviceStreamRsp rsp(body, 1, errCode == QTSS_NoErr ? 200 : 404); //string msg = rsp.GetMsg(); //StrPtrLen jsonContent((char*)msg.data()); //HTTPRequest httpAck(&sServiceStr, httpResponseType); //if(httpAck.CreateResponseHeader()) //{ // if (jsonContent.Len) // httpAck.AppendContentLengthHeader(jsonContent.Len); // //Push msg to OutputBuffer // char respHeader[2048] = { 0 }; // StrPtrLen* ackPtr = httpAck.GetCompleteHTTPHeader(); // strncpy(respHeader,ackPtr->Ptr, ackPtr->Len); // fOutputStream.Put(respHeader); // if (jsonContent.Len > 0) // fOutputStream.Put(jsonContent.Ptr, jsonContent.Len); //} } break; case MSG_CMS_DEV_STREAM_STOP_REQ: { //EasyDarwinDeviceStreamStop stopStreamReq(fContentBuffer); //QTSS_RoleParams packetParams; //memset(&packetParams, 0x00, sizeof(QTSS_RoleParams)); //strcpy(packetParams.easyNVRParams.inNVRSerialNumber, (char*)sEasy_Serial); //strcpy(packetParams.easyNVRParams.inDeviceSerial, (char*)stopStreamReq.GetBodyValue("DeviceSerial").data()); //strcpy(packetParams.easyNVRParams.inCameraSerial, (char*)stopStreamReq.GetBodyValue("CameraSerial").data()); //packetParams.easyNVRParams.inStreamID = atoi(stopStreamReq.GetBodyValue("StreamID").data()); //strcpy(packetParams.easyNVRParams.inProtocol, ""); //strcpy(packetParams.easyNVRParams.inDssIP, ""); //packetParams.easyNVRParams.inDssPort = 0; //// 需要先判断DeviceSerial与当前设备的sEasy_Serial是否一致 //// TODO:: //UInt32 fCurrentModule=0; //QTSS_Error err = QTSS_NoErr; //UInt32 numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kStopStreamRole); //for (; fCurrentModule < numModules; fCurrentModule++) //{ // QTSSModule* theModule = QTSServerInterface::GetModule(QTSSModule::kStopStreamRole, fCurrentModule); // err = theModule->CallDispatch(Easy_NVRStopStream_Role, &packetParams); //} //fCurrentModule = 0; //EasyJsonValue body; //body["DeviceSerial"] = packetParams.easyNVRParams.inDeviceSerial; //body["CameraSerial"] = packetParams.easyNVRParams.inCameraSerial; //body["StreamID"] = packetParams.easyNVRParams.inStreamID; //EasyDarwinDeviceStreamStopRsp rsp(body, 1, err == QTSS_NoErr ? 200 : 404); //string msg = rsp.GetMsg(); ////回应 //StrPtrLen jsonContent((char*)msg.data()); //HTTPRequest httpAck(&sServiceStr, httpResponseType); //if(httpAck.CreateResponseHeader()) //{ // if (jsonContent.Len) // httpAck.AppendContentLengthHeader(jsonContent.Len); // //Push msg to OutputBuffer // char respHeader[2048] = { 0 }; // StrPtrLen* ackPtr = httpAck.GetCompleteHTTPHeader(); // strncpy(respHeader,ackPtr->Ptr, ackPtr->Len); // fOutputStream.Put(respHeader); // if (jsonContent.Len > 0) // fOutputStream.Put(jsonContent.Ptr, jsonContent.Len); //} } break; default: break; } }
SInt64 EasyCMSSession::Run() { OS_Error theErr = OS_NoErr; EventFlags events = this->GetEvents(); if(events & Task::kKillEvent) { qtss_printf("kill event but not handle it!\n"); } while(1) { switch (fState) { case kIdle://空闲 { qtss_printf("kIdle state \n"); if(!IsConnected()) { // TCPSocket未连接的情况,首先进行登录连接 Login(); } else { // TCPSocket已连接的情况下区分具体事件类型 if(events & Task::kStartEvent) { // CMSSession掉线,重新进行上线动作 if(kSessionOffline == fSessionStatus) Login(); } if(events & Task::kReadEvent) { // 对已连接的TCP进行消息读取 fState = kReadingRequest; } if(events & Task::kTimeoutEvent) { // 保活时间到,需要发送保活报文 Login(); } } // 如果有消息需要发送则进入发送流程 if (fOutputStream.GetBytesWritten() > 0) { fState = kSendingResponse; } if(kIdle == fState) return 0; break; } case kReadingRequest: { qtss_printf("kReadingRequest state \n"); // 读取锁,已经在处理一个报文包时,不进行新网络报文的读取和处理 OSMutexLocker readMutexLocker(&fReadMutex); // 网络请求报文存储在fInputStream中 if ((theErr = fInputStream.ReadRequest()) == QTSS_NoErr) { //如果RequestStream返回QTSS_NoErr,就表示已经读取了目前所到达的网络数据 //但,还不能构成一个整体报文,还要继续等待读取... fSocket->GetSocket()->SetTask(this); fSocket->GetSocket()->RequestEvent(EV_RE); return 0; } if ((theErr != QTSS_RequestArrived) && (theErr != E2BIG) && (theErr != QTSS_BadArgument)) { //Any other error implies that the input connection has gone away. // We should only kill the whole session if we aren't doing HTTP. // (If we are doing HTTP, the POST connection can go away) Assert(theErr > 0); // If we've gotten here, this must be an HTTP session with // a dead input connection. If that's the case, we should // clean up immediately so as to not have an open socket // needlessly lingering around, taking up space. Assert(!fSocket->GetSocket()->IsConnected()); this->ResetClientSocket(); return 0; } fState = kProcessingRequest; break; } case kProcessingRequest: { qtss_printf("kProcessingRequest state \n"); // 处理网络报文 Assert( fInputStream.GetRequestBuffer() ); Assert(fRequest == NULL); // 根据具体请求报文构造HTTPRequest请求类 fRequest = NEW HTTPRequest(&sServiceStr, fInputStream.GetRequestBuffer()); // 在这里,我们已经读取了一个完整的Request,并准备进行请求的处理,直到响应报文发出 // 在此过程中,此Session的Socket不进行任何网络数据的读/写; fReadMutex.Lock(); fSessionMutex.Lock(); // 清空发送缓冲区 fOutputStream.ResetBytesWritten(); // 网络请求超过了缓冲区,返回Bad Request if (theErr == E2BIG) { // 返回HTTP报文,错误码408 //(void)QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientBadRequest, qtssMsgRequestTooLong); fState = kSendingResponse; break; } // Check for a corrupt base64 error, return an error if (theErr == QTSS_BadArgument) { //返回HTTP报文,错误码408 //(void)QTSSModuleUtils::SendErrorResponse(fRequest, qtssClientBadRequest, qtssMsgBadBase64); fState = kSendingResponse; break; } Assert(theErr == QTSS_RequestArrived); ProcessRequest(); // 每一步都检测响应报文是否已完成,完成则直接进行回复响应 if (fOutputStream.GetBytesWritten() > 0) { fState = kSendingResponse; } else { fState = kCleaningUp; } break; } case kSendingResponse: { qtss_printf("kSendingResponse state \n"); // 响应报文发送,确保完全发送 // Assert(fRequest != NULL); //发送响应报文 theErr = fOutputStream.Flush(); if (theErr == EAGAIN || theErr == EINPROGRESS) { // If we get this error, we are currently flow-controlled and should // wait for the socket to become writeable again // 如果收到Socket EAGAIN错误,那么我们需要等Socket再次可写的时候再调用发送 fSocket->GetSocket()->SetTask(this); fSocket->GetSocket()->RequestEvent(fSocket->GetEventMask()); this->ForceSameThread(); // We are holding mutexes, so we need to force // the same thread to be used for next Run() return 0; } else if (theErr != QTSS_NoErr) { // Any other error means that the client has disconnected, right? Assert(!this->IsConnected()); ResetClientSocket(); return 0; } fState = kCleaningUp; break; } case kCleaningUp: { qtss_printf("kCleaningUp state \n"); // 清理已经处理的请求或者已经发送的报文缓存 // Cleaning up consists of making sure we've read all the incoming Request Body // data off of the socket ////if (this->GetRemainingReqBodyLen() > 0) ////{ //// err = this->DumpRequestData(); //// if (err == EAGAIN) //// { //// fInputSocketP->RequestEvent(EV_RE); //// this->ForceSameThread(); // We are holding mutexes, so we need to force //// // the same thread to be used for next Run() //// return 0; //// } ////} // 一次请求的读取、处理、响应过程完整,等待下一次网络报文! this->CleanupRequest(); fState = kIdle; if(IsConnected()) { fSocket->GetSocket()->SetTask(this); fSocket->GetSocket()->RequestEvent(fSocket->GetEventMask()); } return 0; } } } return 0; }
QTSS_Error QTAccessFile::AuthorizeRequest(QTSS_StandardRTSP_Params* inParams, Bool16 allowNoAccessFiles, QTSS_ActionFlags noAction, QTSS_ActionFlags authorizeAction, Bool16 *outAuthorizedPtr, Bool16 *outAllowAnyUserPtr) { if ( (NULL == inParams) || (NULL == inParams->inRTSPRequest) || (NULL == outAllowAnyUserPtr) || (NULL == outAuthorizedPtr) ) return QTSS_RequestFailed; *outAllowAnyUserPtr = false; *outAuthorizedPtr = false; QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest; // get the type of request // Don't touch write requests QTSS_ActionFlags action = QTSSModuleUtils::GetRequestActions(theRTSPRequest); if(action == qtssActionFlagsNoFlags) return QTSS_RequestFailed; if( (action & noAction) != 0) return QTSS_NoErr; // we don't handle //get the local file path char* pathBuffStr = QTSSModuleUtils::GetLocalPath_Copy(theRTSPRequest); OSCharArrayDeleter pathBuffDeleter(pathBuffStr); if (NULL == pathBuffStr) return QTSS_RequestFailed; //get the root movie directory char* movieRootDirStr = QTSSModuleUtils::GetMoviesRootDir_Copy(theRTSPRequest); OSCharArrayDeleter movieRootDeleter(movieRootDirStr); if (NULL == movieRootDirStr) return QTSS_RequestFailed; QTSS_UserProfileObject theUserProfile = QTSSModuleUtils::GetUserProfileObject(theRTSPRequest); if (NULL == theUserProfile) return QTSS_RequestFailed; char* accessFilePath = QTAccessFile::GetAccessFile_Copy(movieRootDirStr, pathBuffStr); OSCharArrayDeleter accessFilePathDeleter(accessFilePath); char* username = QTSSModuleUtils::GetUserName_Copy(theUserProfile); OSCharArrayDeleter usernameDeleter(username); UInt32 numGroups = 0; char** groupCharPtrArray = QTSSModuleUtils::GetGroupsArray_Copy(theUserProfile, &numGroups); OSCharPointerArrayDeleter groupCharPtrArrayDeleter(groupCharPtrArray); StrPtrLen accessFileBuf; (void)QTSSModuleUtils::ReadEntireFile(accessFilePath, &accessFileBuf); OSCharArrayDeleter accessFileBufDeleter(accessFileBuf.Ptr); if (accessFileBuf.Len == 0 && !allowNoAccessFiles) { accessFileBuf.Set(sAccessValidUser); if (DEBUG_QTACCESS) qtss_printf("QTAccessFile::AuthorizeRequest SET Accessfile valid user for no accessfile %s\n", sAccessValidUser); } if (accessFileBuf.Len == 0 && allowNoAccessFiles) { accessFileBuf.Set(sAccessAnyUser); if (DEBUG_QTACCESS) qtss_printf("QTAccessFile::AuthorizeRequest SET Accessfile any user for no access file %s\n", sAccessAnyUser); } char realmName[kBuffLen] = { 0 }; StrPtrLen realmNameStr(realmName,kBuffLen -1); //check if this user is allowed to see this movie Bool16 allowRequest = this->AccessAllowed(username, groupCharPtrArray, numGroups, &accessFileBuf, authorizeAction,&realmNameStr, outAllowAnyUserPtr ); debug_printf("accessFile.AccessAllowed for user=%s returned %d\n", username, allowRequest); // Get the auth scheme QTSS_AuthScheme theAuthScheme = qtssAuthNone; UInt32 len = sizeof(theAuthScheme); QTSS_Error theErr = QTSS_GetValue(theRTSPRequest, qtssRTSPReqAuthScheme, 0, (void*)&theAuthScheme, &len); Assert(len == sizeof(theAuthScheme)); if(theErr != QTSS_NoErr) return theErr; // If auth scheme is basic and the realm is present in the access file, use it if((theAuthScheme == qtssAuthBasic) && (realmNameStr.Ptr[0] != '\0')) //set the realm if we have one (void) QTSS_SetValue(theRTSPRequest,qtssRTSPReqURLRealm, 0, realmNameStr.Ptr, ::strlen(realmNameStr.Ptr)); else // if auth scheme is basic and no realm is present, or if the auth scheme is digest, use the realm from the users file { char* userRealm = NULL; (void) QTSS_GetValueAsString(theUserProfile, qtssUserRealm, 0, &userRealm); if(userRealm != NULL) { OSCharArrayDeleter userRealmDeleter(userRealm); (void) QTSS_SetValue(theRTSPRequest,qtssRTSPReqURLRealm, 0, userRealm, ::strlen(userRealm)); } } *outAuthorizedPtr = allowRequest; Bool16 founduser = this->HaveUser(username, NULL); Bool16 authContinue = true; char nameBuff[256]; StrPtrLen reqNameStr(nameBuff, kBuffLen); StrPtrLen profileNameStr(username); theErr = QTSS_GetValue (theRTSPRequest,qtssRTSPReqUserName,0, (void *) reqNameStr.Ptr, &reqNameStr.Len); if (DEBUG_QTACCESS) { qtss_printf("QTAccessFile::AuthorizeRequest qtaccess profile user =%s ", username); reqNameStr.PrintStr("request user="******"\n"); qtss_printf("QTAccessFile::AuthorizeRequest allowRequest=%d founduser=%d authContinue=%d\n", allowRequest, founduser, authContinue); } if (allowRequest && founduser) theErr = QTSSModuleUtils::AuthorizeRequest(theRTSPRequest, &allowRequest, &founduser,&authContinue); if (!allowRequest && !founduser) theErr = QTSSModuleUtils::AuthorizeRequest(theRTSPRequest, &allowRequest, &founduser,&authContinue); if (DEBUG_QTACCESS) { qtss_printf("QTAccessFile::AuthorizeRequest QTSSModuleUtils::AuthorizeRequest qtaccess profile user =%s ", username); reqNameStr.PrintStr("request user="******"\n"); qtss_printf("QTAccessFile::AuthorizeRequest allowRequest=%d founduser=%d authContinue=%d\n", allowRequest, founduser, authContinue); } return theErr; }
int main(int argc, char * argv[]) { extern char* optarg; // on write, don't send signal for SIGPIPE, just set errno to EPIPE // and return -1 //signal is a deprecated and potentially dangerous function //(void) ::signal(SIGPIPE, SIG_IGN); struct sigaction act; #if defined(sun) || defined(i386) || defined(__x86_64__) || defined (__MacOSX__) || defined(__powerpc__) || defined (__osf__) || defined (__sgi_cc__) || defined (__hpux__) || defined (__linux__) sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = (void(*)(int))&sigcatcher; #elif defined(__sgi__) sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = (void(*)(...))&sigcatcher; #else act.sa_mask = 0; act.sa_flags = 0; act.sa_handler = (void(*)(...))&sigcatcher; #endif (void)::sigaction(SIGPIPE, &act, NULL); (void)::sigaction(SIGHUP, &act, NULL); (void)::sigaction(SIGINT, &act, NULL); (void)::sigaction(SIGTERM, &act, NULL); (void)::sigaction(SIGQUIT, &act, NULL); (void)::sigaction(SIGALRM, &act, NULL); #if __solaris__ || __linux__ || __hpux__ //grow our pool of file descriptors to the max! struct rlimit rl; // set it to the absolute maximum that the operating system allows - have to be superuser to do this rl.rlim_cur = RLIM_INFINITY; rl.rlim_max = RLIM_INFINITY; setrlimit (RLIMIT_NOFILE, &rl); #endif #if __MacOSX__ struct rlimit rl; getrlimit(RLIMIT_NOFILE, &rl); //get the default values //printf("current open file limit =%"_U32BITARG_"\n", (UInt32) rl.rlim_cur); //leopard returns 256 //printf("current open file max =%"_U32BITARG_"\n", (UInt32) rl.rlim_max);//leopard returns infinity (-1) rl. rlim_max = (rlim_t) RLIM_INFINITY -1; //use a big number to find out the real max but do not use RLIM_INFINITY that is not allowed. see man page setrlimit (RLIMIT_NOFILE, &rl); //resets the max value stored by limits to the boot config values. getrlimit(RLIMIT_NOFILE, &rl); //now get the real max value //printf("current open file limit =%"_U32BITARG_"\n", (UInt32) rl.rlim_cur); //printf("current open file max =%"_U32BITARG_"\n", (UInt32) rl.rlim_max); rl.rlim_cur = (rlim_t) ( (float) rl.rlim_max * 0.9); //use 90% of the max set in /etc/rc.server and /etc/sysctl.conf.default setrlimit (RLIMIT_NOFILE, &rl); //finally set the current limit #endif #if 0 // testing getrlimit(RLIMIT_NOFILE, &rl); printf("current open file limit =%"_U32BITARG_"\n", (UInt32) rl.rlim_cur); printf("current open file max =%"_U32BITARG_"\n", (UInt32) rl.rlim_max); #endif #if __MacOSX__ || __FreeBSD__ // // These 2 OSes have problems with large socket buffer sizes. Make sure they allow even // ridiculously large ones, because we may need them to receive a large volume of ACK packets // from the client // // We raise the limit imposed by the kernel by calling the sysctl system call. int mib[CTL_MAXNAME]; mib[0] = CTL_KERN; mib[1] = KERN_IPC; mib[2] = KIPC_MAXSOCKBUF; mib[3] = 0; int maxSocketBufferSizeVal = 2000 * 1024; // Allow up to 2 MB. That is WAY more than we should need (void) ::sysctl(mib, 3, 0, 0, &maxSocketBufferSizeVal, sizeof(maxSocketBufferSizeVal)); //int sysctlErr = ::sysctl(mib, 3, 0, 0, &maxSocketBufferSizeVal, sizeof(maxSocketBufferSizeVal)); //qtss_printf("sysctl maxSocketBufferSizeVal=%d err=%d\n",maxSocketBufferSizeVal, sysctlErr); #endif //First thing to do is to read command-line arguments. int ch; int thePort = 0; //port can be set on the command line int statsUpdateInterval = 0; QTSS_ServerState theInitialState = qtssRunningState; Bool16 dontFork = false; Bool16 theXMLPrefsExist = true; UInt32 debugLevel = 0; UInt32 debugOptions = kRunServerDebug_Off; static char* sDefaultConfigFilePath = DEFAULTPATHS_ETC_DIR_OLD "easydarwin.conf"; static char* sDefaultXMLFilePath = DEFAULTPATHS_ETC_DIR "easydarwin.xml"; char* theConfigFilePath = sDefaultConfigFilePath; char* theXMLFilePath = sDefaultXMLFilePath; while ((ch = getopt(argc,argv, "vdfxp:DZ:c:o:S:Ih")) != EOF) // opt: means requires option arg { switch(ch) { case 'v': usage(); ::exit(0); case 'd': dontFork = RunInForeground(); break; case 'D': dontFork = RunInForeground(); debugOptions |= kRunServerDebugDisplay_On; if (debugLevel == 0) debugLevel = 1; if (statsUpdateInterval == 0) statsUpdateInterval = 3; break; case 'Z': Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt. debugLevel = (UInt32) ::atoi(optarg); break; case 'f': theXMLFilePath = DEFAULTPATHS_ETC_DIR "easydarwin.xml"; break; case 'p': Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt. thePort = ::atoi(optarg); break; case 'S': dontFork = RunInForeground(); Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt. statsUpdateInterval = ::atoi(optarg); break; case 'c': Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt. theXMLFilePath = optarg; break; case 'o': Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt. theConfigFilePath = optarg; break; case 'x': theXMLPrefsExist = false; // Force us to generate a new XML prefs file theInitialState = qtssShuttingDownState; dontFork = true; break; case 'I': theInitialState = qtssIdleState; break; case 'h': usage(); ::exit(0); default: break; } } // Check port if (thePort < 0 || thePort > 65535) { qtss_printf("Invalid port value = %d max value = 65535\n",thePort); exit (-1); } // Check expiration date QTSSExpirationDate::PrintExpirationDate(); if (QTSSExpirationDate::IsSoftwareExpired()) { qtss_printf("Streaming Server has expired\n"); ::exit(0); } XMLPrefsParser theXMLParser(theXMLFilePath); // // Check to see if the XML file exists as a directory. If it does, // just bail because we do not want to overwrite a directory if (theXMLParser.DoesFileExistAsDirectory()) { qtss_printf("Directory located at location where streaming server prefs file should be.\n"); exit(-1); } // // Check to see if we can write to the file if (!theXMLParser.CanWriteFile()) { qtss_printf("Cannot write to the streaming server prefs file.\n"); exit(-1); } // If we aren't forced to create a new XML prefs file, whether // we do or not depends solely on whether the XML prefs file exists currently. if (theXMLPrefsExist) theXMLPrefsExist = theXMLParser.DoesFileExist(); if (!theXMLPrefsExist) { // // The XML prefs file doesn't exist, so let's create an old-style // prefs source in order to generate a fresh XML prefs file. if (theConfigFilePath != NULL) { FilePrefsSource* filePrefsSource = new FilePrefsSource(true); // Allow dups if ( filePrefsSource->InitFromConfigFile(theConfigFilePath) ) { qtss_printf("Generating a new prefs file at %s\n", theXMLFilePath); } if (GenerateAllXMLPrefs(filePrefsSource, &theXMLParser)) { qtss_printf("Fatal Error: Could not create new prefs file at: %s. (%d)\n", theXMLFilePath, OSThread::GetErrno()); ::exit(-1); } } } // // Parse the configs from the XML file int xmlParseErr = theXMLParser.Parse(); if (xmlParseErr) { qtss_printf("Fatal Error: Could not load configuration file at %s. (%d)\n", theXMLFilePath, OSThread::GetErrno()); ::exit(-1); } //Unless the command line option is set, fork & daemonize the process at this point if (!dontFork) { #ifdef __sgi__ // for some reason, this method doesn't work right on IRIX 6.4 unless the first arg // is _DF_NOFORK. if the first arg is 0 (as it should be) the result is a server // that is essentially paralized and doesn't respond to much at all. So for now, // leave the first arg as _DF_NOFORK // if (_daemonize(_DF_NOFORK, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO) != 0) if (_daemonize(0, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO) != 0) #else if (daemon(0,0) != 0) #endif { #if DEBUG qtss_printf("Failed to daemonize process. Error = %d\n", OSThread::GetErrno()); #endif exit(-1); } } //Construct a Prefs Source object to get server text messages FilePrefsSource theMessagesSource; theMessagesSource.InitFromConfigFile("qtssmessages.txt"); int status = 0; int pid = 0; pid_t processID = 0; if ( !dontFork) // if (fork) { //loop until the server exits normally. If the server doesn't exit //normally, then restart it. // normal exit means the following // the child quit do // fork at least once but stop on the status conditions returned by wait or if autoStart pref is false { processID = fork(); Assert(processID >= 0); if (processID > 0) // this is the parent and we have a child { sChildPID = processID; status = 0; while (status == 0) //loop on wait until status is != 0; { pid =::wait(&status); SInt8 exitStatus = (SInt8) WEXITSTATUS(status); //qtss_printf("Child Process %d wait exited with pid=%d status=%d exit status=%d\n", processID, pid, status, exitStatus); if (WIFEXITED(status) && pid > 0 && status != 0) // child exited with status -2 restart or -1 don't restart { //qtss_printf("child exited with status=%d\n", exitStatus); if ( exitStatus == -1) // child couldn't run don't try again { qtss_printf("child exited with -1 fatal error so parent is exiting too.\n"); exit (EXIT_FAILURE); } break; // restart the child } if (WIFSIGNALED(status)) // child exited on an unhandled signal (maybe a bus error or seg fault) { //qtss_printf("child was signalled\n"); break; // restart the child } if (pid == -1 && status == 0) // parent woken up by a handled signal { //qtss_printf("handled signal continue waiting\n"); continue; } if (pid > 0 && status == 0) { //qtss_printf("child exited cleanly so parent is exiting\n"); exit(EXIT_SUCCESS); } //qtss_printf("child died for unknown reasons parent is exiting\n"); exit (EXIT_FAILURE); } } else if (processID == 0) // must be the child break; else exit(EXIT_FAILURE); //eek. If you auto-restart too fast, you might start the new one before the OS has //cleaned up from the old one, resulting in startup errors when you create the new //one. Waiting for a second seems to work sleep(1); } while (RestartServer(theXMLFilePath)); // fork again based on pref if server dies if (processID != 0) //the parent is quitting exit(EXIT_SUCCESS); } sChildPID = 0; //we have to do this again for the child process, because sigaction states //do not span multiple processes. (void)::sigaction(SIGPIPE, &act, NULL); (void)::sigaction(SIGHUP, &act, NULL); (void)::sigaction(SIGINT, &act, NULL); (void)::sigaction(SIGTERM, &act, NULL); (void)::sigaction(SIGQUIT, &act, NULL); #ifdef __hpux__ // Set Priority Type to Real Time, timeslice = 100 milliseconds. Change the timeslice upwards as needed. This keeps the server priority above the playlist broadcaster which is a time-share scheduling type. char commandStr[64]; qtss_sprintf(commandStr, "/usr/bin/rtprio -t -%d", (int) getpid()); #if DEBUG qtss_printf("setting priority to Real Time: %s\n", commandStr); #endif (void) ::system(commandStr); #endif #ifdef __solaris__ // Set Priority Type to Real Time, timeslice = 100 milliseconds. Change the timeslice upwards as needed. This keeps the server priority above the playlist broadcaster which is a time-share scheduling type. char commandStr[64]; qtss_sprintf(commandStr, "priocntl -s -c RT -t 10 -i pid %d", (int) getpid()); (void) ::system(commandStr); #endif #ifdef __MacOSX__ (void) ::umask(S_IWGRP|S_IWOTH); // make sure files are opened with default of owner -rw-r-r- #endif //This function starts, runs, and shuts down the server if (::StartServer(&theXMLParser, &theMessagesSource, thePort, statsUpdateInterval, theInitialState, dontFork, debugLevel, debugOptions) != qtssFatalErrorState) { ::RunServer(); CleanPid(false); exit (EXIT_SUCCESS); } else exit(-1); //Cant start server don't try again }
OS_Error OSFileSource::ReadFromCache(UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32* outRcvLen) { OSMutexLocker locker(&fMutex); if (!fFileMap.Initialized() || !fCacheEnabled) { Assert(0); } Assert(outRcvLen != NULL); *outRcvLen = 0; if (inPosition >= fLength) // eof return OS_NoErr; SInt64 buffIndex = fFileMap.GetBuffIndex(inPosition); SInt64 buffSize = 0; SInt64 maxBuffSize = fFileMap.GetMaxBufSize(); SInt64 endIndex = fFileMap.GetBuffIndex(inPosition+inLength); SInt64 maxIndex = fFileMap.GetMaxBuffIndex(); SInt64 buffPos = inPosition - fFileMap.GetBuffOffset(buffIndex); SInt64 buffOffsetLen = 0; char *buffStart = NULL; SInt64 buffCopyLen = inLength; SInt64 bytesToCopy = inLength; char *buffOut = (char*)inBuffer; Bool16 fillBuff = true; char *buffOffset = NULL; #if FILE_SOURCE_BUFFTEST char testBuff[inLength + 1]; buffOut = (char*)testBuff; sBuffCount ++; ::memset(inBuffer,0,inLength); ::memset(testBuff,0,inLength); #endif if (buffIndex > endIndex || endIndex > maxIndex) { #if FILE_SOURCE_DEBUG qtss_printf("OSFileSource::ReadFromCache bad index: buffIndex=%" _S32BITARG_ " endIndex=%" _S32BITARG_ " maxIndex=%" _S32BITARG_ "\n",buffIndex,endIndex,maxIndex); qtss_printf("OSFileSource::ReadFromCache inPosition =%qu buffSize = %" _U32BITARG_ " index=%" _S32BITARG_ "\n",inPosition, fFileMap.GetMaxBufSize(),buffIndex); #endif Assert(0); } while (buffIndex <= endIndex && buffIndex <= maxIndex) { #if FILE_SOURCE_DEBUG qtss_printf("OSFileSource::ReadFromCache inPosition =%qu buffSize = %" _U32BITARG_ " index=%" _S32BITARG_ "\n",inPosition, fFileMap.GetMaxBufSize(),buffIndex); #endif buffStart = fFileMap.GetBuffer(buffIndex, &fillBuff); Assert(buffStart != NULL); if (fillBuff) { OS_Error theErr = this->FillBuffer( (char *) inBuffer, (char *) buffStart, (SInt32) buffIndex); if (theErr != OS_NoErr) return theErr; } buffSize = fFileMap.GetBuffSize(buffIndex); buffOffset = &buffStart[buffPos]; if ( (buffPos == 0) && (bytesToCopy <= maxBuffSize) && (buffSize < bytesToCopy) ) // that's all there is in the file { #if FILE_SOURCE_DEBUG qtss_printf("OSFileSource::ReadFromCache end of file reached buffIndex=%" _U32BITARG_ " buffSize = %" _S32BITARG_ " bytesToCopy=%" _U32BITARG_ "\n",buffIndex, buffSize,bytesToCopy); #endif Assert(buffSize <= (SInt64) kUInt32_Max); ::memcpy(buffOut,buffOffset,(UInt32) buffSize); *outRcvLen += (UInt32) buffSize; break; } buffOffsetLen = buffSize - buffPos; if (buffCopyLen >= buffOffsetLen) buffCopyLen = buffOffsetLen; Assert(buffCopyLen <= buffSize); ::memcpy(buffOut,buffOffset, (UInt32) buffCopyLen); buffOut += buffCopyLen; *outRcvLen += (UInt32) buffCopyLen; bytesToCopy -= buffCopyLen; Assert(bytesToCopy >= 0); buffCopyLen = bytesToCopy; buffPos = 0; buffIndex ++; } #if FILE_SOURCE_DEBUG //qtss_printf("OSFileSource::ReadFromCache inLength= %" _U32BITARG_ " *outRcvLen=%" _U32BITARG_ "\n",inLength, *outRcvLen); #endif #if FILE_SOURCE_BUFFTEST { UInt32 outLen = 0; OS_Error theErr = this->ReadFromPos(inPosition, inBuffer, inLength, &outLen); Assert(*outRcvLen == outLen); if (*outRcvLen != outLen) qtss_printf("OSFileSource::ReadFromCache *outRcvLen != outLen *outRcvLen=%" _U32BITARG_ " outLen=%" _U32BITARG_ "\n",*outRcvLen,outLen); for (int i = 0; i < inLength; i++) { if ( ((char*)inBuffer)[i] != testBuff[i]) { qtss_printf("OSFileSource::ReadFromCache byte pos %d of %" _U32BITARG_ " failed len=%" _U32BITARG_ " inPosition=%qu sBuffCount=%" _S32BITARG_ "\n",i,inLength,outLen,inPosition,sBuffCount); break; } } } #endif return OS_NoErr; }
void usage() { const char *usage_name = PLATFORM_SERVER_BIN_NAME; //long ptrsize = sizeof(char *); printf("size of ptr = %ld\n", ptrsize); //long longsize = sizeof(long); printf("size of long = %ld\n", longsize); qtss_printf("%s/%s ( Build/%s; Platform/%s; %s) Built on: %s\n",QTSServerInterface::GetServerName().Ptr, QTSServerInterface::GetServerVersion().Ptr, QTSServerInterface::GetServerBuild().Ptr, QTSServerInterface::GetServerPlatform().Ptr, QTSServerInterface::GetServerComment().Ptr, QTSServerInterface::GetServerBuildDate().Ptr); qtss_printf("usage: %s [ -d | -p port | -v | -c /myconfigpath.xml | -o /myconfigpath.conf | -x | -S numseconds | -I | -h ]\n", usage_name); qtss_printf("-d: Run in the foreground\n"); qtss_printf("-D: Display performance data\n"); qtss_printf("-p XXX: Specify the default RTSP listening port of the server\n"); qtss_printf("-c /myconfigpath.xml: Specify a config file\n"); qtss_printf("-o /myconfigpath.conf: Specify a DSS 1.x / 2.x config file to build XML file from\n"); qtss_printf("-x: Force create new .xml config file and exit.\n"); qtss_printf("-S n: Display server stats in the console every \"n\" seconds\n"); qtss_printf("-I: Start the server in the idle state\n"); qtss_printf("-h: Prints usage\n"); }
QTSS_ServerState StartServer(XMLPrefsParser* inPrefsSource, PrefsSource* inMessagesSource, UInt16 inPortOverride, int statsUpdateInterval, QTSS_ServerState inInitialState, Bool16 inDontFork, UInt32 debugLevel, UInt32 debugOptions) { //Mark when we are done starting up. If auto-restart is enabled, we want to make sure //to always exit with a status of 0 if we encountered a problem WHILE STARTING UP. This //will prevent infinite-auto-restart-loop type problems Bool16 doneStartingUp = false; QTSS_ServerState theServerState = qtssStartingUpState; sStatusUpdateInterval = statsUpdateInterval; //Initialize utility classes OS::Initialize(); OSThread::Initialize(); Socket::Initialize(); SocketUtils::Initialize(!inDontFork); #if !MACOSXEVENTQUEUE ::select_startevents();//initialize the select() implementation of the event queue #endif //start the server QTSSDictionaryMap::Initialize(); QTSServerInterface::Initialize();// this must be called before constructing the server object sServer = NEW QTSServer(); sServer->SetDebugLevel(debugLevel); sServer->SetDebugOptions(debugOptions); // re-parse config file inPrefsSource->Parse(); Bool16 createListeners = true; if (qtssShuttingDownState == inInitialState) createListeners = false; sServer->Initialize(inPrefsSource, inMessagesSource, inPortOverride,createListeners); if (inInitialState == qtssShuttingDownState) { sServer->InitModules(inInitialState); return inInitialState; } OSCharArrayDeleter runGroupName(sServer->GetPrefs()->GetRunGroupName()); OSCharArrayDeleter runUserName(sServer->GetPrefs()->GetRunUserName()); OSThread::SetPersonality(runUserName.GetObject(), runGroupName.GetObject()); if (sServer->GetServerState() != qtssFatalErrorState) { UInt32 numShortTaskThreads = 0; UInt32 numBlockingThreads = 0; UInt32 numThreads = 0; UInt32 numProcessors = 0; if (OS::ThreadSafe()) { numShortTaskThreads = sServer->GetPrefs()->GetNumThreads(); // whatever the prefs say if (numShortTaskThreads == 0) { numProcessors = OS::GetNumProcessors(); // 1 worker thread per processor, up to 2 threads. // Note: Limiting the number of worker threads to 2 on a MacOS X system with > 2 cores // results in better performance on those systems, as of MacOS X 10.5. Future // improvements should make this limit unnecessary. if (numProcessors > 2) numShortTaskThreads = 2; else numShortTaskThreads = numProcessors; } numBlockingThreads = sServer->GetPrefs()->GetNumBlockingThreads(); // whatever the prefs say if (numBlockingThreads == 0) numBlockingThreads = 1; } if (numShortTaskThreads == 0) numShortTaskThreads = 1; if (numBlockingThreads == 0) numBlockingThreads = 1; numThreads = numShortTaskThreads + numBlockingThreads; //qtss_printf("Add threads shortask=%lu blocking=%lu\n",numShortTaskThreads, numBlockingThreads); TaskThreadPool::SetNumShortTaskThreads(numShortTaskThreads); TaskThreadPool::SetNumBlockingTaskThreads(numBlockingThreads); TaskThreadPool::AddThreads(numThreads); sServer->InitNumThreads(numThreads); #if DEBUG qtss_printf("Number of task threads: %"_U32BITARG_"\n",numThreads); #endif // Start up the server's global tasks, and start listening TimeoutTask::Initialize(); // The TimeoutTask mechanism is task based, // we therefore must do this after adding task threads // this be done before starting the sockets and server tasks } //Make sure to do this stuff last. Because these are all the threads that //do work in the server, this ensures that no work can go on while the server //is in the process of staring up if (sServer->GetServerState() != qtssFatalErrorState) { IdleTask::Initialize(); Socket::StartThread(); OSThread::Sleep(1000); // // On Win32, in order to call modwatch the Socket EventQueue thread must be // created first. Modules call modwatch from their initializer, and we don't // want to prevent them from doing that, so module initialization is separated // out from other initialization, and we start the Socket EventQueue thread first. // The server is still prevented from doing anything as of yet, because there // aren't any TaskThreads yet. sServer->InitModules(inInitialState); sServer->StartTasks(); sServer->SetupUDPSockets(); // udp sockets are set up after the rtcp task is instantiated theServerState = sServer->GetServerState(); } if (theServerState != qtssFatalErrorState) { CleanPid(true); WritePid(!inDontFork); doneStartingUp = true; qtss_printf("Streaming Server done starting up\n"); OSMemory::SetMemoryError(ENOMEM); } // SWITCH TO RUN USER AND GROUP ID //if (!sServer->SwitchPersonality()) // theServerState = qtssFatalErrorState; // // Tell the caller whether the server started up or not return theServerState; }
//登录用户验证 Bool16 Easy_UserAuthentication(const char* inUserName, const char* inPassword) { qtss_printf("User:%s Password:%s Authenticated!\n"); return true; }
int main(int argc, char *argv[]) { FILE *usersFilePtr = NULL, *tempUsersFilePtr = NULL, *passFilePtr = NULL, *groupsFilePtr = NULL; //char line[MAX_STRING_LEN + 1]; char line[MAX_LINE_LEN]; char lineFromFile[MAX_LINE_LEN]; char usernameFromFile[MAX_STRING_LEN + 1]; char realmFromFile[MAX_STRING_LEN + 1]; int found; int result; static char choice[81]; int doCreateNewFile = 0; int doDeleteUser = 0; int addUserToGroup = 0; int deleteUserFromGroup = 0; int createGroup = 0; int deleteGroup = 0; int confirmPotentialDamage = 1; char* qtusersFilePath = NULL; char* qtgroupsFilePath = NULL; char* userName = NULL; char* groupName = NULL; char* realmString = NULL; char* password = NULL; char* passwordFilePath = NULL; int ch; extern char* optarg; extern int optind; /* Read command line arguments */ while ((ch = getopt(argc, argv, "f:cg:r:p:P:A:D:C:R:dFhv?")) != EOF) { switch(ch) { case 'f': qtusersFilePath = CopyString(optarg); break; case 'c': doCreateNewFile = 1; break; case 'g': qtgroupsFilePath = CopyString(optarg); break; case 'r': realmString = CopyString(optarg); if (::strlen(realmString) > MAX_STRING_LEN) { qtss_fprintf(stderr, "Realm cannot have more than %d characters.\n", MAX_STRING_LEN); qtss_printf("Exiting! \n"); exit(1); } break; case 'p': password = CopyString(optarg); ::memset(optarg, 0, ::strlen(optarg)); if (::strlen(password) > MAX_PASSWORD_LEN) { qtss_fprintf(stderr, "Password cannot have more than %d characters.\n", MAX_PASSWORD_LEN); qtss_printf("Exiting! \n"); exit(1); } break; case 'P': passwordFilePath = CopyString(optarg); break; case 'A': groupName = CopyString(optarg); addUserToGroup = 1; break; case 'D': groupName = CopyString(optarg); deleteUserFromGroup = 1; break; case 'C': groupName = CopyString(optarg); createGroup = 1; break; case 'R': groupName = CopyString(optarg); deleteGroup = 1; break; case 'd': doDeleteUser = 1; break; case 'F': confirmPotentialDamage = 0; break; case 'h': case 'v': case '?': default: usage(); break; } } /* If password is to be read from a file, check validity of the password */ if ((password == NULL) && (passwordFilePath != NULL)) { if ((passFilePtr = fopen(passwordFilePath, "r")) != NULL ) { char passline[MAX_STRING_LEN]; char passFromFile[MAX_STRING_LEN]; int passLen = 0; ::memset(passline, 0, MAX_STRING_LEN); ::memset(passFromFile, 0, MAX_STRING_LEN); GetLine(passline, MAX_STRING_LEN, passFilePtr); if (passline[0] == '\'') // if it is single quoted, read until the end single quote GetWord(passFromFile, (passline + 1), '\''); else if (passline[0] == '"') // if it is double quoted, read until the end double quote GetWord(passFromFile, (passline + 1), '"'); else // if it is not quoted, read until the first whitespace GetWord(passFromFile, passline, ' '); passLen = ::strlen(passFromFile); if (passLen == 0) { qtss_fprintf(stderr, "Password in file %s is blank.\n", passwordFilePath); qtss_printf("Exiting! \n"); exit(1); } else if (passLen > MAX_PASSWORD_LEN) { qtss_fprintf(stderr, "Password in file %s has more than %d characters. Cannot accept password.\n", passwordFilePath, MAX_PASSWORD_LEN); qtss_printf("Exiting! \n"); exit(1); } else password = CopyString(passFromFile); fclose(passFilePtr); } } /* deleting a user and (creating a file or setting a password) don't make sense together */ if ( doDeleteUser && (doCreateNewFile || password != NULL) ) { qtss_fprintf(stderr, "Cannot use the -c option (to create the file) with the -d option (to delete the user).\n"); qtss_printf("Exiting! \n"); usage(); } /* realm name only makes sense when creating a new password file */ if ( !doCreateNewFile && (realmString != NULL) ) { qtss_fprintf(stderr, "Can use the -r option only with the -c option (when creating the file).\n"); qtss_printf("Exiting! \n"); usage(); } /* group name checks */ if (groupName != NULL) { /* check length < MAX_STRING_LEN */ if (::strlen(groupName) > MAX_STRING_LEN) { qtss_fprintf(stderr, "Group name cannot have more than %d characters.\n", MAX_STRING_LEN); qtss_printf("Exiting! \n"); exit(1); } /* check for : */ if (strchr(groupName, ':') != NULL) { qtss_printf("Group name cannot contain a ':' character.\n"); qtss_printf("Exiting! \n"); exit(1); } /* can't add user to group and delete user from a group at the same time */ if (addUserToGroup && deleteUserFromGroup) { qtss_printf("Cannot add to or delete from a group at the same time (use either -A or -D option, not both!).\n"); qtss_printf("Exiting! \n"); exit(1); } /* can't create and delete group at the same time */ if (createGroup && deleteGroup) { qtss_printf("Cannot create new group and delete group at the same time (use either -C or -R option, not both!).\n"); qtss_printf("Exiting! \n"); exit(1); } } /* Read in the username */ if (argv[optind] != NULL) { /* If group needs to be created or deleted, username will be ignored. */ if (createGroup || deleteGroup) { qtss_fprintf(stderr, "Warning: username cannot be specified with -C or -R option and will be ignored!\n"); } else { userName = CopyString(argv[optind]); /* check length < MAX_STRING_LEN */ if (::strlen(userName) > MAX_STRING_LEN) { qtss_fprintf(stderr, "Username cannot have more than %d characters.\n", MAX_STRING_LEN); qtss_printf("Exiting! \n"); exit(1); } } } else { /* Exit if username is not given, unless a group has to be created or deleted. */ if (!createGroup && !deleteGroup) { qtss_fprintf(stderr, "Username not given!\n"); qtss_printf("Exiting! \n"); usage(); } } if (confirmPotentialDamage && doDeleteUser) { qtss_printf("Delete user %s (will also delete user from groups in the groups file)? y or n [y] ", userName); fgets( (char*)&choice, 80, stdin); if( choice[0] == 'n' || choice[0] == 'N' ) exit(0); } if (qtusersFilePath == NULL) { qtusersFilePath = new char[strlen(kDefaultQTPasswdFilePath)+1]; strcpy(qtusersFilePath, kDefaultQTPasswdFilePath); } if (qtgroupsFilePath == NULL) { qtgroupsFilePath = new char[strlen(kDefaultQTGroupsFilePath)+1]; strcpy(qtgroupsFilePath, kDefaultQTGroupsFilePath); } if (realmString == NULL) { char* kDefaultRealmString = "Streaming Server"; realmString = new char[strlen(kDefaultRealmString)+1]; strcpy(realmString, kDefaultRealmString); } tempUsersFile = NULL; tempGroupsFile = NULL; #ifndef __Win32__ signal(SIGINT, (void(*)(int))CleanupAndExit); //create file with owner RW permissions only umask(S_IRWXO|S_IRWXG); #endif if (doCreateNewFile) { if (confirmPotentialDamage) if( (usersFilePtr = fopen(qtusersFilePath, "r")) != NULL ) { fclose(usersFilePtr); qtss_printf("File already exists. Do you wish to overwrite it? y or n [y] "); fgets( (char*)&choice, 80, stdin); if( choice[0] == 'n' || choice[0] == 'N' ) CleanupAndExit(); } //create new file or truncate existing one to 0 if ( (usersFilePtr = fopen(qtusersFilePath, "w")) == NULL) { char buffer[kErrorStrSize]; qtss_fprintf(stderr, "Could not open password file %s for writing. (err=%d %s)\n", qtusersFilePath, errno, qtss_strerror(errno, buffer, sizeof(buffer))); perror("fopen"); CleanupAndExit(); } qtss_printf("Creating password file for realm %s.\n", realmString); //write the realm into the file qtss_fprintf(usersFilePtr, "realm %s\n", realmString); fclose(usersFilePtr); SetPrivileges(qtusersFilePath); } #ifdef __Win32__ char separator = '\\'; #else char separator = '/'; #endif char* tmpFile = "tmp.XXXXXX"; char* alternateTempPath = "./tmp.XXXXXX"; char* tempFilePath; int tempFilePathLength = 0; char* lastOccurOfSeparator = strrchr(qtusersFilePath, separator); int pathLength = strlen(qtusersFilePath); if(lastOccurOfSeparator != NULL) { int filenameLength = ::strlen(lastOccurOfSeparator) + sizeof(char); tempFilePathLength = pathLength - filenameLength + sizeof(char) + ::strlen(tmpFile); tempFilePath = new char[tempFilePathLength]; memcpy(tempFilePath, qtusersFilePath, (pathLength - filenameLength)); memcpy(tempFilePath + (pathLength - filenameLength), tmpFile, ::strlen(tmpFile)); tempFilePath[pathLength - filenameLength + ::strlen(tmpFile)] = '\0'; /* Get temp users file path name */ if (!createGroup && !deleteGroup) tempUsersFile = GetTempFileAtPath(tempFilePath, tempFilePathLength); /* Get temp groups file path name */ if ((groupName != NULL) || doDeleteUser) tempGroupsFile = GetTempFileAtPath(tempFilePath, tempFilePathLength); delete [] tempFilePath; } else { if (!createGroup && !deleteGroup) tempUsersFile = GetTempFileAtPath(alternateTempPath, ::strlen(alternateTempPath)); if ((groupName != NULL) || doDeleteUser) tempGroupsFile = GetTempFileAtPath(alternateTempPath, ::strlen(alternateTempPath)); } if ((groupName != NULL) && !(groupsFilePtr = fopen(qtgroupsFilePath, "r"))) { char buffer[kErrorStrSize]; qtss_fprintf(stderr, "Could not open groups file %s to manipulate groups file. (err=%d:%s)\n", qtgroupsFilePath, errno, qtss_strerror(errno, buffer, sizeof(buffer))); //create new file if ( (groupsFilePtr = fopen(qtgroupsFilePath, "w")) == NULL) { char buffer[kErrorStrSize]; qtss_fprintf(stderr, "Could not create a new groups file %s either. (err=%d %s)\n", qtgroupsFilePath, errno, qtss_strerror(errno, buffer, sizeof(buffer))); CleanupAndExit(); } else qtss_printf("Created new groups file %s.\n", qtgroupsFilePath); fclose(groupsFilePtr); SetPrivileges(qtgroupsFilePath); } if (createGroup) { AddOrDeleteGroup(1, groupName, qtgroupsFilePath, tempGroupsFile); qtss_printf("Created new group %s\n", groupName); return 0; } if (deleteGroup) { AddOrDeleteGroup(0, groupName, qtgroupsFilePath, tempGroupsFile); qtss_printf("Deleted group %s\n", groupName); return 0; } if (!(tempUsersFilePtr = fopen(tempUsersFile, "w"))) { char buffer[kErrorStrSize]; qtss_printf("failed\n"); qtss_fprintf(stderr, "Could not open temp users file. (err=%d %s)\n", errno, qtss_strerror(errno, buffer, sizeof(buffer))); CleanupAndExit(); } if (!(usersFilePtr = fopen(qtusersFilePath, "r"))) { char buffer[kErrorStrSize]; qtss_fprintf(stderr, "Could not open passwd file %s for reading. (err=%d:%s)\n", qtusersFilePath, errno, qtss_strerror(errno, buffer, sizeof(buffer))); qtss_fprintf(stderr, "Use -c option to create new one.\n"); CleanupAndExit(); } // Get the realm from the first line while (!(GetLine(line, MAX_LINE_LEN, usersFilePtr))) { if ((line[0] == '#') || (!line[0])) { PutLine(tempUsersFilePtr, line); continue; } else { // line is "realm somename" if( strncmp(line, "realm", strlen("realm")) != 0 ) { qtss_fprintf(stderr, "Invalid users file.\n"); qtss_fprintf(stderr, "The first non-comment non-blank line must be the realm line\n"); qtss_fprintf(stderr, "The file may have been tampered manually!\n"); CleanupAndExit(); } strcpy(realmFromFile ,line + strlen("realm")+1); PutLine(tempUsersFilePtr, line); break; } } // Look for an existing entry with the username found = 0; while (!(GetLine(line, MAX_LINE_LEN, usersFilePtr))) { //write comments and blank lines out to temp file if (found || (line[0] == '#') || (line[0] == 0)) { PutLine(tempUsersFilePtr, line); continue; } strcpy(lineFromFile, line); GetWord(usernameFromFile, lineFromFile, ':'); //if not the user we're looking for, write the line out to the temp file if (strcmp(userName, usernameFromFile) != 0) { PutLine(tempUsersFilePtr, line); continue; } else { if (doDeleteUser) { //to delete a user - just don't write it out to the temp file qtss_printf("Deleting user %s\n", userName); //delete user from all groups in the group file qtss_printf("Deleting user %s from all groups\n", userName); AddOrDeleteUserFromGroup(0, userName, NULL, qtgroupsFilePath, tempGroupsFile); } else { if (addUserToGroup) { PutLine(tempUsersFilePtr, line); qtss_printf("Adding user %s to group %s\n", userName, groupName); AddOrDeleteUserFromGroup(1, userName, groupName, qtgroupsFilePath, tempGroupsFile); } else if (deleteUserFromGroup) { PutLine(tempUsersFilePtr, line); qtss_printf("Deleting user %s from group %s\n", userName, groupName); AddOrDeleteUserFromGroup(0, userName, groupName, qtgroupsFilePath, tempGroupsFile); } else { qtss_printf("Changing password for user %s\n", userName); if(password != NULL) AddPasswordWithoutPrompt(userName, password, realmFromFile, tempUsersFilePtr); else AddPassword(userName, realmFromFile, tempUsersFilePtr); } } found = 1; } } if (!found) { if (doDeleteUser) { qtss_printf("Username %s not found in users file.\n", userName); //delete user from all groups in the group file qtss_printf("Deleting user %s from all groups if found in groups file\n", userName); AddOrDeleteUserFromGroup(0, userName, NULL, qtgroupsFilePath, tempGroupsFile); CleanupAndExit(); } /* check for : in name before adding user */ if(strchr(userName, ':') != NULL) { qtss_printf("Username cannot contain a ':' character."); CleanupAndExit(); } qtss_printf("Adding userName %s\n", userName); if(password != NULL) AddPasswordWithoutPrompt(userName, password, realmFromFile, tempUsersFilePtr); else AddPassword(userName, realmFromFile, tempUsersFilePtr); if (addUserToGroup) { qtss_printf("Adding user %s to group %s\n", userName, groupName); AddOrDeleteUserFromGroup(1, userName, groupName, qtgroupsFilePath, tempGroupsFile); } else if (deleteUserFromGroup) { qtss_printf("Deleting user %s from group %s\n", userName, groupName); AddOrDeleteUserFromGroup(0, userName, groupName, qtgroupsFilePath, tempGroupsFile); } } fclose(usersFilePtr); fclose(tempUsersFilePtr); // Remove old file and change name of temp file to match new file remove(qtusersFilePath); result = rename(tempUsersFile, qtusersFilePath); if(result != 0) { perror("rename failed with error"); CleanupAndExit(); } SetPrivileges(qtusersFilePath); return 0; }
void APITests_DEBUG() { if (0) { qtss_printf("QTSSAdminModule start tests \n"); if (0) { qtss_printf("admin called locked \n"); const int ksleeptime = 15; qtss_printf("sleeping for %d seconds \n",ksleeptime); sleep(ksleeptime); qtss_printf("done sleeping \n"); qtss_printf("QTSS_GlobalUnLock \n"); (void) QTSS_GlobalUnLock(); qtss_printf("again sleeping for %d seconds \n",ksleeptime); sleep(ksleeptime); } if (0) { qtss_printf(" GET VALUE PTR TEST \n"); QTSS_Object *sessionsPtr = NULL; UInt32 paramLen = sizeof(sessionsPtr); UInt32 numValues = 0; QTSS_Error err = 0; err = QTSS_GetNumValues (sServer, qtssSvrClientSessions, &numValues); err = QTSS_GetValuePtr(sServer, qtssSvrClientSessions, 0, (void**)&sessionsPtr, ¶mLen); qtss_printf("Admin Module Num Sessions = %"_U32BITARG_" sessions[0] = %"_S32BITARG_" err = %"_S32BITARG_" paramLen =%"_U32BITARG_"\n", numValues, (SInt32) *sessionsPtr,err,paramLen); UInt32 numAttr = 0; if (sessionsPtr) { err = QTSS_GetNumAttributes (*sessionsPtr, &numAttr); qtss_printf("Admin Module Num attributes = %"_U32BITARG_" sessions[0] = %"_S32BITARG_" err = %"_S32BITARG_"\n", numAttr, (SInt32) *sessionsPtr,err); QTSS_Object theAttributeInfo; char nameBuff[128]; UInt32 len = 127; for (UInt32 i = 0; i < numAttr; i++) { err = QTSS_GetAttrInfoByIndex(*sessionsPtr, i, &theAttributeInfo); nameBuff[0] = 0;len = 127; err = QTSS_GetValue (theAttributeInfo, qtssAttrName,0, nameBuff,&len); nameBuff[len] = 0; qtss_printf("found %s \n",nameBuff); } } } if (0) { qtss_printf(" GET VALUE TEST \n"); QTSS_Object sessions = NULL; UInt32 paramLen = sizeof(sessions); UInt32 numValues = 0; QTSS_Error err = 0; err = QTSS_GetNumValues (sServer, qtssSvrClientSessions, &numValues); err = QTSS_GetValue(sServer, qtssSvrClientSessions, 0, (void*)&sessions, ¶mLen); qtss_printf("Admin Module Num Sessions = %"_U32BITARG_" sessions[0] = %"_S32BITARG_" err = %"_S32BITARG_" paramLen = %"_U32BITARG_"\n", numValues, (SInt32) sessions,err, paramLen); if (sessions) { UInt32 numAttr = 0; err = QTSS_GetNumAttributes (sessions, &numAttr); qtss_printf("Admin Module Num attributes = %"_U32BITARG_" sessions[0] = %"_S32BITARG_" err = %"_S32BITARG_"\n", numAttr,(SInt32) sessions,err); QTSS_Object theAttributeInfo; char nameBuff[128]; UInt32 len = 127; for (UInt32 i = 0; i < numAttr; i++) { err = QTSS_GetAttrInfoByIndex(sessions, i, &theAttributeInfo); nameBuff[0] = 0;len = 127; err = QTSS_GetValue (theAttributeInfo, qtssAttrName,0, nameBuff,&len); nameBuff[len] = 0; qtss_printf("found %s \n",nameBuff); } } } if (0) { qtss_printf("----------------- Start test ----------------- \n"); qtss_printf(" GET indexed pref TEST \n"); QTSS_Error err = 0; UInt32 numAttr = 1; err = QTSS_GetNumAttributes (sModulePrefs, &numAttr); qtss_printf("Admin Module Num preference attributes = %"_U32BITARG_" err = %"_S32BITARG_"\n", numAttr, err); QTSS_Object theAttributeInfo; char valueBuff[512]; char nameBuff[128]; QTSS_AttributeID theID; UInt32 len = 127; UInt32 i = 0; qtss_printf("first pass over preferences\n"); for ( i = 0; i < numAttr; i++) { err = QTSS_GetAttrInfoByIndex(sModulePrefs, i, &theAttributeInfo); nameBuff[0] = 0;len = 127; err = QTSS_GetValue (theAttributeInfo, qtssAttrName,0, nameBuff,&len); nameBuff[len]=0; theID = qtssIllegalAttrID; len = sizeof(theID); err = QTSS_GetValue (theAttributeInfo, qtssAttrID,0, &theID,&len); qtss_printf("found preference=%s \n",nameBuff); } valueBuff[0] = 0;len = 512; err = QTSS_GetValue (sModulePrefs, theID,0, valueBuff,&len);valueBuff[len] = 0; qtss_printf("Admin Module QTSS_GetValue name = %s id = %"_S32BITARG_" value=%s err = %"_S32BITARG_"\n", nameBuff,theID, valueBuff, err); err = QTSS_SetValue (sModulePrefs,theID,0, valueBuff,len); qtss_printf("Admin Module QTSS_SetValue name = %s id = %"_S32BITARG_" value=%s err = %"_S32BITARG_"\n", nameBuff,theID, valueBuff, err); { QTSS_ServiceID id; (void) QTSS_IDForService(QTSS_REREAD_PREFS_SERVICE, &id); (void) QTSS_DoService(id, NULL); } valueBuff[0] = 0;len = 512; err = QTSS_GetValue (sModulePrefs, theID,0, valueBuff,&len);valueBuff[len] = 0; qtss_printf("Admin Module QTSS_GetValue name = %s id = %"_S32BITARG_" value=%s err = %"_S32BITARG_"\n", nameBuff,theID, valueBuff, err); err = QTSS_SetValue (sModulePrefs,theID,0, valueBuff,len); qtss_printf("Admin Module QTSS_SetValue name = %s id = %"_S32BITARG_" value=%s err = %"_S32BITARG_"\n", nameBuff,theID, valueBuff, err); qtss_printf("second pass over preferences\n"); for ( i = 0; i < numAttr; i++) { err = QTSS_GetAttrInfoByIndex(sModulePrefs, i, &theAttributeInfo); nameBuff[0] = 0;len = 127; err = QTSS_GetValue (theAttributeInfo, qtssAttrName,0, nameBuff,&len); nameBuff[len]=0; theID = qtssIllegalAttrID; len = sizeof(theID); err = QTSS_GetValue (theAttributeInfo, qtssAttrID,0, &theID,&len); qtss_printf("found preference=%s \n",nameBuff); } qtss_printf("----------------- Done test ----------------- \n"); } } }
void TaskThread::Entry() { Task* theTask = NULL; while (true) { theTask = this->WaitForTask(); // // WaitForTask returns NULL when it is time to quit if (theTask == NULL || false == theTask->Valid()) return; Bool16 doneProcessingEvent = false; while (!doneProcessingEvent) { //If a task holds locks when it returns from its Run function, //that would be catastrophic and certainly lead to a deadlock #if DEBUG Assert(this->GetNumLocksHeld() == 0); Assert(theTask->fInRunCount == 0); theTask->fInRunCount++; #endif theTask->fUseThisThread = NULL; // Each invocation of Run must independently // request a specific thread. SInt64 theTimeout = 0; if (theTask->fWriteLock) { OSMutexWriteLocker mutexLocker(&TaskThreadPool::sMutexRW); if (TASK_DEBUG) qtss_printf("TaskThread::Entry run global locked TaskName=%s CurMSec=%.3f thread=%p task=%p\n", theTask->fTaskName, OS::StartTimeMilli_Float(), (void *) this, (void *)theTask); theTimeout = theTask->Run(); theTask->fWriteLock = false; } else { OSMutexReadLocker mutexLocker(&TaskThreadPool::sMutexRW); if (TASK_DEBUG) qtss_printf("TaskThread::Entry run TaskName=%s CurMSec=%.3f thread=%p task=%p\n", theTask->fTaskName, OS::StartTimeMilli_Float(), (void *) this, (void *)theTask); theTimeout = theTask->Run(); } #if DEBUG Assert(this->GetNumLocksHeld() == 0); theTask->fInRunCount--; Assert(theTask->fInRunCount == 0); #endif if (theTimeout < 0) { if (TASK_DEBUG) { qtss_printf("TaskThread::Entry delete TaskName=%s CurMSec=%.3f thread=%p task=%p\n", theTask->fTaskName, OS::StartTimeMilli_Float(), (void *) this, (void *)theTask); theTask->fUseThisThread = NULL; if (NULL != fHeap.Remove(&theTask->fTimerHeapElem)) qtss_printf("TaskThread::Entry task still in heap before delete\n"); if (NULL != theTask->fTaskQueueElem.InQueue()) qtss_printf("TaskThread::Entry task still in queue before delete\n"); theTask->fTaskQueueElem.Remove(); if (theTask->fEvents &~Task::kAlive) qtss_printf("TaskThread::Entry flags still set before delete\n"); (void)atomic_sub(&theTask->fEvents, 0); ::strncat(theTask->fTaskName, " deleted", sizeof(theTask->fTaskName) - 1); } theTask->fTaskName[0] = 'D'; //mark as dead delete theTask; theTask = NULL; doneProcessingEvent = true; } else if (theTimeout == 0) { //We want to make sure that 100% definitely the task's Run function WILL //be invoked when another thread calls Signal. We also want to make sure //that if an event sneaks in right as the task is returning from Run() //(via Signal) that the Run function will be invoked again. doneProcessingEvent = compare_and_store(Task::kAlive, 0, &theTask->fEvents); if (doneProcessingEvent) theTask = NULL; } else { //note that if we get here, we don't reset theTask, so it will get passed into //WaitForTask if (TASK_DEBUG) qtss_printf("TaskThread::Entry insert TaskName=%s in timer heap thread=%p elem=%p task=%p timeout=%.2f\n", theTask->fTaskName, (void *) this, (void *)&theTask->fTimerHeapElem, (void *)theTask, (float)theTimeout / (float)1000); theTask->fTimerHeapElem.SetValue(OS::Milliseconds() + theTimeout); fHeap.Insert(&theTask->fTimerHeapElem); (void)atomic_or(&theTask->fEvents, Task::kIdleEvent); doneProcessingEvent = true; } #if TASK_DEBUG SInt64 yieldStart = OS::Milliseconds(); #endif this->ThreadYield(); #if TASK_DEBUG SInt64 yieldDur = OS::Milliseconds() - yieldStart; static SInt64 numZeroYields; if (yieldDur > 1) { if (TASK_DEBUG) qtss_printf("TaskThread::Entry time in Yield %qd, numZeroYields %qd \n", yieldDur, numZeroYields); numZeroYields = 0; } else numZeroYields++; #endif } } }
Bool16 Authenticate(QTSS_RTSPRequestObject request, StrPtrLen* namePtr, StrPtrLen* passwordPtr) { Bool16 authenticated = true; char* authName = namePtr->GetAsCString(); OSCharArrayDeleter authNameDeleter(authName); QTSS_ActionFlags authAction = qtssActionFlagsAdmin; // authenticate callback to retrieve the password QTSS_Error err = QTSS_Authenticate(authName, sAuthResourceLocalPath, sAuthResourceLocalPath, authAction, qtssAuthBasic, request); if (err != QTSS_NoErr) { return false; // Couldn't even call QTSS_Authenticate...abandon! } // Get the user profile object from the request object that was created in the authenticate callback QTSS_UserProfileObject theUserProfile = NULL; UInt32 len = sizeof(QTSS_UserProfileObject); err = QTSS_GetValue(request, qtssRTSPReqUserProfile, 0, (void*)&theUserProfile, &len); Assert(len == sizeof(QTSS_UserProfileObject)); if (err != QTSS_NoErr) authenticated = false; if(err == QTSS_NoErr) { char* reqPassword = passwordPtr->GetAsCString(); OSCharArrayDeleter reqPasswordDeleter(reqPassword); char* userPassword = NULL; (void) QTSS_GetValueAsString(theUserProfile, qtssUserPassword, 0, &userPassword); OSCharArrayDeleter userPasswordDeleter(userPassword); if(userPassword == NULL) { authenticated = false; } else { #ifdef __Win32__ // The password is md5 encoded for win32 char md5EncodeResult[120]; MD5Encode(reqPassword, userPassword, md5EncodeResult, sizeof(md5EncodeResult)); if(::strcmp(userPassword, md5EncodeResult) != 0) authenticated = false; #else if(::strcmp(userPassword, (char*)crypt(reqPassword, userPassword)) != 0) authenticated = false; #endif } } char* realm = NULL; Bool16 allowed = true; //authorize callback to check authorization // allocates memory for realm err = QTSS_Authorize(request, &realm, &allowed); // QTSS_Authorize allocates memory for the realm string // we don't use the realm returned by the callback, but instead // use our own. // delete the memory allocated for realm because we don't need it! OSCharArrayDeleter realmDeleter(realm); if(err != QTSS_NoErr) { qtss_printf("QTSSAdminModule::Authenticate: QTSS_Authorize failed\n"); return false; // Couldn't even call QTSS_Authorize...abandon! } if(authenticated && allowed) return true; return false; }
//Windows服务控制 void WINAPI ServiceControl(DWORD inControlCode) { QTSS_ServerState theState; QTSServerInterface* theServer = QTSServerInterface::GetServer(); DWORD theStatusReport = SERVICE_START_PENDING; if (theServer != NULL) theState = theServer->GetServerState(); else theState = qtssStartingUpState; switch(inControlCode) { // Stop the service. // case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: { if (theState == qtssStartingUpState) break; // // Signal the server to shut down. theState = qtssShuttingDownState; if (theServer != NULL) theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState)); break; } case SERVICE_CONTROL_PAUSE: { if (theState != qtssRunningState) break; // // Signal the server to refuse new connections. theState = qtssRefusingConnectionsState; if (theServer != NULL) theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState)); break; } case SERVICE_CONTROL_CONTINUE: { if (theState != qtssRefusingConnectionsState) break; // // Signal the server to refuse new connections. theState = qtssRefusingConnectionsState; if (theServer != NULL) theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState)); break; } case SERVICE_CONTROL_INTERROGATE: break; // Just update our status default: break; } if (theServer != NULL) { theState = theServer->GetServerState(); // // Convert a QTSS state to a Win32 Service state switch (theState) { case qtssStartingUpState: theStatusReport = SERVICE_START_PENDING; break; case qtssRunningState: theStatusReport = SERVICE_RUNNING; break; case qtssRefusingConnectionsState: theStatusReport = SERVICE_PAUSED; break; case qtssFatalErrorState: theStatusReport = SERVICE_STOP_PENDING; break; case qtssShuttingDownState: theStatusReport = SERVICE_STOP_PENDING; break; default: theStatusReport = SERVICE_RUNNING; break; } } else theStatusReport = SERVICE_START_PENDING; qtss_printf("Reporting status from ServiceControl function\n"); ::ReportStatus(theStatusReport, NO_ERROR); }
int main(int argc, char * argv[]) { extern char* optarg; //First thing to do is to read command-line arguments. int ch; char* theConfigFilePath = "./easydarwin.cfg"; char* theXMLFilePath = "./easydarwin.xml"; Bool16 notAService = false; Bool16 theXMLPrefsExist = true; Bool16 dontFork = false; #if _DEBUG char* compileType = "Compile_Flags/_DEBUG; "; #else char* compileType = ""; #endif while ((ch = getopt(argc,argv, "vdxp:o:c:irsS:I")) != EOF) // opt: means requires option { switch(ch) { case 'v': qtss_printf("%s/%s ( Build/%s; Platform/%s; %s%s) Built on: %s\n",QTSServerInterface::GetServerName().Ptr, QTSServerInterface::GetServerVersion().Ptr, QTSServerInterface::GetServerBuild().Ptr, QTSServerInterface::GetServerPlatform().Ptr, compileType, QTSServerInterface::GetServerComment().Ptr, QTSServerInterface::GetServerBuildDate().Ptr); qtss_printf("usage: %s [ -d | -p port | -v | -c /myconfigpath.xml | -o /myconfigpath.conf | -x | -S numseconds | -I | -h ]\n", QTSServerInterface::GetServerName().Ptr); qtss_printf("-d: Don't run as a Win32 Service\n"); qtss_printf("-p XXX: Specify the default RTSP listening port of the server\n"); qtss_printf("-c c:\\myconfigpath.xml: Specify a config file path\n"); qtss_printf("-o c:\\myconfigpath.conf: Specify a DSS 1.x / 2.x config file path\n"); qtss_printf("-x: Force create new .xml config file from 1.x / 2.x config\n"); qtss_printf("-i: Install the Darwin Streaming Server service\n"); qtss_printf("-r: Remove the Darwin Streaming Server service\n"); qtss_printf("-s: Start the Darwin Streaming Server service\n"); qtss_printf("-S n: Display server stats in the console every \"n\" seconds\n"); qtss_printf("-I: Start the server in the idle state\n"); ::exit(0); case 'd': notAService = true; break; case 'p': Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt. sPort = ::atoi(optarg); break; case 'c': Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt. theXMLFilePath = optarg; break; case 'S': Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt. sStatsUpdateInterval = ::atoi(optarg); break; case 'o': Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt. theConfigFilePath = optarg; break; case 'x': theXMLPrefsExist = false; // Force us to generate a new XML prefs file break; case 'i': qtss_printf("Installing the Darwin Streaming Server service...\n"); ::InstallService("Darwin Streaming Server"); qtss_printf("Starting the Darwin Streaming Server service...\n"); ::RunAsService("Darwin Streaming Server"); ::exit(0); break; case 'r': qtss_printf("Removing the Darwin Streaming Server service...\n"); ::RemoveService("Darwin Streaming Server"); ::exit(0); case 's': qtss_printf("Starting the Darwin Streaming Server service...\n"); ::RunAsService("Darwin Streaming Server"); ::exit(0); case 'I': sInitialState = qtssIdleState; break; default: break; } } // // Check expiration date QTSSExpirationDate::PrintExpirationDate(); if (QTSSExpirationDate::IsSoftwareExpired()) { qtss_printf("Streaming Server has expired\n"); ::exit(0); } // // Create an XML prefs parser object using the specified path sXMLParser = new XMLPrefsParser(theXMLFilePath); // // Check to see if the XML file exists as a directory. If it does, // just bail because we do not want to overwrite a directory if (sXMLParser->DoesFileExistAsDirectory()) { qtss_printf("Directory located at location where streaming server prefs file should be.\n"); ::exit(0); } if (!sXMLParser->CanWriteFile()) { qtss_printf("Cannot write to the streaming server prefs file.\n"); ::exit(0); } // If we aren't forced to create a new XML prefs file, whether // we do or not depends solely on whether the XML prefs file exists currently. if (theXMLPrefsExist) theXMLPrefsExist = sXMLParser->DoesFileExist(); if (!theXMLPrefsExist) { // //Construct a Prefs Source object to get server preferences int prefsErr = sPrefsSource.InitFromConfigFile(theConfigFilePath); if ( prefsErr ) qtss_printf("Could not load configuration file at %s.\n Generating a new prefs file at %s\n", theConfigFilePath, theXMLFilePath); // // Generate a brand-new XML prefs file out of the old prefs int xmlGenerateErr = GenerateAllXMLPrefs(&sPrefsSource, sXMLParser); if (xmlGenerateErr) { qtss_printf("Fatal Error: Could not create new prefs file at: %s. (%d)\n", theConfigFilePath, OSThread::GetErrno()); ::exit(-1); } } // // Parse the configs from the XML file int xmlParseErr = sXMLParser->Parse(); if (xmlParseErr) { qtss_printf("Fatal Error: Could not load configuration file at %s. (%d)\n", theXMLFilePath, OSThread::GetErrno()); ::exit(-1); } // // Construct a messages source object sMessagesSource.InitFromConfigFile("qtssmessages.txt"); // // Start Win32 DLLs WORD wsVersion = MAKEWORD(1, 1); WSADATA wsData; (void)::WSAStartup(wsVersion, &wsData); if (notAService) { // If we're running off the command-line, don't do the service initiation crap. ::StartServer(sXMLParser, &sMessagesSource, sPort, sStatsUpdateInterval, sInitialState, false,0, kRunServerDebug_Off); // No stats update interval for now ::RunServer(); ::exit(0); } SERVICE_TABLE_ENTRY dispatchTable[] = { { "", ServiceMain }, { NULL, NULL } }; // // In case someone runs the server improperly, print out a friendly message. qtss_printf("Darwin Streaming Server must either be started from the DOS Console\n"); qtss_printf("using the -d command-line option, or using the Service Control Manager\n\n"); qtss_printf("Waiting for the Service Control Manager to start Darwin Streaming Server...\n"); BOOL theErr = ::StartServiceCtrlDispatcher(dispatchTable); if (!theErr) { qtss_printf("Fatal Error: Couldn't start Service\n"); ::exit(-1); } return (0); }
// this routine is not used Bool16 RTPSessionOutput::PacketShouldBeThinned(QTSS_RTPStreamObject inStream, StrPtrLen* inPacket) { return false; // function is disabled. static UInt16 sZero = 0; //This function determines whether the packet should be dropped. //It also adjusts the sequence number if necessary if (inPacket->Len < 4) return false; UInt16 curSeqNum = this->GetPacketSeqNumber(inPacket); UInt32* curQualityLevel = NULL; UInt16* nextSeqNum = NULL; UInt16* theSeqNumOffset = NULL; SInt64* lastChangeTime = NULL; UInt32 theLen = 0; (void)QTSS_GetValuePtr(inStream, qtssRTPStrQualityLevel, 0, (void**)&curQualityLevel, &theLen); if ((curQualityLevel == NULL) || (theLen != sizeof(UInt32))) return false; (void)QTSS_GetValuePtr(inStream, sNextSeqNumAttr, 0, (void**)&nextSeqNum, &theLen); if ((nextSeqNum == NULL) || (theLen != sizeof(UInt16))) { nextSeqNum = &sZero; (void)QTSS_SetValue(inStream, sNextSeqNumAttr, 0, nextSeqNum, sizeof(UInt16)); } (void)QTSS_GetValuePtr(inStream, sSeqNumOffsetAttr, 0, (void**)&theSeqNumOffset, &theLen); if ((theSeqNumOffset == NULL) || (theLen != sizeof(UInt16))) { theSeqNumOffset = &sZero; (void)QTSS_SetValue(inStream, sSeqNumOffsetAttr, 0, theSeqNumOffset, sizeof(UInt16)); } UInt16 newSeqNumOffset = *theSeqNumOffset; (void)QTSS_GetValuePtr(inStream, sLastQualityChangeAttr, 0, (void**)&lastChangeTime, &theLen); if ((lastChangeTime == NULL) || (theLen != sizeof(SInt64))) { static SInt64 startTime = 0; lastChangeTime = &startTime; (void)QTSS_SetValue(inStream, sLastQualityChangeAttr, 0, lastChangeTime, sizeof(SInt64)); } SInt64 timeNow = OS::Milliseconds(); if (*lastChangeTime == 0 || *curQualityLevel == 0) *lastChangeTime =timeNow; if (*curQualityLevel > 0 && ((*lastChangeTime + 30000) < timeNow) ) // 30 seconds between reductions { *curQualityLevel -= 1; // reduce quality value. If we quality doesn't change then we may have hit some steady state which we can't get out of without thinning or increasing the quality *lastChangeTime =timeNow; //qtss_printf("RTPSessionOutput set quality to %"_U32BITARG_"\n",*curQualityLevel); } //Check to see if we need to drop to audio only if ((*curQualityLevel >= ReflectorSession::kAudioOnlyQuality) && (*nextSeqNum == 0)) { #if REFLECTOR_THINNING_DEBUGGING || RTP_SESSION_DEBUGGING qtss_printf(" *** Reflector Dropping to audio only *** \n"); #endif //All we need to do in this case is mark the sequence number of the first dropped packet (void)QTSS_SetValue(inStream, sNextSeqNumAttr, 0, &curSeqNum, sizeof(UInt16)); *lastChangeTime =timeNow; } //Check to see if we can reinstate video if ((*curQualityLevel == ReflectorSession::kNormalQuality) && (*nextSeqNum != 0)) { //Compute the offset amount for each subsequent sequence number. This offset will //alter the sequence numbers so that they increment normally (providing the illusion to the //client that there are no missing packets) newSeqNumOffset = (*theSeqNumOffset) + (curSeqNum - (*nextSeqNum)); (void)QTSS_SetValue(inStream, sSeqNumOffsetAttr, 0, &newSeqNumOffset, sizeof(UInt16)); (void)QTSS_SetValue(inStream, sNextSeqNumAttr, 0, &sZero, sizeof(UInt16)); } //tell the caller whether to drop this packet or not. if (*curQualityLevel >= ReflectorSession::kAudioOnlyQuality) return true; else { //Adjust the sequence number of the current packet based on the offset, if any curSeqNum -= newSeqNumOffset; this->SetPacketSeqNumber(inPacket, curSeqNum); return false; } }
int main(int argc, char *argv[]) { // Temporary vars int ch; UInt16 tempInt16; UInt32 tempInt32; Float32 tempFloat32; Float64 tempFloat64; // General vars int fd; const char *MovieFilename; int TrackNumber; QTTrack *Track; QTHintTrack *HintTrack; bool Debug = false, DeepDebug = false; UInt16 NumPackets; extern int optind; // // Read our command line options while( (ch = getopt(argc, argv, "dD")) != -1 ) { switch( ch ) { case 'd': Debug = true; break; case 'D': Debug = true; DeepDebug = true; break; } } argc -= optind; argv += optind; // // Validate our arguments. if( argc != 2 ) { qtss_printf("usage: QTRTPGen [-d] [-D] <filename> <track number>\n"); exit(1); } MovieFilename = *argv++; TrackNumber = atoi(*argv++); // // Open the movie. QTFile file(Debug, DeepDebug); file.Open(MovieFilename); // // Find the specified track and dump out information about its' samples. if( !file.FindTrack(TrackNumber, &Track) ) { qtss_printf("Error! Could not find track number %d in file \"%s\"!", TrackNumber, MovieFilename); exit(1); } // // Make sure that this is a hint track. if( !file.IsHintTrack(Track) ) { qtss_printf("Error! Track number %d is not a hint track!\n", TrackNumber); exit(1); } HintTrack = (QTHintTrack *)Track; // // Initialize this track. HintTrack->Initialize(); // // Dump some information about this track. { time_t unixCreationTime = (time_t)HintTrack->GetCreationTime() + (time_t)QT_TIME_TO_LOCAL_TIME; time_t unixModificationTime = (time_t)HintTrack->GetModificationTime() + (time_t)QT_TIME_TO_LOCAL_TIME; char buffer[kTimeStrSize]; struct tm timeResult; qtss_printf("-- Track #%02"_S32BITARG_" ---------------------------\n", HintTrack->GetTrackID()); qtss_printf(" Name : %s\n", HintTrack->GetTrackName()); qtss_printf(" Created on : %s", qtss_asctime(qtss_gmtime(&unixCreationTime, &timeResult),buffer, sizeof(buffer))); qtss_printf(" Modified on : %s", qtss_asctime(qtss_gmtime(&unixModificationTime, &timeResult),buffer, sizeof(buffer))); qtss_printf(" Total RTP bytes : %"_64BITARG_"u\n", HintTrack->GetTotalRTPBytes()); qtss_printf(" Total RTP packets : %"_64BITARG_"u\n", HintTrack->GetTotalRTPPackets()); qtss_printf(" Average bitrate : %.2f Kbps\n",(float) ((HintTrack->GetTotalRTPBytes() << 3) / file.GetDurationInSeconds()) / 1024); qtss_printf(" Average packet size: %"_64BITARG_"u\n", HintTrack->GetTotalRTPBytes() / HintTrack->GetTotalRTPPackets()); UInt32 UDPIPHeaderSize = (56 * HintTrack->GetTotalRTPPackets()); UInt32 RTPUDPIPHeaderSize = ((56+12) * HintTrack->GetTotalRTPPackets()); qtss_printf(" Percentage of stream wasted on UDP/IP headers : %.2f\n", (float)UDPIPHeaderSize / (float)(HintTrack->GetTotalRTPBytes() + UDPIPHeaderSize) * 100); qtss_printf(" Percentage of stream wasted on RTP/UDP/IP headers: %.2f\n", (float)RTPUDPIPHeaderSize / (float)(HintTrack->GetTotalRTPBytes() + RTPUDPIPHeaderSize) * 100); qtss_printf("\n"); qtss_printf("\n"); } // // Open our file to write the packets out to. fd = open("track.cache", O_CREAT | O_TRUNC | O_WRONLY, 0666); if( fd == -1 ) { qtss_printf("Error! Could not create output file!\n"); exit(1); } // // Write out the header. tempInt16 = 1; write(fd, (char *)&tempInt16, 2); // isCompletelyWritten tempInt16 = 0; write(fd, (char *)&tempInt16, 2); // padding1 tempFloat64 = file.GetDurationInSeconds(); write(fd, (char *)&tempFloat64, 8); // movieLength tempInt32 = HintTrack->GetRTPTimescale(); write(fd, (char *)&tempInt32, 4); // rtpTimescale tempInt16 = HintTrack->GetRTPSequenceNumberRandomOffset(); write(fd, (char *)&tempInt16, 2); // seqNumRandomOffset tempInt16 = 0; write(fd, (char *)&tempInt16, 2); // padding2 tempFloat32 = HintTrack->GetTotalRTPBytes() / file.GetDurationInSeconds(); write(fd, (char *)&tempFloat32, 4); // dataRate // // Go through all of the samples in this track, printing out their offsets // and sizes. qtss_printf("Sample # NPkts\n"); qtss_printf("-------- -----\n"); UInt32 curSample = 1; QTHintTrack_HintTrackControlBlock HCB; while( HintTrack->GetNumPackets(curSample, &NumPackets) == QTTrack::errNoError ) { // // Generate all of the packets. qtss_printf("Generating %u packet(s) in sample #%"_U32BITARG_"..\n", NumPackets, curSample); for( UInt16 curPacket = 1; curPacket <= NumPackets; curPacket++ ) { // General vars #define MAX_PACKET_LEN 2048 char Packet[MAX_PACKET_LEN]; UInt32 PacketLength; Float64 TransmitTime; // // Generate this packet. PacketLength = MAX_PACKET_LEN; HintTrack->GetPacket(curSample, curPacket, Packet, &PacketLength, &TransmitTime, false, false, 0,&HCB); // // Write out the packet header. write(fd, (char *)&TransmitTime, 8); // transmitTime tempInt16 = PacketLength; write(fd, (char *)&tempInt16, 2); // packetLength tempInt16 = 0; write(fd, (char *)&tempInt16, 2); // padding1 // // Write out the packet. write(fd, Packet, PacketLength); } // // Next sample. curSample++; } // // Close the output file. close(fd); return 0; }
QTSS_Error RTSPResponseStream::WriteV(iovec* inVec, UInt32 inNumVectors, UInt32 inTotalLength, UInt32* outLengthSent, UInt32 inSendType) { QTSS_Error theErr = QTSS_NoErr; UInt32 theLengthSent = 0; UInt32 amtInBuffer = this->GetCurrentOffset() - fBytesSentInBuffer; if (amtInBuffer > 0) { // There is some data in the output buffer. Make sure to send that // first, using the empty space in the ioVec. inVec[0].iov_base = this->GetBufPtr() + fBytesSentInBuffer; inVec[0].iov_len = amtInBuffer; theErr = fSocket->WriteV(inVec, inNumVectors, &theLengthSent); if (fPrintRTSP) { DateBuffer theDate; DateTranslator::UpdateDateBuffer(&theDate, 0); // get the current GMT date and time qtss_printf("\n#S->C:\n#time: ms=%"_U32BITARG_" date=%s\n", (UInt32) OS::StartTimeMilli_Int(), theDate.GetDateBuffer() ); for (UInt32 i =0; i < inNumVectors; i++) { StrPtrLen str((char*)inVec[i].iov_base, (UInt32) inVec[i].iov_len); str.PrintStrEOL(); } } if (theLengthSent >= amtInBuffer) { // We were able to send all the data in the buffer. Great. Flush it. this->Reset(); fBytesSentInBuffer = 0; // Make theLengthSent reflect the amount of data sent in the ioVec theLengthSent -= amtInBuffer; } else { // Uh oh. Not all the data in the buffer was sent. Update the // fBytesSentInBuffer count, and set theLengthSent to 0. fBytesSentInBuffer += theLengthSent; Assert(fBytesSentInBuffer < this->GetCurrentOffset()); theLengthSent = 0; } // theLengthSent now represents how much data in the ioVec was sent } else if (inNumVectors > 1) { theErr = fSocket->WriteV(&inVec[1], inNumVectors - 1, &theLengthSent); } // We are supposed to refresh the timeout if there is a successful write. if (theErr == QTSS_NoErr) fTimeoutTask->RefreshTimeout(); // If there was an error, don't alter anything, just bail if ((theErr != QTSS_NoErr) && (theErr != EAGAIN)) return theErr; // theLengthSent at this point is the amount of data passed into // this function that was sent. if (outLengthSent != NULL) *outLengthSent = theLengthSent; // Update the StringFormatter fBytesWritten variable... this data // wasn't buffered in the output buffer at any time, so if we // don't do this, this amount would get lost fBytesWritten += theLengthSent; // All of the data was sent... whew! if (theLengthSent == inTotalLength) return QTSS_NoErr; // We need to determine now whether to copy the remaining unsent // iovec data into the buffer. This is determined based on // the inSendType parameter passed in. if (inSendType == kDontBuffer) return theErr; if ((inSendType == kAllOrNothing) && (theLengthSent == 0)) return EAGAIN; // Some or none of the iovec data was sent. Copy the remainder into the output // buffer. // The caller should consider this data sent. if (outLengthSent != NULL) *outLengthSent = inTotalLength; UInt32 curVec = 1; while (theLengthSent >= inVec[curVec].iov_len) { // Skip over the vectors that were in fact sent. Assert(curVec < inNumVectors); theLengthSent -= inVec[curVec].iov_len; curVec++; } while (curVec < inNumVectors) { // Copy the remaining vectors into the buffer this->Put( ((char*)inVec[curVec].iov_base) + theLengthSent, inVec[curVec].iov_len - theLengthSent); theLengthSent = 0; curVec++; } return QTSS_NoErr; }
QTSS_Error EasyCMSSession::ProcessMessage() { if(NULL == fRequest) return QTSS_BadArgument; //解析HTTPRequest报文 QTSS_Error theErr = fRequest->Parse(); if (theErr != QTSS_NoErr) return QTSS_BadArgument; //获取具体Content json数据部分 StrPtrLen* lengthPtr = fRequest->GetHeaderValue(httpContentLengthHeader); StringParser theContentLenParser(lengthPtr); theContentLenParser.ConsumeWhitespace(); UInt32 content_length = theContentLenParser.ConsumeInteger(NULL); if (content_length) { qtss_printf("EasyCMSSession::ProcessMessage read content-length:%d \n", content_length); // 检查content的fContentBuffer和fContentBufferOffset是否有值存在,如果存在,说明我们已经开始 // 进行content请求处理,如果不存在,我们需要创建并初始化fContentBuffer和fContentBufferOffset if (fContentBuffer == NULL) { fContentBuffer = NEW char[content_length + 1]; memset(fContentBuffer,0,content_length + 1); fContentBufferOffset = 0; } UInt32 theLen = 0; // 读取HTTP Content报文数据 theErr = fInputStream.Read(fContentBuffer + fContentBufferOffset, content_length - fContentBufferOffset, &theLen); Assert(theErr != QTSS_BadArgument); if (theErr == QTSS_RequestFailed) { OSCharArrayDeleter charArrayPathDeleter(fContentBuffer); fContentBufferOffset = 0; fContentBuffer = NULL; return QTSS_RequestFailed; } qtss_printf("EasyCMSSession::ProcessMessage() Add Len:%d \n", theLen); if ((theErr == QTSS_WouldBlock) || (theLen < ( content_length - fContentBufferOffset))) { // // Update our offset in the buffer fContentBufferOffset += theLen; Assert(theErr == QTSS_NoErr); return QTSS_WouldBlock; } Assert(theErr == QTSS_NoErr); // 处理完成报文后会自动进行Delete处理 OSCharArrayDeleter charArrayPathDeleter(fContentBuffer); qtss_printf("EasyCMSSession::ProcessMessage() Get Complete Msg:\n%s", fContentBuffer); EasyProtocol protocol(fContentBuffer); int nNetMsg = protocol.GetMessageType(); switch (nNetMsg) { case MSG_SC_FREE_STREAM_ACK: { string strErrorNum = protocol.GetHeaderValue(EASY_TAG_ERROR_NUM); string strSerial = protocol.GetBodyValue(EASY_TAG_SERIAL); string strChannle = protocol.GetBodyValue(EASY_TAG_CHANNEL); qtss_printf("EasyCMS停止推流响应:%s,Serial=%s,Channel=%s",strErrorNum.c_str(),strSerial.c_str(),strChannle.c_str()); fLiveSession=false;//进入析构 } break; default: break; } }