/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2008 Apple Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ * */ /* File: StreamingLoadTool.cpp Contains: Tool that simulates streaming movie load */ #include #include #include #include #include #include #ifndef kVersionString #include "../revision.h" #endif #ifndef __Win32__ #include #include #include #include #include #include #include #endif #include "ClientSession.h" #include "FilePrefsSource.h" #include "OSMemory.h" #include "OSArrayObjectDeleter.h" #include "SocketUtils.h" #include "StringFormatter.h" #include "Socket.h" #include "OS.h" #include "Task.h" #include "TimeoutTask.h" #include "SVector.h" #ifndef __MacOSX__ #include "getopt.h" #include "../revision.h" #endif #define STREAMINGLOADTOOL_DEBUG 0 #define PACKETADDSIZE 28 // IP headers = 20 + UDP headers = 8 // // Static data static UInt32 sConnectionsThatErrored = 0; static UInt32 sFailedConnections = 0; static UInt32 sSuccessfulConnections = 0; static FILE* sLog = NULL; static ClientSession** sClientSessionArray = NULL; static UInt32 sNumClients = 1; static Bool16 sNumClientsIsSpecified = false; static Bool16 sGotSigInt = false; static Bool16 sQuitNow = false; static SInt64 sSigIntTime = 0; static UInt64 sTotalBytesReceived = 0; static UInt64 sTotalPacketsReceived = 0; static UInt64 sTotalPacketsLost = 0; static UInt64 sTotalOutOfOrder = 0; static UInt64 sTotalOutOfBound = 0; static UInt64 sTotalDuplicates = 0; static UInt64 sTotalNumAcks = 0; static UInt64 sTotalMalformed = 0; static UInt64 sTotalLatePackets; static UInt64 sTotalBufferOverflowedPackets; static Bool16 sEnable3GPP = false; int main(int argc, char *argv[]); // // Helper functions char* GetClientTypeDescription(ClientSession::ClientType inClientType); void DoDNSLookup(SVector &theURLlist, SVector &ioIPAddrs); void RecordClientInfoBeforeDeath(ClientSession* inSession); char* GetDeathReasonDescription(UInt32 inDeathReason); char* GetPayloadDescription(QTSS_RTPPayloadType inPayload); void CheckForStreamingLoadToolDotMov(SVector &ioIPAddrArray, SVector &theURLlist, UInt16 inPort, SVector &userList, SVector &passwordList, UInt32 verboseLevel); UInt32 CalcStartTime(Bool16 inRandomThumb, UInt32 inMovieLength); extern char* optarg; #ifndef __Win32__ void sigcatcher(int sig, int /*sinfo*/, struct sigcontext* /*sctxt*/); void sigcatcher(int sig, int /*sinfo*/, struct sigcontext* /*sctxt*/) { //printf("sigcatcher =%d\n", sig); if ((sig == SIGINT) || (sig == SIGTERM)) { // TheTime check and sQuitNow flag tests are needed test with Linux and OSX. if (sGotSigInt && (OS::Milliseconds() > sSigIntTime + 500) ) { if (!sQuitNow)// print only once printf("Force quitting\n"); sQuitNow = true; } else { sSigIntTime = OS::Milliseconds(); sGotSigInt = true; } } } #endif int main(int argc, char *argv[]) { #ifndef __Win32__ struct sigaction act; #if defined(sun) || defined(i386) || 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(SIGINT, &act, NULL); (void)::sigaction(SIGTERM, &act, NULL); #if __MacOSX__ || __solaris__ //grow our pool of file descriptors to the max! struct rlimit rl; rl.rlim_cur = Socket::kMaxNumSockets; rl.rlim_max = Socket::kMaxNumSockets; setrlimit (RLIMIT_NOFILE, &rl); #endif #else // // Start Win32 DLLs WORD wsVersion = MAKEWORD(1, 1); WSADATA wsData; (void)::WSAStartup(wsVersion, &wsData); #endif #ifdef __Win32__ char* configFilePath = "streamingloadtool.cfg"; #else char* configFilePath = "streamingloadtool.conf"; #endif Bool16 configFilePathIsSpecified = false; Bool16 dropPost = false; ClientSession::ClientType theClientType = ClientSession::kRTSPUDPClientType; Bool16 theClientTypeIsSpecified = false; UInt16 thePort = 554; Bool16 thePortIsSpecified = false; UInt32 theMovieLength = 60; Bool16 theMovieLengthIsSpecified = false; Bool16 runForever = false; UInt32 theHTTPCookie = 1; Bool16 shouldLog = false; char* logPath = "streamingloadtool.log"; UInt32 proxyIP = 0; Bool16 appendJunk = false; UInt32 theReadInterval = 50; UInt32 sockRcvBuf = 32768; Float32 lateTolerance = 0; char* rtpMetaInfo = NULL; Float32 speed = 1; UInt32 verboseLevel = 0; char* packetPlayHeader = NULL; UInt32 overbufferwindowInK = 0; Bool16 randomThumb = false; Bool16 sendOptions = false; Bool16 requestRandomData = false; SInt32 randomDataSize = 0; UInt32 rtcpInterval = 5000; UInt32 bandwidth = 0; UInt32 guarenteedBitRate = 0; UInt32 maxBitRate = 0; UInt32 maxTransferDelay = 0; Bool16 enableForcePlayoutDelay = false; UInt32 playoutDelay = 0; UInt32 bufferSpace = 100000; UInt32 delayTime = 10000; Float32 startDelayFrac = 0.5; // // Set up our User Agent string for the RTSP client char theUserAgentStr[128]; ::sprintf(theUserAgentStr, "StreamingLoadTool-%s",kVersionString); RTSPClient::SetUserAgentStr(theUserAgentStr); SVectoruserList; SVectorpasswordList; SVector theURLlist; char* controlID = NULL; char ch = 0; // // Read our command line options while( (ch = getopt(argc, argv, "vf:c:i:V:u:t:p:n:l:s:")) != -1 ) { switch( ch ) { case '?': case 'v': printf("StreamingLoadTool %s, built on %s, %s.\n", kVersionString, __DATE__ , __TIME__); printf("Usage: StreamingLoadTool [-u theURL] [-t transport] [-n port] [-l length] [-i urlid] [-f path] [-c #] [-V #]\n"); printf("-v: Print this message\n"); printf("-f: Config file to use. Defaults to streamingloadtool.conf if the URL (-u) is not specified.\n"); printf("-c: HTTP cookie to use. Overrides what is in config file\n"); printf("-V: debug verbose level\n"); printf("-i: RTSP stream URL id (i.e. trackID or streamID etc.). Default is -i trackID\n"); printf("-u: the URL (format: rtsp://etc/movie.mov).\n"); printf("-t: the transport type: udp, reliableudp, tcp, or http.\n"); printf("-p: the port.\n"); printf("-n: the number of clients.\n"); printf("-l: the movie duration.\n"); printf("-s: the user and password format: (user:password).\n"); exit(0); break; case 'f': configFilePathIsSpecified = true; configFilePath = optarg; break; case 'i': controlID = optarg; break; case 'c': theHTTPCookie = atoi(optarg); break; case 'V': verboseLevel = atoi(optarg); break; case 'u': theURLlist.push_back(optarg); break; case 't': theClientTypeIsSpecified = true; if (::strcmp("http", optarg) == 0) theClientType = ClientSession::kRTSPHTTPClientType; else if (::strcmp("reliableudp", optarg) == 0) theClientType = ClientSession::kRTSPReliableUDPClientType; else if (::strcmp("tcp", optarg) == 0) theClientType = ClientSession::kRTSPTCPClientType; else if (::strcmp("udp", optarg) == 0) theClientType = ClientSession::kRTSPUDPClientType; else if (::strcmp("3gudp", optarg) == 0) { theClientType = ClientSession::kRTSPUDPClientType; sEnable3GPP = true; } else { printf("Invalid transport type: %s\n", optarg); exit(0); } break; case 'p': thePortIsSpecified = true; thePort = atoi(optarg); break; case 'n': sNumClientsIsSpecified = true; sNumClients = atoi(optarg); break; case 'l': theMovieLengthIsSpecified = true; theMovieLength = atoi(optarg); break; case 's' : { char *str = ::strdup(optarg); char *user = str; char *password = NULL; char *colonChar = ::strchr(str, ':'); printf("optarg=%s\n",str); if (colonChar != NULL) { *colonChar = '\0'; password = colonChar + 1; } if ((user != NULL) && (password != NULL) ) { userList.push_back(user); passwordList.push_back(password); } break; } } } FilePrefsSource theFileParser(true); // Get settings from the file Bool16 configFileError = theFileParser.InitFromConfigFile(configFilePath); if (configFileError && theURLlist.empty()) { printf("Couldn't find StreamingLoadTool config file at: %s\n", configFilePath); ::exit(-1); } else if (!configFileError) { for (UInt32 x = 0; true; x++) { char* theValue = theFileParser.GetValueAtIndex(x); char* theKey = theFileParser.GetKeyAtIndex(x); if (theKey == NULL) break; if (theValue != NULL) { if (::strcmp("clienttype", theKey) == 0 && !theClientTypeIsSpecified) { if (::strcmp("http", theValue) == 0) theClientType = ClientSession::kRTSPHTTPClientType; else if (::strcmp("reliableudp", theValue) == 0) theClientType = ClientSession::kRTSPReliableUDPClientType; else if (::strcmp("tcp", theValue) == 0) theClientType = ClientSession::kRTSPTCPClientType; else if (::strcmp("udp", theValue) == 0) theClientType = ClientSession::kRTSPUDPClientType; else if (::strcmp("3gudp", theValue) == 0) { theClientType = ClientSession::kRTSPUDPClientType; sEnable3GPP = true; } else { printf("Invalid transport type: %s\n", theValue); exit(0); } } else if (::strcmp("player", theKey) == 0) { if (theValue[0] != 0) { ::strncpy(theUserAgentStr, theValue, sizeof(theUserAgentStr)); theUserAgentStr[sizeof(theUserAgentStr) -1] = 0; RTSPClient::SetUserAgentStr(theUserAgentStr); } } else if (::strcmp("droppost", theKey) == 0) { if (::strcmp("yes", theValue) == 0) dropPost = true; } else if (::strcmp("concurrentclients", theKey) == 0 && !sNumClientsIsSpecified) { ::sscanf(theValue, "%"_U32BITARG_"", &sNumClients); } else if (::strcmp("port", theKey) == 0 && !thePortIsSpecified) { UInt32 tempPort = 0; ::sscanf(theValue, "%"_U32BITARG_"", &tempPort); thePort = (UInt16)tempPort; } else if (::strcmp("movielength", theKey) == 0 && !theMovieLengthIsSpecified) { ::sscanf(theValue, "%"_U32BITARG_"", &theMovieLength); } else if (::strcmp("runforever", theKey) == 0) { if (::strcmp("yes", theValue) == 0) runForever = true; } else if (::strcmp("shouldlog", theKey) == 0) { if (::strcmp("yes", theValue) == 0) shouldLog = true; } else if (::strcmp("appendjunk", theKey) == 0) { if (::strcmp("yes", theValue) == 0) appendJunk = true; } else if (::strcmp("logpath", theKey) == 0) { logPath = theValue; } else if (::strcmp("proxyip", theKey) == 0) { proxyIP = SocketUtils::ConvertStringToAddr(theValue); } else if (::strcmp("clientwindow", theKey) == 0) { ::sscanf(theValue, "%"_U32BITARG_"", &sockRcvBuf); } else if (::strcmp("httpcookie", theKey) == 0) { if (theHTTPCookie == 1) { // Ignore if set by command line ::sscanf(theValue, "%"_U32BITARG_"", &theHTTPCookie); theHTTPCookie *= 1000000; } } else if (::strcmp("readinterval", theKey) == 0) { ::sscanf(theValue, "%"_U32BITARG_"", &theReadInterval); } else if (::strcmp("latetolerance", theKey) == 0) { ::sscanf(theValue, "%f", &lateTolerance); } else if (::strcmp("rtpmetainfo", theKey) == 0) { if (::strlen(theValue) > 0) rtpMetaInfo = theValue; } else if (::strcmp("speed", theKey) == 0) { ::sscanf(theValue, "%f", &speed); } else if (::strcmp("packetplayheader", theKey) == 0) { if (::strlen(theValue) > 0) packetPlayHeader = theValue; } else if (::strcmp("overbufferwindowsize", theKey) == 0) { ::sscanf(theValue, "%"_U32BITARG_"", &overbufferwindowInK); } else if (::strcmp("randomthumb", theKey) == 0) { if (::strcmp("yes", theValue) == 0) randomThumb = true; } else if (::strcmp("sendoptions", theKey) == 0) { if (::strcmp("yes", theValue) == 0) sendOptions = true; } else if (::strcmp("requestrandomdata", theKey) == 0) { if (::strcmp("yes", theValue) == 0) requestRandomData = true; } else if (::strcmp("randomdatasize", theKey) == 0) { ::sscanf(theValue, "%"_S32BITARG_"", &randomDataSize); } else if (::strcmp("rtcpinterval", theKey) == 0) { ::sscanf(theValue, "%"_S32BITARG_"", &rtcpInterval); } else if (::strcmp("url", theKey) == 0) { theURLlist.push_back(theValue); } else if (::strcmp("user", theKey) == 0) { char *str = ::strdup(theValue); char *user = str; char *password = NULL; char *colonChar = ::strchr(str, ':'); if (colonChar != NULL) { *colonChar = '\0'; password = colonChar + 1; } userList.push_back(user); passwordList.push_back(password); } else if (::strcmp("bandwidth", theKey) == 0) { ::sscanf(theValue, "%"_U32BITARG_, &bandwidth); } else if (::strcmp("enable3GPP", theKey) == 0) { if (::strcmp("yes", theValue) == 0) sEnable3GPP = true; } else if (::strcmp("GBW", theKey) == 0) { ::sscanf(theValue, "%"_U32BITARG_, &guarenteedBitRate); } else if (::strcmp("MBW", theKey) == 0) { ::sscanf(theValue, "%"_U32BITARG_, &maxBitRate); } else if (::strcmp("MTD", theKey) == 0) { ::sscanf(theValue, "%"_U32BITARG_, &maxTransferDelay); } else if (::strcmp("enableForcePlayoutDelay", theKey) == 0) { if (::strcmp("yes", theValue) == 0) enableForcePlayoutDelay = true; } else if (::strcmp("playoutDelay", theKey) == 0) { ::sscanf(theValue, "%"_U32BITARG_, &playoutDelay); } else if (::strcmp("buffer", theKey) == 0) { ::sscanf(theValue, "%"_U32BITARG_"", &bufferSpace); } else if (::strcmp("delay", theKey) == 0) { ::sscanf(theValue, "%"_U32BITARG_"", &delayTime); } else if (::strcmp("startdelay", theKey) == 0) { ::sscanf(theValue, "%f", &startDelayFrac); if(startDelayFrac < 0) startDelayFrac = -1.0; } else printf("Found bad directive in StreamingLoadTool config file: %s\n", theKey); } } } if(theURLlist.empty()) { printf("Didn't find any URLs in StreamingLoadTool config file.\n"); exit(-1); } // // Figure out what type of clients we are going to run if ((theClientType == ClientSession::kRTSPHTTPClientType) && dropPost) theClientType = ClientSession::kRTSPHTTPDropPostClientType; // Do IP lookups on all the URLs SVector theIPAddrArray; theIPAddrArray.resize(theURLlist.size(), 0); ::DoDNSLookup(theURLlist, theIPAddrArray); // Final setup before running the show OS::Initialize(); OSThread::Initialize(); OSMemory::SetMemoryError(ENOMEM); Socket::Initialize(); SocketUtils::Initialize(); #if !MACOSXEVENTQUEUE ::select_startevents();//initialize the select() implementation of the event queue #endif TaskThreadPool::AddThreads(1); TimeoutTask::Initialize(); Socket::StartThread(); //Check for the existance of a file streamingloadtool.mov before proceeding ::CheckForStreamingLoadToolDotMov(theIPAddrArray, theURLlist, thePort, userList, passwordList, verboseLevel); // If user specified a proxy to connect through, use that IP address, not the IP address in the URL. if (proxyIP != 0) { for (UInt32 ipAddrCounter = 0; ipAddrCounter < theURLlist.size(); ipAddrCounter++) theIPAddrArray[ipAddrCounter] = proxyIP; } // Open the log if we are logging if (shouldLog) sLog = ::fopen(logPath, "w"); sClientSessionArray = NEW ClientSession*[sNumClients]; UInt32 theCurURLIndex = 0; UInt32 theCurUserIndex = 0; for (UInt32 clientCount = 0; clientCount < sNumClients; clientCount++, theCurURLIndex = (theCurURLIndex + 1) % theURLlist.size(), theCurUserIndex++) { while(theIPAddrArray[theCurURLIndex] == 0) theCurURLIndex = (theCurURLIndex + 1) % theURLlist.size(); if (theCurUserIndex == userList.size()) theCurUserIndex = 0; sClientSessionArray[clientCount] = NEW ClientSession( theIPAddrArray[theCurURLIndex], thePort, theURLlist[theCurURLIndex], theClientType, // Client type theMovieLength, // Movie length CalcStartTime(randomThumb, theMovieLength), // Movie start time rtcpInterval, 0, // Options interval theHTTPCookie++, // HTTP cookie appendJunk, theReadInterval, // Interval between data reads sockRcvBuf, // socket recv buffer size lateTolerance, // late tolerance rtpMetaInfo, speed, verboseLevel, packetPlayHeader, overbufferwindowInK, sendOptions, // send options request before the Describe requestRandomData, // request random data in the options request randomDataSize, // size of the random data to request sEnable3GPP, guarenteedBitRate, maxBitRate, maxTransferDelay, enableForcePlayoutDelay, playoutDelay, bandwidth, bufferSpace, delayTime, static_cast(startDelayFrac * delayTime), controlID, userList.empty() ? NULL : userList[theCurUserIndex], passwordList.empty() ? NULL : passwordList[theCurUserIndex]); #if STREAMINGLOADTOOL_DEBUG printf("Creating a ClientSession for URL: %s\n", theURLlist[theCurURLIndex]); #endif } // // Now, all we have to do is loop, destroying and re-creating ClientSession objects when they die. // Occassionally, lets print out status to show what is currently going on Bool16 isStillActive = false; UInt32 loopCount = 0; do { if (sGotSigInt) break; OSThread::Sleep(1000); isStillActive = false; for (UInt32 y = 0; y < sNumClients; y++) { if (sClientSessionArray == NULL) continue; //skip over NULL client sessions if ((loopCount & 7) == 0) // Fancy way of mod'ing by 8, so the following will execute every 8 seconds { // if the server has not sent me any packets in the last 8 seconds, and the at least 10 seconds has elapsed since the RTSP play, then timeout if ( (sClientSessionArray[y] != NULL && sClientSessionArray[y]->GetSessionPacketsReceived() == 0 && sClientSessionArray[y]->GetTotalPlayTimeInMsec() > 10000) ) { sClientSessionArray[y]->Signal(Task::kTimeoutEvent); // Tell it to destroy itself if the Client is idle for a while } } if ( (sClientSessionArray[y] != NULL && sClientSessionArray[y]->IsDone()) ) { while(theIPAddrArray[theCurURLIndex] == 0) theCurURLIndex = (theCurURLIndex + 1) % theURLlist.size(); if (theCurUserIndex == userList.size()) theCurUserIndex = 0; ::RecordClientInfoBeforeDeath(sClientSessionArray[y]); sClientSessionArray[y]->Signal(Task::kKillEvent); // Tell it to destroy itself sClientSessionArray[y] = NULL; if (runForever) { isStillActive = true; // If there is a new URL to fetch, kill this client and restart sClientSessionArray[y] = NEW ClientSession( theIPAddrArray[theCurURLIndex], // IP addr thePort, // IP port theURLlist[theCurURLIndex], theClientType, // Client type theMovieLength, // Movie length CalcStartTime(randomThumb, theMovieLength), // Movie start time rtcpInterval, 0, // Options interval theHTTPCookie++, // HTTP cookie appendJunk, theReadInterval, // Interval between data reads sockRcvBuf, // socket recv buffer size lateTolerance, // late tolerance rtpMetaInfo, speed, verboseLevel, packetPlayHeader, overbufferwindowInK, sendOptions, // send options request before the Describe requestRandomData, // request random data in the options request randomDataSize, // size of the random data to request sEnable3GPP, guarenteedBitRate, maxBitRate, maxTransferDelay, enableForcePlayoutDelay, playoutDelay, bandwidth, bufferSpace, delayTime, (startDelayFrac < 0) ? static_cast(kUInt32_Max) : static_cast(startDelayFrac * delayTime), controlID, userList.empty() ? NULL : userList[theCurUserIndex], passwordList.empty() ? NULL : passwordList[theCurUserIndex]); #if STREAMINGLOADTOOL_DEBUG printf("Creating a ClientSession for URL: %s\n", theURLlist[theCurURLIndex]); #endif theCurURLIndex = (theCurURLIndex + 1) % theURLlist.size(); theCurUserIndex++; } } else if (sClientSessionArray[y] != NULL) //still running isStillActive = true; } if ((loopCount & 7) == 0) // Fancy way of mod'ing by 8, so the following will execute every 8 seconds { if ((loopCount & 127) == 0) // Fancy way of dividing by 127, { // Occassionally give the user lots of info printf("StreamingLoadTool test in progress.\n"); printf("Config file: %s. Client type: %s. Num clients: %"_U32BITARG_".\n", configFilePath, GetClientTypeDescription(theClientType), sNumClients); printf("Movie length: %"_U32BITARG_". Run forever: %d. HTTP cookie: %"_U32BITARG_". Port: %d\n", theMovieLength, runForever, theHTTPCookie, thePort); if (shouldLog) printf("Writing StreamingLoadTool log at: %s\n", logPath); printf("Active Playing Attempts Success Errors Failed Bitrate\n"); } UInt32 addBytes = ClientSession::GetConnectionPacketsReceived() * PACKETADDSIZE; Float32 bitsReceived = (Float32) (ClientSession::GetConnectionBytesReceived() + addBytes) / 1000; bitsReceived += .5; printf("%5lu %6lu %8lu %6lu %6lu %6lu %9.0fk\n", ClientSession:: GetActiveConnections (), ClientSession:: GetPlayingConnections (), ClientSession:: GetConnectionAttempts (), sSuccessfulConnections, sConnectionsThatErrored, sFailedConnections, bitsReceived// depends on 8 second update for bits per second ); } loopCount++; } while (isStillActive == true); if (sGotSigInt) { // // If someone hits Ctrl-C, force all the clients to wrap it up printf("Sending TEARDOWNs.\n"); // // Tell all the clients to wrap it up. for (UInt32 clientCount = 0; clientCount < sNumClients; clientCount++) { if (sClientSessionArray == NULL) continue; //skip over NULL client sessions if (sClientSessionArray[clientCount] != NULL) sClientSessionArray[clientCount]->Signal(ClientSession::kTeardownEvent); } // // Wait for the clients to complete Bool16 isDone = false; while (!isDone && !sQuitNow) { //wait until all the clients are done doing teardown OSThread::Sleep(1000); isDone = true; for (UInt32 cc2 = 0; cc2 < sNumClients; cc2++) { if (sClientSessionArray == NULL) continue; //skip over NULL client sessions if (sClientSessionArray[cc2] == NULL) continue; if (!sClientSessionArray[cc2]->IsDone()) isDone = false; } } } // // We're done... now go through and delete the last sessions(not really) for (UInt32 z = 0; z < sNumClients; z++) { if (sClientSessionArray == NULL) continue; //skip over NULL client sessions if (sClientSessionArray[z] == NULL) continue; ::RecordClientInfoBeforeDeath(sClientSessionArray[z]); } if (sLog != NULL) ::fclose(sLog); printf("%5lu %6lu %8lu %6lu %6lu %6lu %9.0fk\n", ClientSession:: GetActiveConnections (), ClientSession:: GetPlayingConnections (), ClientSession:: GetConnectionAttempts (), sSuccessfulConnections, sConnectionsThatErrored, sFailedConnections, 0.0// depends on 8 second update for bits per second ); printf("StreamingLoadTool test complete. Total number of connections: %"_U32BITARG_".\n", ClientSession:: GetConnectionAttempts ()); printf( "Total bytes received: %"_U64BITARG_ ". Total packets received: %"_U64BITARG_ ". Total out of order packets: %"_U64BITARG_ ". Total out of bound packets: %"_U64BITARG_ ". Total ACKs sent: %"_U64BITARG_ ". Total malformed packets: %"_U64BITARG_, sTotalBytesReceived, sTotalPacketsReceived, sTotalOutOfOrder, sTotalOutOfBound, sTotalNumAcks, sTotalMalformed ); if (sEnable3GPP) { printf( ". Total 3g packets lost: %"_U64BITARG_ ". Total 3g duplicate packets: %"_U64BITARG_ ". Total 3g late packets: %"_U64BITARG_ ". Total 3g buffer-overflowed packets: %"_U64BITARG_, sTotalPacketsLost, sTotalDuplicates, sTotalLatePackets, sTotalBufferOverflowedPackets ); } printf(".\n"); } UInt32 CalcStartTime(Bool16 inRandomThumb, UInt32 inMovieLength) { UInt32 theMovieLength = inMovieLength; if (theMovieLength > 1) theMovieLength--; if (inRandomThumb) return ::rand() % theMovieLength; else return 0; } void CheckForStreamingLoadToolPermission(UInt32* inIPAddrArray, UInt32 inNumURLs, UInt16 inPort) { //Eventually check for the existance of a specially formatted sdp file (assuming the server blindly returns sdps) } //Currently will only authenticate with the FIRST username/password if provided void CheckForStreamingLoadToolDotMov(SVector &ioIPAddrArray, SVector &theURLlist, UInt16 inPort, SVector &userList, SVector &passwordList, UInt32 verboseLevel) { Assert(ioIPAddrArray.size() == theURLlist.size()); printf("Checking for 'streamingloadtool.mov' on the target servers\n"); OSArrayObjectDeleter holder = NEW UInt32[theURLlist.size() + 1]; UInt32* uniqueIPAddrs = holder.GetObject(); ::memset(uniqueIPAddrs, 0, sizeof(UInt32) * (theURLlist.size() + 1)); for (UInt32 count = 0; count < theURLlist.size(); count++) { if (ioIPAddrArray[count] == 0) //skip over one's that failed DNS continue; //check for duplicates /* Bool16 dup = false; for (UInt32 x = 0; uniqueIPAddrs[x] != 0; x++) { if (uniqueIPAddrs[x] == ioIPAddrArray[count]) { dup = true; break; } } if (dup) continue; // For tracking dups. uniqueIPAddrs[count] = ioIPAddrArray[count]; */ // Format the URL: rtsp://xx.xx.xx.xx/streamingloadtool.mov char theAddrBuf[50]; StrPtrLen theAddrBufPtr(theAddrBuf, 50); struct in_addr theAddr; theAddr.s_addr = htonl(ioIPAddrArray[count]); SocketUtils::ConvertAddrToString(theAddr, &theAddrBufPtr); char theURLBuf[100]; StringFormatter theFormatter(theURLBuf, 100); theFormatter.Put("rtsp://"); theFormatter.Put(theAddrBufPtr); theFormatter.Put("/streamingloadtool.mov"); theFormatter.PutTerminator(); StrPtrLenDel theURL(theFormatter.GetAsCString()); // Make an RTSP client. We'll send a DESCRIBE to the server to check for this sucker TCPClientSocket theSocket = TCPClientSocket(0); //blocking // tell the client this is the URL to use theSocket.Set(ioIPAddrArray[count], inPort); RTSPClient theClient = RTSPClient(&theSocket); theClient.SetVerboseLevel(verboseLevel); if(userList.size() > 0) { theClient.SetName(userList.back()); theClient.SetPassword(passwordList.back()); } theClient.Set(theURL); // // Send the DESCRIBE! Whoo hoo! OS_Error theErr = theClient.SendDescribe(); while (theErr == EINPROGRESS || theErr == EAGAIN) theErr = theClient.SendDescribe(); if (theErr != OS_NoErr) { printf("##WARNING: Error connecting to %s.\n\n", theURLlist[count]); ioIPAddrArray[count] = 0; continue; } if (theClient.GetStatus() != 200) { printf("##WARNING: Cannot access %s\n\n", theURL.Ptr); ioIPAddrArray[count] = 0; } theClient.SendTeardown(); } int addrCount = 0; for (UInt32 x = 0; x < theURLlist.size(); x++) { if ( 0 != ioIPAddrArray[x]) addrCount++ ; } if (addrCount == 0) { printf("No valid destinations\n"); exit (-1); } printf("Done checking for 'streamingloadtool.mov' on all servers -- %i valid URL's\n", addrCount); } void DoDNSLookup(SVector &theURLlist, SVector &ioIPAddrs) { Assert(theURLlist.size() == ioIPAddrs.size()); enum { eDNSNameSize = 128 }; char theDNSName[eDNSNameSize + 1]; for (UInt32 x = 0; x < theURLlist.size(); x++) { // First extract the DNS name from this URL as a c-string StrPtrLen theURL = StrPtrLen(theURLlist[x]); StringParser theURLParser(&theURL); StrPtrLen theDNSNamePtr; theURLParser.ConsumeLength(NULL, 7); // skip over rtsp:// theURLParser.ConsumeUntil(&theDNSNamePtr, '/'); // grab the DNS name StringParser theDNSParser(&theDNSNamePtr); theDNSParser.ConsumeUntil(&theDNSNamePtr, ':'); // strip off the port number if any if (theDNSNamePtr.Len > eDNSNameSize) { theDNSNamePtr.PrintStr("DSN Name Failed Lookup.\n", "\n"); printf("The DNS name is %"_U32BITARG_" in length and is longer than the allowed %d.\n",theDNSNamePtr.Len, eDNSNameSize); return; } theDNSName[0] = 0; ::memcpy(theDNSName, theDNSNamePtr.Ptr, theDNSNamePtr.Len); theDNSName[theDNSNamePtr.Len] = 0; ioIPAddrs[x] = 0; // Now pass that DNS name into gethostbyname. struct hostent* theHostent = ::gethostbyname(theDNSName); if (theHostent != NULL) ioIPAddrs[x] = ntohl(*(UInt32*)(theHostent->h_addr_list[0])); else ioIPAddrs[x] = SocketUtils::ConvertStringToAddr(theDNSName); if (ioIPAddrs[x] == 0) { printf("Couldn't look up host name: %s.\n", theDNSName); //exit(-1); } } } char* GetClientTypeDescription(ClientSession::ClientType inClientType) { static char* kUDPString = "RTSP/UDP client"; static char* kTCPString = "RTSP/TCP client"; static char* kHTTPString = "RTSP/HTTP client"; static char* kHTTPDropPostString = "RTSP/HTTP drop post client"; static char* kReliableUDPString = "RTSP/ReliableUDP client"; switch (inClientType) { case ClientSession::kRTSPUDPClientType: return kUDPString; case ClientSession::kRTSPTCPClientType: return kTCPString; case ClientSession::kRTSPHTTPClientType: return kHTTPString; case ClientSession::kRTSPHTTPDropPostClientType: return kHTTPDropPostString; case ClientSession::kRTSPReliableUDPClientType: return kReliableUDPString; } Assert(0); return NULL; } char* GetDeathReasonDescription(UInt32 inDeathReason) { static char* kDiedNormallyString = "Completed normally"; static char* kTeardownFailedString = "Failure: Couldn't complete TEARDOWN"; static char* kRequestFailedString = "Failure: Failed RTSP request"; static char* kBadSDPString = "Failure: misformatted SDP"; static char* kSessionTimedoutString = "Failure: Couldn't connect to server(timeout)"; static char* kConnectionFailedString = "Failure: Server refused connection"; static char* kDiedWhilePlayingString = "Failure: Disconnected while playing"; switch (inDeathReason) { case ClientSession::kDiedNormally: return kDiedNormallyString; case ClientSession::kTeardownFailed: return kTeardownFailedString; case ClientSession::kRequestFailed: return kRequestFailedString; case ClientSession::kBadSDP: return kBadSDPString; case ClientSession::kSessionTimedout: return kSessionTimedoutString; case ClientSession::kConnectionFailed: return kConnectionFailedString; case ClientSession::kDiedWhilePlaying: return kDiedWhilePlayingString; } Assert(0); return NULL; } char* GetPayloadDescription(QTSS_RTPPayloadType inPayload) { static char* kSound = "Sound"; static char* kVideo = "Video"; static char* kUnknown = "Unknown"; switch (inPayload) { case qtssVideoPayloadType: return kVideo; case qtssAudioPayloadType: return kSound; default: return kUnknown; } return NULL; } void RecordClientInfoBeforeDeath(ClientSession* inSession) { if (inSession->GetReasonForDying() == ClientSession::kRequestFailed) sConnectionsThatErrored++; else if (inSession->GetReasonForDying() != ClientSession::kDiedNormally) sFailedConnections++; else sSuccessfulConnections++; { UInt32 theReason = inSession->GetReasonForDying(); in_addr theAddr; theAddr.s_addr = htonl(inSession->GetSocket()->GetHostAddr()); char* theAddrStr = ::inet_ntoa(theAddr); // // Write a log entry for this client if (sLog != NULL) ::fprintf(sLog, "Client complete. IP addr = %s, URL = %s. Connection status: %s. ", theAddrStr, inSession->GetClient()->GetURL()->Ptr, ::GetDeathReasonDescription(theReason)); if (theReason == ClientSession::kRequestFailed) if (sLog != NULL) ::fprintf(sLog, "Failed request status: %"_U32BITARG_"", inSession->GetRequestStatus()); if (sLog != NULL) ::fprintf(sLog, "\n"); // // If this was a successful connection, log statistics for this connection if ((theReason == ClientSession::kDiedNormally) || (theReason == ClientSession::kTeardownFailed) || (theReason == ClientSession::kSessionTimedout)) { UInt32 bytesReceived = 0; for (UInt32 trackCount = 0; trackCount < inSession->GetSDPInfo()->GetNumStreams(); trackCount++) { if (sLog != NULL) ::fprintf(sLog, "Track type: %s. Total packets received: %"_U32BITARG_ ". Total out of order packets: %"_U32BITARG_ ". Total out of bound packets: %"_U32BITARG_ ". Total ACKs sent: %"_U32BITARG_ ". Total malformed packets: %"_U32BITARG_, ::GetPayloadDescription(inSession->GetTrackType(trackCount)), inSession->GetNumPacketsReceived(trackCount), inSession->GetNumPacketsOutOfOrder(trackCount), inSession->GetNumOutOfBoundPackets(trackCount), inSession->GetNumAcks(trackCount), inSession->GetNumMalformedPackets(trackCount) ); if (sEnable3GPP) { if (sLog != NULL) ::fprintf(sLog, ". Total 3g packets lost: %"_U32BITARG_ ". Total 3g duplicate packets: %"_U32BITARG_ ". Total 3g late packets: %"_U32BITARG_ ". Total 3g rate adapt buffer-overflowed packets: %"_U32BITARG_, inSession->Get3gNumPacketsLost(trackCount), inSession->Get3gNumDuplicates(trackCount), inSession->Get3gNumLatePackets(trackCount), inSession->Get3gNumBufferOverflowedPackets(trackCount) ); } if (sLog != NULL) ::fprintf(sLog,".\n"); bytesReceived += inSession->GetNumBytesReceived(trackCount); bytesReceived += inSession->GetNumPacketsReceived(trackCount) * PACKETADDSIZE; sTotalBytesReceived += inSession->GetNumBytesReceived(trackCount); sTotalPacketsReceived += inSession->GetNumPacketsReceived(trackCount); sTotalOutOfOrder += inSession->GetNumPacketsOutOfOrder(trackCount); sTotalOutOfBound += inSession->GetNumOutOfBoundPackets(trackCount); sTotalNumAcks += inSession->GetNumAcks(trackCount); sTotalMalformed += inSession->GetNumMalformedPackets(trackCount); sTotalPacketsLost += inSession->Get3gNumPacketsLost(trackCount); sTotalDuplicates += inSession->Get3gNumDuplicates(trackCount); sTotalLatePackets += inSession->Get3gNumLatePackets(trackCount); sTotalBufferOverflowedPackets += inSession->Get3gNumBufferOverflowedPackets(trackCount); } UInt32 duration = (UInt32)(inSession->GetTotalPlayTimeInMsec() / 1000); Float32 bitRate = (((Float32)bytesReceived) / ((Float32)duration) * 8) / 1024; if (sLog != NULL) ::fprintf(sLog, "Play duration in sec: %"_U32BITARG_". Total stream bit rate in Kbits / sec: %f.\n", duration, bitRate); } if (sLog != NULL) ::fprintf(sLog, "\n"); } }