Darwin-Streaming-Server/StreamingLoadTool/StreamingLoadTool.cpp

1201 lines
42 KiB
C++
Raw Normal View History

/*
*
* @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 <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include<string.h>
#ifndef kVersionString
#include "../revision.h"
#endif
#ifndef __Win32__
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#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<char *> &theURLlist, SVector<UInt32> &ioIPAddrs);
void RecordClientInfoBeforeDeath(ClientSession* inSession);
char* GetDeathReasonDescription(UInt32 inDeathReason);
char* GetPayloadDescription(QTSS_RTPPayloadType inPayload);
void CheckForStreamingLoadToolDotMov(SVector<UInt32> &ioIPAddrArray, SVector<char *> &theURLlist, UInt16 inPort, SVector<char *> &userList, SVector<char *> &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);
SVector<char *>userList;
SVector<char *>passwordList;
SVector<char *> 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<UInt32> 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<UInt32>(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<UInt32>(kUInt32_Max) : static_cast<UInt32>(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<UInt32> &ioIPAddrArray, SVector<char *> &theURLlist, UInt16 inPort, SVector<char *> &userList, SVector<char *> &passwordList, UInt32 verboseLevel)
{
Assert(ioIPAddrArray.size() == theURLlist.size());
printf("Checking for 'streamingloadtool.mov' on the target servers\n");
OSArrayObjectDeleter<UInt32> 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<char *> &theURLlist, SVector<UInt32> &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");
}
}