+#endif
+
+#ifndef kVersionString
+#include "../../revision.h"
+#endif
+
+#if DEBUG
+#define REFLECTOR_MODULE_DEBUGGING 0
+#else
+#define REFLECTOR_MODULE_DEBUGGING 0
+#endif
+
+// STATIC DATA
+
+static char* sRelayPrefs = NULL;
+static char* sRelayStatsURL = NULL;
+static StrPtrLen sRequestHeader;
+static QTSS_ModulePrefsObject sPrefs = NULL;
+static QTSS_ServerObject sServer = NULL;
+static QTSS_Object sAttributes = NULL;
+static Bool16 sSkipAuthorization = true;
+static Bool16 sIsRelaySession = true;
+static char* sIsRelaySessionAttrName = "QTSSRelayModuleIsRelaySession";
+static QTSS_AttributeID sIsRelaySessionAttr = qtssIllegalAttrID;
+
+static int sRelayPrefModDate = -1;
+
+// ATTRIBUTES
+
+static QTSS_AttributeID sRelayModulePrefParseErr = qtssIllegalAttrID;
+
+static char* sDefaultRelayPrefs = DEFAULTPATHS_ETC_DIR "relayconfig.xml";
+
+#ifdef __MacOSX__
+#define kResponseHeader "HTTP/1.0 200 OK\r\nServer: QuickTimeStreamingServer/%s/%s\r\nConnection: Close\r\nContent-Type: text/html\r\n\r\nRelay Stats"
+#else
+#define kResponseHeader "HTTP/1.0 200 OK\r\nServer: DarwinStreamingServer/%s/%s\r\nConnection: Close\r\nContent-Type: text/html\r\n\r\nRelay Stats"
+#endif
+
+static char sResponseHeader[1024];
+
+static OSQueue* sSessionQueue = NULL;
+static OSQueue* sAnnouncedQueue = NULL;
+static OSQueue* sRTSPSourceInfoQueue = NULL;
+static OSQueue* sRTSPSessionIDQueue = NULL;
+
+static XMLParser* sRelayPrefsFile = NULL;
+static OSMutex sResolverMutex;
+class DNSResolverThread;
+DNSResolverThread* sResolverThread = NULL;
+Bool16 sDoResolveAgain = false;
+
+// This struct is used when Rereading Relay Prefs
+struct SourceInfoQueueElem
+{
+ SourceInfoQueueElem(SourceInfo* inInfo, Bool16 inRTSPInfo) :fElem(), fSourceInfo(inInfo),
+ fIsRTSPSourceInfo(inRTSPInfo),
+ fShouldDelete(true) { fElem.SetEnclosingObject(this); }
+ ~SourceInfoQueueElem() {}
+
+ OSQueueElem fElem;
+ SourceInfo* fSourceInfo;
+ Bool16 fIsRTSPSourceInfo;
+ Bool16 fShouldDelete;
+};
+
+// This struct is used when setting up announced source relays
+struct RTSPSessionIDQueueElem
+{
+ RTSPSessionIDQueueElem(UInt32 rtspSessionID) :fElem(), fRTSPSessionID(rtspSessionID) { fElem.SetEnclosingObject(this); }
+ ~RTSPSessionIDQueueElem() {}
+
+ OSQueueElem fElem;
+ UInt32 fRTSPSessionID;
+};
+
+
+// FUNCTION PROTOTYPES
+
+static QTSS_Error QTSSRelayModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
+static QTSS_Error Register(QTSS_Register_Params* inParams);
+static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
+static QTSS_Error Shutdown();
+static QTSS_Error RereadPrefs();
+
+static RelaySession* CreateSession(SourceInfoQueueElem* inElem);
+static RelaySession* FindSession(SourceInfoQueueElem* inElem);
+static RelaySession* FindNextSession(SourceInfoQueueElem* inElem, OSQueueIter* inIterPtr);
+
+static void AddOutputs(SourceInfo* inInfo, RelaySession* inSession, Bool16 inIsRTSPSourceInfo);
+static void RemoveOutput(ReflectorOutput* inOutput, RelaySession* inSession);
+
+static QTSS_Error Filter(QTSS_StandardRTSP_Params* inParams);
+static void FindRelaySessions(OSQueue* inSessionQueue);
+
+static QTSS_Error SetupAnnouncedSessions(QTSS_StandardRTSP_Params* inParams);
+static RTSPSessionIDQueueElem* FindRTSPSessionIDQueueElem(UInt32 inSessionID);
+static RTSPSourceInfo* FindAnnouncedSourceInfo(UInt32 inIP, StrPtrLen& inURL);
+static RTSPSourceInfo* FindExistingRelayInfo(UInt32 inIP, StrPtrLen& inURL);
+
+static void RereadRelayPrefs(XMLParser* prefsParser);
+static void FindSourceInfos(OSQueue* inSessionQueue, OSQueue* inAnnouncedQueue, XMLParser* prefsParser);
+static void ClearSourceInfos(OSQueue* inQueue);
+
+static QTSS_Error RouteAuthorization(QTSS_StandardRTSP_Params* inParams);
+static Bool16 IsRelayRequest(UInt16 inPort);
+
+static void ReadRelayPrefsFile();
+static Bool16 CheckDNSNames(XMLParser* prefsFile, Bool16 doResolution);
+static void ResolveDNSAddr(XMLTag* tag);
+
+
+// DNS Resolution can block a thread for a long time (there's no async version), and on X
+// we only have 1 task thread, so the resolution must be on another thread. However, we
+// want to do the rest of the RereadPrefs on the main task thread, so we need both a thread
+// and a task. The thread resolves the addresses and fires the task, the task deletes
+// the thread and is deleted itself as soon as it's done.
+
+class DNSResolverThread : public OSThread
+{
+ class RereadPrefsTask : public Task
+ {
+
+
+ public:
+ RereadPrefsTask() : Task () {this->SetTaskName("DNSResolverThread::RereadPrefsTask");}
+ virtual SInt64 Run()
+ {
+ RereadRelayPrefs(sRelayPrefsFile);
+ delete sResolverThread;
+ sResolverThread = NULL;
+ delete sRelayPrefsFile;
+
+ // we need to see if reread prefs has been called again while we were resolving.
+ // If so, it just exited without doing anything, and we need to start over to pick
+ // up whatever changed.
+ sResolverMutex.Lock();
+ sRelayPrefsFile = NULL;
+ if (sDoResolveAgain)
+ {
+ sDoResolveAgain = false;
+ sResolverMutex.Unlock();
+ ReadRelayPrefsFile();
+ }
+ else sResolverMutex.Unlock();
+
+ return -1;
+ }
+ };
+
+public:
+ static void ResolveRelayPrefs(XMLParser* relayPrefs)
+ {
+ sResolverMutex.Lock();
+
+ if (sRelayPrefsFile == NULL)
+ {
+ sRelayPrefsFile = relayPrefs;
+ sResolverThread = new DNSResolverThread();
+ sResolverThread->Start();
+ }
+ else
+ {
+ sDoResolveAgain = true; // it's already resolving, tell it to try again when it's done
+ delete relayPrefs;
+ }
+
+ sResolverMutex.Unlock();
+ }
+
+ virtual void Entry()
+ {
+ if (sRelayPrefsFile != NULL)
+ {
+ CheckDNSNames(sRelayPrefsFile, true);
+ RereadPrefsTask* tempTask = new RereadPrefsTask();
+ tempTask->Signal(Task::kIdleEvent);
+ }
+ }
+};
+
+// FUNCTION IMPLEMENTATIONS
+
+QTSS_Error QTSSRelayModule_Main(void* inPrivateArgs)
+{
+ return _stublibrary_main(inPrivateArgs, QTSSRelayModuleDispatch);
+}
+
+
+QTSS_Error QTSSRelayModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
+{
+ switch (inRole)
+ {
+ case QTSS_Register_Role:
+ return Register(&inParams->regParams);
+ case QTSS_Initialize_Role:
+ return Initialize(&inParams->initParams);
+ case QTSS_RereadPrefs_Role:
+
+ return RereadPrefs();
+ case QTSS_RTSPFilter_Role:
+ return Filter(&inParams->rtspRequestParams);
+ case QTSS_RTSPRoute_Role:
+ return RouteAuthorization(&inParams->rtspRouteParams);
+ case QTSS_RTSPPostProcessor_Role:
+ return SetupAnnouncedSessions(&inParams->rtspPostProcessorParams);
+ case QTSS_Shutdown_Role:
+ return Shutdown();
+ }
+ return QTSS_NoErr;
+}
+
+
+QTSS_Error Register(QTSS_Register_Params* inParams)
+{
+ // Do role & attribute setup
+ (void)QTSS_AddRole(QTSS_Initialize_Role);
+ (void)QTSS_AddRole(QTSS_Shutdown_Role);
+ (void)QTSS_AddRole(QTSS_RTSPFilter_Role);
+ (void)QTSS_AddRole(QTSS_RTSPRoute_Role);
+ (void)QTSS_AddRole(QTSS_RereadPrefs_Role);
+ (void)QTSS_AddRole(QTSS_RTSPPostProcessor_Role);
+
+ // get the text for the error message from the server atrribute.
+ static char* sRelayModulePrefParseErrName = "QTSSRelayModulePrefParseError";
+ (void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sRelayModulePrefParseErrName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssTextMessagesObjectType, sRelayModulePrefParseErrName, &sRelayModulePrefParseErr);
+
+ // add a boolean attribute for the relay session to the client session object
+ // we set it to true if we know that it is a relay session else we set it to false
+ // (set to true for those client sessions that are created for the relay when announces come in)
+ (void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sIsRelaySessionAttrName, NULL, qtssAttrDataTypeBool16);
+ (void)QTSS_IDForAttr(qtssClientSessionObjectType, sIsRelaySessionAttrName, &sIsRelaySessionAttr);
+
+ RelaySession::Register();
+
+ RelayOutput::Register();
+ // Reflector session needs to setup some parameters too.
+ ReflectorStream::Register();
+
+ // Tell the server our name!
+ static char* sModuleName = "QTSSRelayModule";
+ ::strcpy(inParams->outModuleName, sModuleName);
+
+ return QTSS_NoErr;
+}
+
+
+QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
+{
+ // Setup module utils
+ QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
+ sSessionQueue = NEW OSQueue();
+ sAnnouncedQueue = NEW OSQueue();
+ sRTSPSourceInfoQueue = NEW OSQueue();
+ sRTSPSessionIDQueue = NEW OSQueue();
+ sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
+ sServer = inParams->inServer;
+ sAttributes = QTSSModuleUtils::GetModuleAttributesObject(inParams->inModule);
+
+ qtss_sprintf(sResponseHeader, kResponseHeader, kVersionString, kBuildString);
+
+ // Call helper class initializers
+ // this function calls the ReflectorSession base class Initialize function
+ RelaySession::Initialize(sAttributes);
+
+#if QTSS_RELAY_EXTERNAL_MODULE
+ // The reflector is dependent on a number of objects in the Common Utilities
+ // library that get setup by the server if the reflector is internal to the
+ // server.
+ //
+ // So, if the reflector is being built as a code fragment, it must initialize
+ // those pieces itself
+#if !MACOSXEVENTQUEUE
+ ::select_startevents();//initialize the select() implementation of the event queue
+#endif
+ OS::Initialize();
+ Socket::Initialize();
+ SocketUtils::Initialize();
+
+ const UInt32 kNumReflectorThreads = 8;
+ TaskThreadPool::AddThreads(kNumReflectorThreads);
+ IdleTask::Initialize();
+ Socket::StartThread();
+#endif
+
+ //
+ // Instead of passing our own module prefs object, as one might expect,
+ // here we pass in the QTSSReflectorModule's, because the prefs that
+ // apply to ReflectorStream are stored in that module's prefs
+ StrPtrLen theReflectorModule("QTSSReflectorModule");
+ QTSS_ModulePrefsObject theReflectorPrefs =
+ QTSSModuleUtils::GetModulePrefsObject(QTSSModuleUtils::GetModuleObjectByName(theReflectorModule));
+
+ // Call helper class initializers
+ ReflectorStream::Initialize(theReflectorPrefs);
+ RereadPrefs();
+
+ return QTSS_NoErr;
+}
+
+QTSS_Error Shutdown()
+{
+#if QTSS_REFLECTOR_EXTERNAL_MODULE
+ TaskThreadPool::RemoveThreads();
+#endif
+ return QTSS_NoErr;
+}
+
+QTSS_Error RereadPrefs()
+{
+ delete [] sRelayPrefs;
+ delete [] sRelayStatsURL;
+
+ sRelayPrefs = QTSSModuleUtils::GetStringAttribute(sPrefs, "relay_prefs_file", sDefaultRelayPrefs);
+ sRelayStatsURL = QTSSModuleUtils::GetStringAttribute(sPrefs, "relay_stats_url", "");
+
+ ReadRelayPrefsFile();
+ return QTSS_NoErr;
+}
+
+RelaySession* CreateSession(SourceInfoQueueElem* inElem)
+{
+ RelaySession* theSession = NEW RelaySession(NULL, inElem->fSourceInfo);
+ QTSS_Error theErr = theSession->SetupRelaySession(inElem->fSourceInfo);
+ inElem->fShouldDelete = false; // SourceInfo will be deleted by the RelaySession
+ if (theErr != QTSS_NoErr)
+ {
+ delete theSession;
+ return NULL;
+ }
+
+ theSession->FormatHTML(NULL);
+
+ sSessionQueue->EnQueue(theSession->GetQueueElem());
+ return theSession;
+}
+
+RelaySession* FindSession(SourceInfoQueueElem* inElem)
+{
+ OSQueueIter theIter(sSessionQueue);
+ return FindNextSession(inElem, &theIter);
+}
+
+RelaySession* FindNextSession(SourceInfoQueueElem* inElem, OSQueueIter* inIterPtr)
+{
+ RelaySession* theSession = NULL;
+
+ for ( ;!inIterPtr->IsDone(); inIterPtr->Next())
+ {
+ theSession = (RelaySession*)inIterPtr->GetCurrent()->GetEnclosingObject();
+ if (theSession->Equal(inElem->fSourceInfo))
+ return theSession;
+ }
+
+ return NULL;
+}
+
+void RemoveOutput(ReflectorOutput* inOutput, RelaySession* inSession)
+{
+ // This function removes the output from the RelaySession, then
+ // checks to see if the session should go away. If it should, this deletes it
+ inSession->RemoveOutput(inOutput,false);
+ delete inOutput;
+
+ if (inSession->GetNumOutputs() == 0)
+ {
+ sSessionQueue->Remove(inSession->GetQueueElem());
+ delete inSession;
+ }
+}
+
+void RemoveAllOutputs(RelaySession* inSession)
+{
+ OSMutexLocker locker(RelayOutput::GetQueueMutex());
+ for (OSQueueIter iter(RelayOutput::GetOutputQueue()); !iter.IsDone(); )
+ {
+ RelayOutput* theOutput = (RelayOutput*)iter.GetCurrent()->GetEnclosingObject();
+
+ iter.Next();
+ if(theOutput->GetRelaySession() == inSession)
+ {
+ inSession->RemoveOutput(theOutput,false);
+ delete theOutput;
+ }
+ }
+
+ Assert(inSession->GetNumOutputs() == 0);
+ sSessionQueue->Remove(inSession->GetQueueElem());
+ delete inSession;
+}
+
+QTSS_Error Filter(QTSS_StandardRTSP_Params* inParams)
+{
+ UInt32 theLen = 0;
+ char* theFullRequest = NULL;
+ (void)QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest, &theLen);
+
+ OSMutexLocker locker(RelayOutput::GetQueueMutex());
+
+ // Check to see if this is a request we should handle
+ if ((sRequestHeader.Ptr == NULL) || (sRequestHeader.Len == 0))
+ return QTSS_NoErr;
+ if ((theFullRequest == NULL) || (theLen < sRequestHeader.Len))
+ return QTSS_NoErr;
+ if (::memcmp(theFullRequest, sRequestHeader.Ptr, sRequestHeader.Len) != 0)
+ return QTSS_NoErr;
+
+ // Keep-alive should be off!
+ Bool16 theFalse = false;
+ (void)QTSS_SetValue(inParams->inRTSPRequest, qtssRTSPReqRespKeepAlive, 0, &theFalse, sizeof(theFalse));
+
+ (void)QTSS_Write(inParams->inRTSPRequest, sResponseHeader, ::strlen(sResponseHeader), NULL, 0);
+
+ // First build up a queue of all RelaySessions with RelayOutputs. This way,
+ // we can present a list that's sorted.
+ OSQueue theSessionQueue;
+ FindRelaySessions(&theSessionQueue);
+
+ static StrPtrLen sNoRelays("There are no currently active relays
");
+ if (theSessionQueue.GetLength() == 0)
+ (void)QTSS_Write(inParams->inRTSPRequest, sNoRelays.Ptr, sNoRelays.Len, NULL, 0);
+
+ // Now go through our RelaySessionQueue, writing the info for each RelaySession,
+ // and all the outputs associated with that session
+ for (OSQueueIter iter(&theSessionQueue); !iter.IsDone(); iter.Next())
+ {
+ RelaySession* theSession = (RelaySession*)iter.GetCurrent()->GetEnclosingObject();
+ (void)QTSS_Write(inParams->inRTSPRequest, theSession->GetSourceInfoHTML()->Ptr, theSession->GetSourceInfoHTML()->Len, NULL, 0);
+
+ for (OSQueueIter iter2(RelayOutput::GetOutputQueue()); !iter2.IsDone(); iter2.Next())
+ {
+ RelayOutput* theOutput = (RelayOutput*)iter2.GetCurrent()->GetEnclosingObject();
+ if (theSession == theOutput->GetRelaySession())
+ {
+ (void)QTSS_Write(inParams->inRTSPRequest, theOutput->GetOutputInfoHTML()->Ptr, theOutput->GetOutputInfoHTML()->Len, NULL, 0);
+
+ // Write current stats for this output
+ char theStatsBuf[1024];
+ qtss_sprintf(theStatsBuf, "Current stats for this relay: %"_U32BITARG_" packets per second. %"_U32BITARG_" bits per second. %"_64BITARG_"d packets since it started. %"_64BITARG_"d bits since it started", theOutput->GetCurPacketsPerSecond(), theOutput->GetCurBitsPerSecond(), theOutput->GetTotalPacketsSent(), theOutput->GetTotalBytesSent());
+ (void)QTSS_Write(inParams->inRTSPRequest, &theStatsBuf[0], ::strlen(theStatsBuf), NULL, 0);
+ }
+ }
+ }
+ static StrPtrLen sResponseEnd("");
+ (void)QTSS_Write(inParams->inRTSPRequest, sResponseEnd.Ptr, sResponseEnd.Len, NULL, 0);
+
+ for (OSQueueIter iter3(&theSessionQueue); !iter3.IsDone(); )
+ {
+ // Cleanup the memory we had allocated in FindRelaySessions
+ OSQueueElem* theElem = iter3.GetCurrent();
+ iter3.Next();
+ theSessionQueue.Remove(theElem);
+ delete theElem;
+ }
+ return QTSS_NoErr;
+}
+
+void FindRelaySessions(OSQueue* inSessionQueue)
+{
+ for (OSQueueIter theIter(RelayOutput::GetOutputQueue()); !theIter.IsDone(); theIter.Next())
+ {
+ RelayOutput* theOutput = (RelayOutput*)theIter.GetCurrent()->GetEnclosingObject();
+ Bool16 found = false;
+
+ // Check to see if we've already seen this RelaySession
+ for (OSQueueIter theIter2(inSessionQueue); !theIter2.IsDone(); theIter2.Next())
+ {
+ if (theOutput->GetRelaySession() == theIter2.GetCurrent()->GetEnclosingObject())
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ // We haven't seen this one yet, so put it on the queue.
+ OSQueueElem* theElem = NEW OSQueueElem(theOutput->GetRelaySession());
+ inSessionQueue->EnQueue(theElem);
+ }
+ }
+}
+
+QTSS_Error SetupAnnouncedSessions(QTSS_StandardRTSP_Params* inParams)
+{
+
+ QTSS_Error theErr = QTSS_NoErr;
+ Bool16 setup = false;
+ Bool16 play = false;
+
+ // Get the RTSP Method
+ QTSS_RTSPMethod* theMethod = NULL;
+ UInt32 theLen = 0;
+ theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqMethod, 0, (void**)&theMethod, &theLen);
+ if ((theErr != QTSS_NoErr) || (theLen != sizeof(QTSS_RTSPMethod)))
+ {
+ Assert(0);
+ return QTSS_NoErr;
+ }
+
+ // Get the SETUP Transport Mode
+ QTSS_RTPTransportMode* theMode = NULL;
+ theLen = 0;
+ theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqTransportMode, 0, (void**)&theMode, &theLen);
+ if ((theErr != QTSS_NoErr) || (theLen != sizeof(QTSS_RTPTransportMode)))
+ {
+ Assert(0);
+ return QTSS_NoErr;
+ }
+
+ if ( (*theMethod == qtssSetupMethod) && (*theMode == qtssRTPTransportModeRecord) )
+ setup = true;
+ else if (*theMethod == qtssPlayMethod || *theMethod == qtssRecordMethod)
+ play = true;
+
+ // We are only interested in SETUP mode=receive or mode=record and PLAY or RECORD requests
+ if (!(setup || play))
+ return QTSS_NoErr;
+
+ // Get the RTSP Status Code
+ QTSS_RTSPStatusCode* theStatusCode = NULL;
+ theLen = 0;
+ theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqStatusCode, 0, (void**)&theStatusCode, &theLen);
+ if ((theErr != QTSS_NoErr) || (theLen != sizeof(QTSS_RTSPStatusCode)))
+ {
+ Assert(0);
+ return QTSS_NoErr;
+ }
+
+ // We are only interested in 200 OK requests, ofcourse
+ if(*theStatusCode != qtssSuccessOK)
+ return QTSS_NoErr;
+
+ // Now get the RTSP Session ID
+ UInt32* theSessionID = 0;
+ theLen = 0;
+ theErr = QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesID, 0, (void**)&theSessionID, &theLen);
+ if ((theErr != QTSS_NoErr) || (theLen != sizeof(UInt32)))
+ {
+ Assert(0);
+ return QTSS_NoErr;
+ }
+
+ OSMutexLocker locker(RelayOutput::GetQueueMutex());
+
+ RTSPSessionIDQueueElem* theElem = FindRTSPSessionIDQueueElem(*theSessionID);
+
+ // If the RTSPSessionID is not in the queue
+ if (theElem == NULL)
+ {
+ // If it's a SETUP mode=receive or record add the RTSPSessionID to the queue
+ if(setup)
+ {
+ RTSPSessionIDQueueElem* idElem = NEW RTSPSessionIDQueueElem(*theSessionID);
+ sRTSPSessionIDQueue->EnQueue(&idElem->fElem);
+ }
+
+ // Nothing more to do
+ return QTSS_NoErr;
+ }
+
+ // If theElem is not NULL
+ if(setup) // No need to add it to the queue twice
+ return QTSS_NoErr;
+
+ // At this point it has to be a PLAY request with the session ID in the queue
+
+ // Remove the session ID from the queue
+ sRTSPSessionIDQueue->Remove(&theElem->fElem);
+ delete theElem;
+ theElem = 0;
+
+ // Get the Remote IP address
+ UInt32* theIP = 0;
+ theLen = 0;
+ theErr = QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesRemoteAddr, 0, (void**)&theIP, &theLen);
+ if ((theErr != QTSS_NoErr) || (theLen != sizeof(UInt32)))
+ {
+ Assert(0);
+ return QTSS_NoErr;
+ }
+
+ // Get the URI
+ theLen = 0;
+ char* theURLStr = NULL;
+ theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqURI, 0, (void**)&theURLStr, &theLen);
+ if ((theErr != QTSS_NoErr) || (theURLStr == NULL))
+ {
+ Assert(0);
+ return QTSS_NoErr;
+ }
+
+ StrPtrLen theURL(theURLStr, theLen);
+
+ // Check if a source info exists for this ANNOUNCED URL
+ RTSPSourceInfo* info = FindAnnouncedSourceInfo(*theIP, theURL);
+ if(info == NULL) // if a source info does not exists, we don't have to do anything
+ return QTSS_NoErr;
+
+ // Check if a relay has already been set up for this
+ // if it has, remove all the outputs, delete the session
+ // before creating a new source info etc...
+ RTSPSourceInfo* existingInfo = FindExistingRelayInfo(*theIP, theURL);
+ if(existingInfo != NULL)
+ {
+ RelaySession* session = existingInfo->GetRelaySession();
+ if(session == NULL)
+ delete existingInfo;
+ else
+ RemoveAllOutputs(session);
+ }
+
+ RTSPSourceInfo* newInfo = NEW RTSPSourceInfo(*info);
+ newInfo->SetAnnounceActualIP(*theIP); // this is used later along with the source url to check if a relay is already set up
+
+ UInt16 thePort = 0;
+ theLen = sizeof(UInt16);
+ theErr = QTSS_GetValue(sServer, qtssSvrRTSPPorts, 0, (void *) &thePort, &theLen);
+
+ if ((theErr != QTSS_NoErr) || (theLen != sizeof(UInt16)))
+ {
+ delete newInfo;
+ return QTSS_NoErr;
+ }
+
+ newInfo->SetSourceParameters(INADDR_LOOPBACK, thePort, theURL);
+ newInfo->StartSessionCreatorTask(sSessionQueue, sRTSPSourceInfoQueue);
+
+ return QTSS_NoErr;
+}
+
+RTSPSessionIDQueueElem* FindRTSPSessionIDQueueElem(UInt32 inSessionID)
+{
+ for (OSQueueIter iter(sRTSPSessionIDQueue); !iter.IsDone(); iter.Next())
+ {
+ RTSPSessionIDQueueElem* theElem = (RTSPSessionIDQueueElem*)iter.GetCurrent()->GetEnclosingObject();
+
+ if (theElem->fRTSPSessionID == inSessionID)
+ return theElem;
+ }
+
+ return NULL;
+}
+
+RTSPSourceInfo* FindAnnouncedSourceInfo(UInt32 inIP, StrPtrLen& inURL)
+{
+ RTSPSourceInfo* info = NULL;
+ Bool16 ipMatchfound = false;
+
+ // check to see if the ip address + URL match any of the source infos in the sAnnouncedQueue.
+ // in this order:
+ // 1. IP + URL match
+ // 2. IP match
+ // 3. All IPs+URLs match (meaning all announced broadcasts)
+
+ for (OSQueueIter iter(sAnnouncedQueue); !iter.IsDone(); iter.Next())
+ {
+ SourceInfo* theInfo = ((SourceInfoQueueElem*)iter.GetCurrent()->GetEnclosingObject())->fSourceInfo;
+
+// RTSPSourceInfo* announceInfo = dynamic_cast(theInfo);
+ RTSPSourceInfo* announceInfo = (RTSPSourceInfo *)(theInfo);
+// if ((announceInfo == NULL) || (!announceInfo->IsAnnounce()))
+// continue;
+
+ UInt32 infoIP = announceInfo->GetAnnounceIP();
+ StrPtrLen infoURL(announceInfo->GetAnnounceURL());
+
+ // if there is a source info for ANNOUNCE ALL, keep this info until a better one is found
+ if (!ipMatchfound && (infoIP == 0) && ((infoURL.Ptr == NULL) || (infoURL.Len == 0)))
+ {
+ info = announceInfo;
+ continue;
+ }
+
+ // if there is a source info for ANNOUNCE any URL from an IP, keep this info until a better one is found
+ if ((infoIP == inIP) && ((infoURL.Ptr == NULL) || (infoURL.Len == 0)))
+ {
+ info = announceInfo;
+ ipMatchfound = true;
+ continue;
+ }
+
+ // if there is a source info for ANNOUNCE for the URL + IP combo, return this as the source info
+ // strip leading path delimiters
+ UInt32 count = 0;
+ while (count < inURL.Len && inURL.Ptr[count] == '/')
+ { count ++;
+ }
+ StrPtrLen announcedURL(&inURL.Ptr[count],inURL.Len - count);
+
+ // strip leading path delimiters
+ count = 0;
+ while (count < infoURL.Len && infoURL.Ptr[count] == '/')
+ { count ++;
+ }
+ StrPtrLen configMountPoint(&infoURL.Ptr[count],infoURL.Len - count);
+
+ if ((infoIP == inIP) && (configMountPoint.Equal(announcedURL)))
+ {
+ info = announceInfo;
+ break;
+ }
+ }
+
+ // cast the source info to RTSPSourceInfo because all the elements of the sAnnouncedQueue
+ // are RTSPSourceInfos anyway.
+ // Don't really like this but I suppose it will do for now
+ return info;
+}
+
+RTSPSourceInfo* FindExistingRelayInfo(UInt32 inIP, StrPtrLen& inURL)
+{
+ if (inURL.Ptr == NULL)
+ return NULL;
+
+ for (OSQueueIter iter(sRTSPSourceInfoQueue); !iter.IsDone(); iter.Next())
+ {
+ RTSPSourceInfo* info = (RTSPSourceInfo*)iter.GetCurrent()->GetEnclosingObject();
+
+ if ((inIP == info->GetAnnounceActualIP()) && inURL.Equal(info->GetSourceURL()))
+ return info;
+ }
+
+ return NULL;
+}
+
+void RereadRelayPrefs(XMLParser* prefsParser)
+{
+ // Construct a SourceInfo object for each relayable thingy
+ OSQueue sourceInfoQueue;
+ ClearSourceInfos(sAnnouncedQueue);
+ FindSourceInfos(&sourceInfoQueue, sAnnouncedQueue, prefsParser);
+
+ // Ok, we have all our SourceInfo objects. Now, lets alter the list of Relay
+ // outputs to match
+
+ // Go through the queue of Relay outputs, removing the source Infos that
+ // are already going
+ OSMutexLocker locker(RelayOutput::GetQueueMutex());
+
+ // We must reread the relay stats URL here, because we have the RelayOutput
+ // mutex so we know that Filter can't be filtering now
+ delete [] sRequestHeader.Ptr;
+ sRequestHeader.Ptr = NULL;
+ sRequestHeader.Len = 0;
+
+ if ((sRelayStatsURL != NULL) && (::strlen(sRelayStatsURL) > 0))
+ {
+ // sRequestHeader line should look like: GET /sRelayStatsURL HTTP
+ sRequestHeader.Ptr = NEW char[::strlen(sRelayStatsURL) + 15];
+ ::strcpy(sRequestHeader.Ptr, "GET /");
+ ::strcat(sRequestHeader.Ptr, sRelayStatsURL);
+ ::strcat(sRequestHeader.Ptr, " HTTP");
+ sRequestHeader.Len = ::strlen(sRequestHeader.Ptr);
+ }
+
+ for (OSQueueIter iter(RelayOutput::GetOutputQueue()); !iter.IsDone(); )
+ {
+ RelayOutput* theOutput = (RelayOutput*)iter.GetCurrent()->GetEnclosingObject();
+
+ Bool16 stillActive = false;
+
+ // Check to see if this RelayOutput matches one of the streams in the
+ // SourceInfo queue. If it does, this has 2 implications:
+ //
+ // 1) The stream in the SourceInfo that matches should NOT be setup as a
+ // new RelayOutput, because it already exists. (Equal will set a flag in
+ // the StreamInfo object saying as much)
+ //
+ // 2) This particular RelayOutput should remain (not be deleted).
+ for (OSQueueIter iter2(&sourceInfoQueue); !iter2.IsDone(); iter2.Next())
+ {
+ SourceInfo* theInfo =
+ ((SourceInfoQueueElem*)iter2.GetCurrent()->GetEnclosingObject())->fSourceInfo;
+ if (theOutput->Equal(theInfo))
+ {
+ stillActive = true;
+ break;
+ }
+ }
+
+ // Also check if this RelayOutput matches one of the streams in the
+ // AnnouncedInfo queue. If it does, the implications are same as above.
+ // Perform this check only if it is not already found in the first queue
+ if(!stillActive)
+ {
+ for (OSQueueIter iter3(sAnnouncedQueue); !iter3.IsDone(); iter3.Next())
+ {
+ SourceInfo* theInfo =
+ ((SourceInfoQueueElem*)iter3.GetCurrent()->GetEnclosingObject())->fSourceInfo;
+ if (theOutput->Equal(theInfo))
+ {
+ stillActive = true;
+ break;
+ }
+ }
+ }
+
+ iter.Next();
+
+ // This relay output is no longer on our list
+ if (!stillActive)
+ RemoveOutput(theOutput, theOutput->GetRelaySession());
+ }
+
+ // We've pruned the list of RelayOutputs of all outputs that are no longer
+ // going on. Now, all that is left to do is to create any new outputs (and sessions)
+ // that are just starting up
+
+ for (OSQueueIter iter4(&sourceInfoQueue); !iter4.IsDone(); iter4.Next())
+ {
+ SourceInfoQueueElem* theElem = (SourceInfoQueueElem*)iter4.GetCurrent()->GetEnclosingObject();
+ if (theElem->fSourceInfo->GetNumNewOutputs() == 0) // Because we've already checked for Outputs
+ continue; // That already exist, we know which ones are new
+
+ RelaySession* theSession = FindSession(theElem);
+
+ if (theSession == NULL)
+ {
+ if (theElem->fIsRTSPSourceInfo)
+ {
+ theElem->fShouldDelete = false;
+// RTSPSourceInfo* theInfo = dynamic_cast(theElem->fSourceInfo);
+ RTSPSourceInfo* theInfo = (RTSPSourceInfo *)(theElem->fSourceInfo);
+ theInfo->StartSessionCreatorTask(sSessionQueue, sRTSPSourceInfoQueue);
+ }
+ else
+ {
+ theSession = CreateSession(theElem);
+ if (theSession != NULL)
+ AddOutputs(theElem->fSourceInfo, theSession, theElem->fIsRTSPSourceInfo);
+ }
+ }
+ else
+ {
+ AddOutputs(theElem->fSourceInfo, theSession, theElem->fIsRTSPSourceInfo);
+ }
+ }
+
+ // For the announced source infos, find a session that is already active, and add any
+ // output that isn't already set up. If a session isn't found, don't create it!
+ for (OSQueueIter iter5(sAnnouncedQueue); !iter5.IsDone(); iter5.Next())
+ {
+ SourceInfoQueueElem* theElem = (SourceInfoQueueElem*)iter5.GetCurrent()->GetEnclosingObject();
+ if (theElem->fSourceInfo->GetNumNewOutputs() == 0) // Because we've already checked for Outputs
+ continue; // That already exist, we know which ones are new
+
+ for (OSQueueIter iter6(sSessionQueue); !iter6.IsDone(); )
+ {
+
+ RelaySession* theSession = FindNextSession(theElem, &iter6);
+ if(!iter6.IsDone())
+ iter6.Next();
+
+ if (theSession == NULL)
+ continue;
+
+ AddOutputs(theElem->fSourceInfo, theSession, theElem->fIsRTSPSourceInfo);
+ }
+ }
+
+ // Clear only the first source info queue. The announced source info queue
+ // must be kept around so that we know which announced broadcasts to relay
+ ClearSourceInfos(&sourceInfoQueue);
+}
+
+void AddOutputs(SourceInfo* inInfo, RelaySession* inSession, Bool16 inIsRTSPSourceInfo)
+{
+ for (UInt32 x = 0; x < inInfo->GetNumOutputs(); x++)
+ {
+ SourceInfo::OutputInfo* theOutputInfo = inInfo->GetOutputInfo(x);
+ if (theOutputInfo->fAlreadySetup)
+ continue;
+
+ RelayOutput* theOutput = NEW RelayOutput(inInfo, x, inSession, inIsRTSPSourceInfo);
+ if (theOutput->IsValid())
+ inSession->AddOutput(theOutput, false);
+ else
+ delete theOutput;
+ }
+}
+
+void FindSourceInfos(OSQueue* inSessionQueue, OSQueue* inAnnouncedQueue, XMLParser* prefsParser)
+{
+ SourceInfoQueueElem* theElem = NULL;
+
+ XMLTag* tag = prefsParser->GetRootTag();
+ XMLTag* relayTag;
+ UInt32 index = 0;
+ while ((relayTag = tag->GetEmbeddedTagByNameAndAttr("OBJECT", "TYPE", "relay", index)) != NULL)
+ {
+ index++;
+
+ XMLTag* enabledTag = relayTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "enabled");
+ if ((enabledTag != NULL) && (enabledTag->GetValue() != NULL) && !strcmp(enabledTag->GetValue(), "false"))
+ continue; // this relay pref is disabled
+
+ XMLTag* sourceTag = relayTag->GetEmbeddedTagByNameAndAttr("OBJECT", "CLASS", "source");
+ if (sourceTag == NULL)
+ continue;
+
+ char* type = sourceTag->GetAttributeValue("TYPE");
+ if (type == NULL)
+ continue;
+
+ if (!strcmp(type, "relay_sdp"))
+ {
+ XMLTag* fileNameTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "sdp_path");
+ if (!fileNameTag)
+ continue;
+
+ char* fileName = fileNameTag->GetValue();
+ if (fileName == NULL)
+ continue;
+
+ StrPtrLen theFileData;
+ (void)QTSSModuleUtils::ReadEntireFile(fileName, &theFileData);
+ if (theFileData.Len > 0) // There is a Relay SDP file!
+ {
+ RelaySDPSourceInfo* theInfo = NEW RelaySDPSourceInfo(&theFileData);
+
+ // Only keep this sdp file around if there are input streams &
+ // output streams described in it.
+ if ((theInfo->GetNumStreams() == 0) || (theInfo->GetNumOutputs() == 0))
+ delete theInfo;
+ else
+ {
+ theElem = NEW SourceInfoQueueElem(theInfo, false);
+ inSessionQueue->EnQueue(&theElem->fElem);
+ }
+ delete [] theFileData.Ptr;// We don't need the file data anymore
+ }
+ }
+ else if (!strcmp(type, "udp_source"))
+ {
+ RCFSourceInfo* theRCFInfo = NEW RCFSourceInfo(relayTag);
+ // Only keep this sdp file around if there are input streams &
+ // output streams described in it.
+ if ((theRCFInfo->GetNumStreams() == 0) || (theRCFInfo->GetNumOutputs() == 0))
+ delete theRCFInfo;
+ else
+ {
+ theElem = NEW SourceInfoQueueElem(theRCFInfo, false);
+ inSessionQueue->EnQueue(&theElem->fElem);
+ }
+ }
+ else if (!strcmp(type, "rtsp_source"))
+ {
+ // This is an rtsp source, so go through the describe, setup, play connect
+ // process before moving on.
+ RTSPSourceInfo* theRTSPInfo = NEW RTSPSourceInfo(0);
+ QTSS_Error theErr = theRTSPInfo->ParsePrefs(relayTag, false);
+ if (theErr == QTSS_NoErr)
+ {
+ // We have to do this *after* doing the DESCRIBE because parsing
+ // the relay destinations depends on information in the SDP
+ theRTSPInfo->ParseRelayDestinations(relayTag);
+
+ if (theRTSPInfo->GetNumOutputs() == 0)
+ delete theRTSPInfo;
+ else
+ {
+ theElem = NEW SourceInfoQueueElem(theRTSPInfo, true);
+ inSessionQueue->EnQueue(&theElem->fElem);
+ }
+ }
+ else
+ delete theRTSPInfo;
+ }
+ else if (!strcmp(type, "announced_source"))
+ {
+ // This is an announced rtsp source, so just set up an RTSPSourceInfo object and parse
+ // through the destinations. Leave the rest for later.
+ RTSPSourceInfo* theRTSPInfo = NEW RTSPSourceInfo(true);
+ QTSS_Error theErr = theRTSPInfo->ParsePrefs(relayTag, true);
+ if (theErr == QTSS_NoErr)
+ {
+ theRTSPInfo->ParseRelayDestinations(relayTag);
+
+ if (theRTSPInfo->GetNumOutputs() == 0)
+ delete theRTSPInfo;
+ else
+ {
+ theElem = NEW SourceInfoQueueElem(theRTSPInfo, true);
+ // put this sourceinfo elem on the global sAnnouncedQueue so that
+ // its session can be set up when an ANNOUNCE is received
+ inAnnouncedQueue->EnQueue(&theElem->fElem);
+ }
+ }
+ else
+ delete theRTSPInfo;
+ }
+ }
+}
+
+void ClearSourceInfos(OSQueue* inQueue)
+{
+ for (OSQueueIter theIter(inQueue); !theIter.IsDone(); )
+ {
+ // Get the current queue element, and make sure to move onto the
+ // next one, as the current one is going away.
+ SourceInfoQueueElem* theElem = (SourceInfoQueueElem*)theIter.GetCurrent()->GetEnclosingObject();
+
+ theIter.Next();
+
+ inQueue->Remove(&theElem->fElem);
+
+ // If we're supposed to delete this SourceInfo, then do so
+ if (theElem->fShouldDelete)
+ delete theElem->fSourceInfo;
+ delete theElem;
+ }
+}
+
+QTSS_Error RouteAuthorization(QTSS_StandardRTSP_Params* inParams)
+{
+ QTSS_Error theErr = QTSS_NoErr;
+ UInt32 theLen = 0;
+
+ // Get the Remote IP address
+ UInt32* theIP = 0;
+ theErr = QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesRemoteAddr, 0, (void**)&theIP, &theLen);
+ if ((theErr != QTSS_NoErr) || (theLen != sizeof(UInt32)))
+ {
+ Assert(0);
+ return QTSS_NoErr;
+ }
+
+ if (*theIP != INADDR_LOOPBACK)
+ return QTSS_NoErr;
+
+ // Get the Remote Port
+ UInt16* thePort = 0;
+ theLen = 0;
+ theErr = QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesRemotePort, 0, (void**)&thePort, &theLen);
+ if ((theErr != QTSS_NoErr) || (theLen != sizeof(UInt16)))
+ {
+ Assert(0);
+ return QTSS_NoErr;
+ }
+
+ // Check to see if any of the outputs has an announced source
+ // whose source URI and request port match theURL and thePort
+ if (IsRelayRequest(*thePort))
+ {
+ // ask server to skip authorization
+ if (QTSS_NoErr != QTSS_SetValue(inParams->inRTSPRequest,qtssRTSPReqSkipAuthorization, 0, &sSkipAuthorization, sizeof(sSkipAuthorization)))
+ return QTSS_RequestFailed; // bail on the request if the SetValue call fails!
+
+ // set the client session QTSSRelayModuleIsRelaySession attribute to true
+ if (QTSS_NoErr != QTSS_SetValue(inParams->inClientSession, sIsRelaySessionAttr, 0,(void *)&sIsRelaySession, sizeof(sIsRelaySession)))
+ return QTSS_RequestFailed; // bail on the request if the SetValue call fails!
+ }
+
+ return QTSS_NoErr;
+}
+
+Bool16 IsRelayRequest(UInt16 inPort)
+{
+ for (OSQueueIter iter(sRTSPSourceInfoQueue); !iter.IsDone(); iter.Next())
+ {
+ RTSPSourceInfo* info = (RTSPSourceInfo*)iter.GetCurrent()->GetEnclosingObject();
+
+ if (inPort == (info->GetClientSocket())->GetLocalPort())
+ return true;
+ }
+
+ return false;
+}
+
+void ReadRelayPrefsFile()
+{
+ // check to see if file has changed
+ struct stat buf;
+ if (::stat(sRelayPrefs, &buf) >= 0)
+ {
+ if (sRelayPrefModDate == buf.st_mtime)
+ return;
+
+ sRelayPrefModDate = buf.st_mtime;
+ }
+
+ XMLParser* xmlFile = new XMLParser(sRelayPrefs);
+
+ if (!xmlFile->DoesFileExist())
+ {
+ delete xmlFile;
+ return; // just return with no error logged
+ }
+
+ char errorBuffer[1000];
+ if (!xmlFile->ParseFile(errorBuffer, sizeof(errorBuffer)))
+ {
+ // log this error to the error log
+ QTSSModuleUtils::LogError( qtssWarningVerbosity, sRelayModulePrefParseErr, 0, NULL, NULL);
+ delete xmlFile;
+ return; // file was invalid, so no source infos
+ }
+
+ if (!CheckDNSNames(xmlFile, false))
+ {
+ // do reread prefs stuff now
+ RereadRelayPrefs(xmlFile);
+ delete xmlFile;
+ return;
+ }
+
+ // otherwise we need to do the DNS resolution on another thread
+ DNSResolverThread::ResolveRelayPrefs(xmlFile); // will keep or delete xmlFile
+}
+
+Bool16 CheckDNSNames(XMLParser* prefsFile, Bool16 doResolution)
+{
+ XMLTag* tag = prefsFile->GetRootTag();
+ XMLTag* relayTag;
+ XMLTag* prefTag;
+ UInt32 index = 0;
+ while ((relayTag = tag->GetEmbeddedTagByNameAndAttr("OBJECT", "TYPE", "relay", index)) != NULL)
+ {
+ index++;
+
+ XMLTag* enabledTag = relayTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "enabled");
+ if ((enabledTag != NULL) && (enabledTag->GetValue() != NULL) && !strcmp(enabledTag->GetValue(), "false"))
+ continue; // this relay pref is disabled
+
+ XMLTag* sourceTag = relayTag->GetEmbeddedTagByNameAndAttr("OBJECT", "CLASS", "source");
+ if (sourceTag == NULL)
+ continue;
+
+ prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "in_addr");
+ if (prefTag != NULL)
+ {
+ char* destAddrStr = prefTag->GetValue();
+ if (destAddrStr != NULL)
+ if (SocketUtils::ConvertStringToAddr(destAddrStr) == INADDR_NONE)
+ {
+ if (doResolution)
+ ResolveDNSAddr(prefTag);
+ else
+ return true;
+ }
+ }
+ prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "source_addr");
+ if (prefTag != NULL)
+ {
+ char* srcAddrStr = prefTag->GetValue();
+ if (srcAddrStr != NULL)
+ if (SocketUtils::ConvertStringToAddr(srcAddrStr) == INADDR_NONE)
+ {
+ if (doResolution)
+ ResolveDNSAddr(prefTag);
+ else
+ return true;
+ }
+ }
+
+ UInt32 numTags = relayTag->GetNumEmbeddedTags();
+ for (UInt32 y = 0; y < numTags; y++)
+ {
+ XMLTag* destTag = relayTag->GetEmbeddedTagByNameAndAttr("OBJECT", "CLASS", "destination", y);
+ if (destTag == NULL)
+ break;
+
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "out_addr");
+ if (prefTag != NULL)
+ {
+ char* outAddrStr = prefTag->GetValue();
+ if (outAddrStr != NULL)
+ if (SocketUtils::ConvertStringToAddr(outAddrStr) == INADDR_NONE)
+ {
+ if (doResolution)
+ ResolveDNSAddr(prefTag);
+ else
+ return true;
+ }
+ }
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "dest_addr");
+ if (prefTag != NULL)
+ {
+ char* destAddrStr = prefTag->GetValue();
+ if (destAddrStr != NULL)
+ if (SocketUtils::ConvertStringToAddr(destAddrStr) == INADDR_NONE)
+ {
+ if (doResolution)
+ ResolveDNSAddr(prefTag);
+ else
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void ResolveDNSAddr(XMLTag* tag)
+{
+ struct in_addr inAddr;
+ struct hostent* theHostent = ::gethostbyname(tag->GetValue());
+ if (theHostent != NULL)
+ {
+ char buffer[50];
+ StrPtrLen temp(buffer);
+ inAddr.s_addr = *(UInt32*)(theHostent->h_addr_list[0]);
+ SocketUtils::ConvertAddrToString(inAddr, &temp);
+ tag->SetValue(buffer);
+ }
+}
+
diff --git a/APIModules/QTSSReflectorModule/QTSSRelayModule.h b/APIModules/QTSSReflectorModule/QTSSRelayModule.h
new file mode 100644
index 0000000..a2dcd94
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/QTSSRelayModule.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * @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: QTSSReflectorModule.h
+
+ Contains: QTSS API module
+
+
+
+*/
+
+#ifndef _QTSSRELAYMODULE_H_
+#define _QTSSRELAYMODULE_H_
+
+#include "QTSS.h"
+
+extern "C"
+{
+ EXPORT QTSS_Error QTSSRelayModule_Main(void* inPrivateArgs);
+}
+
+#endif //_QTSSRELAYMODULE_H_
diff --git a/APIModules/QTSSReflectorModule/QTSSSplitterModule.cpp b/APIModules/QTSSReflectorModule/QTSSSplitterModule.cpp
new file mode 100644
index 0000000..576c411
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/QTSSSplitterModule.cpp
@@ -0,0 +1,667 @@
+/*
+ *
+ * @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: QTSSSplitterModule.cpp
+
+ Contains: Implementation of QTSSSplitterModule class.
+
+
+
+
+
+*/
+
+#include "QTSSSplitterModule.h"
+#include "QTSSModuleUtils.h"
+#include "ReflectorSession.h"
+#include "OSArrayObjectDeleter.h"
+#include "OSMemory.h"
+
+//ReflectorOutput objects
+#include "RTPSessionOutput.h"
+#include "RelayOutput.h"
+
+//SourceInfo objects
+#include "RTSPSourceInfo.h"
+
+// Fixes
+#include "QTSSMemoryDeleter.h"
+#include "FilePrefsSource.h"
+
+// ATTRIBUTES
+
+static QTSS_AttributeID sOutputAttr = qtssIllegalAttrID;
+static QTSS_AttributeID sSessionAttr = qtssIllegalAttrID;
+static QTSS_AttributeID sStreamCookieAttr = qtssIllegalAttrID;
+
+static QTSS_AttributeID sRemoteHostRespondedWithAnErrorErr = qtssIllegalAttrID;
+static QTSS_AttributeID sRemoteHostRefusedConnectionErr = qtssIllegalAttrID;
+static QTSS_AttributeID sExpectedDigitFilenameErr = qtssIllegalAttrID;
+static QTSS_AttributeID sBadTrackIDErr = qtssIllegalAttrID;
+
+// STATIC DATA
+
+static const UInt32 kSessionStartingIdleTimeInMsec = 20;
+static const StrPtrLen kCacheControlHeader("no-cache");
+static QTSS_PrefsObject sServerPrefs = NULL;
+
+static OSRefTable* sSessionMap = NULL;
+
+// Important strings
+static StrPtrLen sRCFSuffix(".rcf");
+static StrPtrLen sRTSPSourceStr("relay_source");
+
+// FUNCTION PROTOTYPES
+
+static QTSS_Error QTSSSplitterModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
+static QTSS_Error Register(QTSS_Register_Params* inParams);
+static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
+static QTSS_Error Shutdown();
+static QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParams);
+static QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams);
+static ReflectorSession* FindOrCreateSession(StrPtrLen* inPath, QTSS_StandardRTSP_Params* inParams);
+static QTSS_Error HandleSourceInfoErr(QTSS_Error rtspSourceInfoErr, QTSS_StandardRTSP_Params* inParams,
+ ReflectorSession* inSession, RTSPClient* inClient);
+static void DeleteSessionOnError(ReflectorSession* inSession, QTSS_ClientSessionObject inCliSession);
+static void NullOutSessionAttr(QTSS_ClientSessionObject inSession);
+static QTSS_Error DoSetup(QTSS_StandardRTSP_Params* inParams, ReflectorSession* inSession);
+static QTSS_Error DoPlay(QTSS_StandardRTSP_Params* inParams, ReflectorSession* inSession);
+static QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams);
+static void IssueTeardown(ReflectorSession* inSession);
+static void RequestSocketEvent(QTSS_StreamRef inStream, UInt32 inEventMask);
+
+// FUNCTION IMPLEMENTATIONS
+
+QTSS_Error QTSSSplitterModule_Main(void* inPrivateArgs)
+{
+ return _stublibrary_main(inPrivateArgs, QTSSSplitterModuleDispatch);
+}
+
+
+QTSS_Error QTSSSplitterModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
+{
+ switch (inRole)
+ {
+ case QTSS_Register_Role:
+ return Register(&inParams->regParams);
+ case QTSS_Initialize_Role:
+ return Initialize(&inParams->initParams);
+ case QTSS_RTSPPreProcessor_Role:
+ return ProcessRTSPRequest(&inParams->rtspRequestParams);
+ case QTSS_ClientSessionClosing_Role:
+ return DestroySession(&inParams->clientSessionClosingParams);
+ case QTSS_Shutdown_Role:
+ return Shutdown();
+ }
+ return QTSS_NoErr;
+}
+
+
+QTSS_Error Register(QTSS_Register_Params* inParams)
+{
+ // Do role & attribute setup
+ (void)QTSS_AddRole(QTSS_Initialize_Role);
+ (void)QTSS_AddRole(QTSS_Shutdown_Role);
+ (void)QTSS_AddRole(QTSS_RTSPPreProcessor_Role);
+ (void)QTSS_AddRole(QTSS_ClientSessionClosing_Role);
+
+ // Add text messages attributes
+ static char* sRemoteHostRespondedWithAnErrorName = "QTSSSplitterModuleRemoteHostError";
+ static char* sRemoteHostRefusedConnectionName = "QTSSSplitterModuleRemoteHostRefused";
+ static char* sExpectedDigitFilenameName = "QTSSSplitterModuleExpectedDigitFilename";
+ static char* sBadTrackIDErrName = "QTSSSplitterModuleBadTrackID";
+
+ (void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sRemoteHostRespondedWithAnErrorName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssTextMessagesObjectType, sRemoteHostRespondedWithAnErrorName, &sRemoteHostRespondedWithAnErrorErr);
+
+ (void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sRemoteHostRefusedConnectionName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssTextMessagesObjectType, sRemoteHostRefusedConnectionName, &sRemoteHostRefusedConnectionErr);
+
+ (void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sExpectedDigitFilenameName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssTextMessagesObjectType, sExpectedDigitFilenameName, &sExpectedDigitFilenameErr);
+
+ (void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sBadTrackIDErrName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssTextMessagesObjectType, sBadTrackIDErrName, &sBadTrackIDErr);
+
+ // Add an RTP session attribute for tracking ReflectorSession objects
+ static char* sOutputName = "QTSSSplitterModuleOutput";
+ static char* sSessionName= "QTSSSplitterModuleSession";
+ static char* sStreamCookieName = "QTSSSplitterModuleStreamCookie";
+
+ (void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sOutputName, NULL, qtssAttrDataTypeVoidPointer);
+ (void)QTSS_IDForAttr(qtssClientSessionObjectType, sOutputName, &sOutputAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sSessionName, NULL, qtssAttrDataTypeVoidPointer);
+ (void)QTSS_IDForAttr(qtssClientSessionObjectType, sSessionName, &sSessionAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sStreamCookieName, NULL, qtssAttrDataTypeVoidPointer);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sStreamCookieName, &sStreamCookieAttr);
+
+ // Reflector stream needs to setup some parameters too.
+ ReflectorStream::Register();
+ // RTPSessionOutput needs to do the same
+ RTPSessionOutput::Register();
+
+ // Tell the server our name!
+ static char* sModuleName = "QTSSSplitterModule";
+ ::strcpy(inParams->outModuleName, sModuleName);
+
+ return QTSS_NoErr;
+}
+
+
+QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
+{
+ // Setup module utils
+ QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
+ sSessionMap = NEW OSRefTable();
+ sServerPrefs = inParams->inPrefs;
+
+ //
+ // Instead of passing our own module prefs object, as one might expect,
+ // here we pass in the QTSSReflectorModule's, because the prefs that
+ // apply to ReflectorStream are stored in that module's prefs
+ StrPtrLen theReflectorModule("QTSSReflectorModule");
+ QTSS_ModulePrefsObject theReflectorPrefs =
+ QTSSModuleUtils::GetModulePrefsObject(QTSSModuleUtils::GetModuleObjectByName(theReflectorModule));
+
+ // Call helper class initializers
+ ReflectorStream::Initialize(theReflectorPrefs);
+ ReflectorSession::Initialize();
+
+ // Report to the server that this module handles DESCRIBE, SETUP, PLAY, PAUSE, and TEARDOWN
+ static QTSS_RTSPMethod sSupportedMethods[] = { qtssDescribeMethod, qtssSetupMethod, qtssTeardownMethod, qtssPlayMethod, qtssPauseMethod };
+ QTSSModuleUtils::SetupSupportedMethods(inParams->inServer, sSupportedMethods, 5);
+
+ return QTSS_NoErr;
+}
+
+QTSS_Error Shutdown()
+{
+ return QTSS_NoErr;
+}
+
+QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParams)
+{
+ QTSS_RTSPMethod* theMethod = NULL;
+ UInt32 theLen = 0;
+ if ((QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqMethod, 0,
+ (void**)&theMethod, &theLen) != QTSS_NoErr) || (theLen != sizeof(QTSS_RTSPMethod)))
+ {
+ Assert(0);
+ return QTSS_RequestFailed;
+ }
+
+ if (*theMethod == qtssDescribeMethod)
+ return DoDescribe(inParams);
+
+ RTPSessionOutput** theOutput = NULL;
+ QTSS_Error theErr = QTSS_GetValuePtr(inParams->inClientSession, sOutputAttr, 0, (void**)&theOutput, &theLen);
+ if ((theErr != QTSS_NoErr) || (theLen != sizeof(RTPSessionOutput*)))
+ return QTSS_RequestFailed;
+
+ switch (*theMethod)
+ {
+ case qtssSetupMethod:
+ return DoSetup(inParams, (*theOutput)->GetReflectorSession());
+ case qtssPlayMethod:
+ return DoPlay(inParams, (*theOutput)->GetReflectorSession());
+ case qtssTeardownMethod:
+ // Tell the server that this session should be killed, and send a TEARDOWN response
+ (void)QTSS_Teardown(inParams->inClientSession);
+ (void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, inParams->inClientSession, 0);
+ break;
+ case qtssPauseMethod:
+ (void)QTSS_Pause(inParams->inClientSession);
+ (void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, inParams->inClientSession, 0);
+ break;
+ default:
+ break;
+ }
+ return QTSS_NoErr;
+}
+
+
+QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams)
+{
+ QTSS_Error theErr = QTSS_NoErr;
+
+ // If this URL doesn't end with an .rcf, don't even bother. Ah, if only the QTSSReflectorModule
+ // could make this same check as well
+ StrPtrLen theURI;
+ (void)QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqURI, 0, (void**)&theURI.Ptr, &theURI.Len);
+ if ((theURI.Len < sRCFSuffix.Len) || (!sRCFSuffix.EqualIgnoreCase(&theURI.Ptr[theURI.Len - sRCFSuffix.Len], sRCFSuffix.Len)))
+ return QTSS_NoErr;
+
+ // Check and see if we are in the process of setting this connection up already
+ ReflectorSession* theSession = NULL;
+ UInt32 theLen = sizeof(ReflectorSession*);
+ (void)QTSS_GetValue(inParams->inClientSession, sSessionAttr, 0, (void*)&theSession, &theLen);
+
+ if (theSession == NULL)
+ {
+ // If we are not already in the process of initializing and setting up this ReflectorSession,
+ // attempt to create one or attach to one.
+
+ // Check and see if the full path to this file matches an existing ReflectorSession
+ StrPtrLen thePathPtr;
+ OSCharArrayDeleter rcfPath(QTSSModuleUtils::GetFullPath( inParams->inRTSPRequest,
+ qtssRTSPReqFilePath,
+ &thePathPtr.Len, NULL));
+
+ thePathPtr.Ptr = rcfPath.GetObject();
+ theSession = FindOrCreateSession(&thePathPtr, inParams);
+ // If this function returned an error, this request shouldn't be handled by this module
+ if (theSession == NULL)
+ return QTSS_NoErr;
+ }
+ if (!((RTSPSourceInfo*)theSession->GetSourceInfo())->IsDescribeComplete())
+ {
+ // Only the session owner need worry about this code. this is an easy.
+ // Way of checking this.
+ Assert(theLen == sizeof(ReflectorSession*));
+
+ // If we haven't finished the describe yet, call
+ // Describe again on the RTSPSourceInfo object.
+ QTSS_Error theErr = ((RTSPSourceInfo*)theSession->GetSourceInfo())->Describe();
+ if (theErr != QTSS_NoErr)
+ return HandleSourceInfoErr(theErr, inParams, theSession,
+ ((RTSPSourceInfo*)theSession->GetSourceInfo())->GetRTSPClient());
+ else
+ {
+ // Describe has completed. At this point we can setup the ReflectorSession.
+ // However, tell it not to consider this session completely setup yet, as
+ theErr = theSession->SetupReflectorSession(theSession->GetSourceInfo(), inParams, ReflectorSession::kDontMarkSetup);
+ if (theErr != QTSS_NoErr)
+ {
+ // If we get an error here, for some reason we couldn't bind the ports, etc, etc.
+ // Just abort
+ DeleteSessionOnError(theSession, inParams->inClientSession);
+ return theErr;
+ }
+ }
+ }
+
+ if (!theSession->IsSetup())
+ {
+ // Only the session owner need worry about this code. this is an easy.
+ // Way of checking this.
+ Assert(theLen == sizeof(ReflectorSession*));
+
+ // If we get here, the DESCRIBE has completed, but if we are the owner that isn't enough.
+ // We need to make sure that the SETUP and PLAY requests execute as well.
+ theErr = ((RTSPSourceInfo*)theSession->GetSourceInfo())->SetupAndPlay();
+ if (theErr != QTSS_NoErr)
+ return HandleSourceInfoErr(theErr, inParams, theSession,
+ ((RTSPSourceInfo*)theSession->GetSourceInfo())->GetRTSPClient());
+
+ // We've completed the SETUP and PLAY process if we are here. The ReflectorSession
+ // is completely setup.
+ theSession->ManuallyMarkSetup();
+
+ // NULL out the sSessionAttr, we don't need it anymore.
+ NullOutSessionAttr(inParams->inClientSession);
+ }
+
+ //ok, we've found or setup the proper reflector session, create an RTPSessionOutput object,
+ //and add it to the session's list of outputs
+ RTPSessionOutput* theNewOutput = NEW RTPSessionOutput(inParams->inClientSession, theSession, sServerPrefs, sStreamCookieAttr );
+ theSession->AddOutput(theNewOutput, true);
+
+ // And vice-versa, store this reflector session in the RTP session.
+ (void)QTSS_SetValue(inParams->inClientSession, sOutputAttr, 0, &theNewOutput, sizeof(theNewOutput));
+
+ // Finally, send the DESCRIBE response
+
+ //above function has signalled that this request belongs to us, so let's respond
+ iovec theDescribeVec[2] = { 0 };
+
+ Assert(theSession->GetLocalSDP()->Ptr != NULL);
+ theDescribeVec[1].iov_base = theSession->GetLocalSDP()->Ptr;
+ theDescribeVec[1].iov_len = theSession->GetLocalSDP()->Len;
+ (void)QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssCacheControlHeader,
+ kCacheControlHeader.Ptr, kCacheControlHeader.Len);
+ QTSSModuleUtils::SendDescribeResponse(inParams->inRTSPRequest, inParams->inClientSession,
+ &theDescribeVec[0], 2, theSession->GetLocalSDP()->Len);
+ return QTSS_NoErr;
+}
+
+ReflectorSession* FindOrCreateSession(StrPtrLen* inPath, QTSS_StandardRTSP_Params* inParams)
+{
+ // This function assumes that inPath is NULL terminated
+
+ StrPtrLen theFileData;
+ RTSPSourceInfo* theInfo = NULL;
+
+ (void)QTSSModuleUtils::ReadEntireFile(inPath->Ptr, &theFileData);
+ if (theFileData.Len > 0)
+ theInfo = NEW RTSPSourceInfo(Socket::kNonBlockingSocketType);
+ else
+ return NULL;
+
+ // We need to interpret this file as a standard prefs file, so let the
+ // FilePrefsSource object parse it, then call ParsePrefs on the RTSPSourceInfo object,
+ // which will parse out the RCF metadata.
+
+ FilePrefsSource thePrefsSource(true);// Allow duplicates
+ (void)thePrefsSource.InitFromConfigFile(inPath->Ptr);
+ QTSS_Error theErr = theInfo->ParsePrefs(&thePrefsSource, 0);
+ if (theErr != QTSS_NoErr)
+ {
+ delete theInfo;
+ return NULL;
+ }
+
+ // Ok, look for a reflector session matching the URL specified in the RCF file.
+ // A unique broadcast is defined by the URL, the URL is the argument to resolve.
+
+ OSMutexLocker locker(sSessionMap->GetMutex());
+ OSRef* theSessionRef = sSessionMap->Resolve(theInfo->GetRTSPClient()->GetURL());
+ ReflectorSession* theSession = NULL;
+
+ if (theSessionRef == NULL)
+ {
+ //If this URL doesn't already have a reflector session, we must make a new
+ //one. We already have the proper sourceInfo object, so we only need to construct the session
+
+ theSession = NEW ReflectorSession(theInfo->GetRTSPClient()->GetURL(), theInfo);
+
+ //put the session's ID into the session map.
+ theErr = sSessionMap->Register(theSession->GetRef());
+ Assert(theErr == QTSS_NoErr);
+
+ //unless we do this, the refcount won't increment (and we'll delete the session prematurely
+ OSRef* debug = sSessionMap->Resolve(theInfo->GetRTSPClient()->GetURL());
+ Assert(debug == theSession->GetRef());
+
+ // Create a socket stream for the TCP socket in the RTSPClient object. The socket stream will
+ // allow this module to receive events on the socket
+ QTSS_StreamRef theSockStream = NULL;
+ theErr = QTSS_CreateStreamFromSocket(theInfo->GetRTSPClient()->GetSocket()->GetSocket()->GetSocketFD(), &theSockStream);
+ Assert(theErr == QTSS_NoErr);
+ Assert(theSockStream != NULL);
+
+ // Store the socket stream in the Reflector Session so we can get at it easily later on
+ theSession->SetSocketStream(theSockStream);
+
+ // This RTSP session is the "owner" of this ReflectorSession, and will be responsible
+ // for setting it up properly, so we should make sure this attribute gets set
+ UInt32 theLen = sizeof(theSession);
+ theErr = QTSS_SetValue(inParams->inClientSession, sSessionAttr, 0, (void*)&theSession, theLen);
+ Assert(theErr == QTSS_NoErr);
+ }
+ else
+ {
+ // We aren't the owner of this ReflectorSession, and only the owner needs to keep this
+ // RTSPSourceInfo object around.
+ delete theInfo;
+
+ theSession = (ReflectorSession*)theSessionRef->GetObject();
+
+ if (!theSession->IsSetup())
+ {
+ // We are not the creator of this session, and it may not be setup yet. If it isn't,
+ // we should simply wait for it to be setup.
+ sSessionMap->Release(theSession->GetRef());
+
+ // Give the owner some time to finish setting it up.
+ (void)QTSS_SetIdleTimer(kSessionStartingIdleTimeInMsec);
+ return NULL; // There isn't a completed session... yet.............
+ }
+ }
+
+ Assert(theSession != NULL);
+ return theSession;
+}
+
+QTSS_Error HandleSourceInfoErr(QTSS_Error rtspSourceInfoErr, QTSS_StandardRTSP_Params* inParams,
+ ReflectorSession* inSession, RTSPClient* inClient)
+{
+ // If we get an EAGAIN here, the DESCRIBE hasn't completed yet
+ if ((rtspSourceInfoErr == EAGAIN) || (rtspSourceInfoErr == EINPROGRESS))
+ {
+ // We're making an assumption here that inClient only uses one socket to connect to
+ // the server. We only have one stream, so we have to make that assumption.
+
+ // Note that it is not necessary to have any kind of timeout here, because the server
+ // naturally times out idle connections. If the server doesn't respond for awhile,
+ // this session will naturally go away
+ inClient->GetSocket()->GetSocket()->DontAutoCleanup();
+ RequestSocketEvent(inSession->GetSocketStream(), inClient->GetSocket()->GetEventMask());
+ return QTSS_NoErr; // We'll get called in the same method again when there is more work to do
+ }
+
+ // We've encountered a fatal error for this session, so delete it.
+ DeleteSessionOnError(inSession, inParams->inClientSession);
+
+ if (rtspSourceInfoErr == QTSS_RequestFailed)
+ {
+ // This happens if the remote host responded with an error.
+ char tempBuf[20];
+ qtss_sprintf(tempBuf, "%"_U32BITARG_"", inClient->GetStatus());
+ StrPtrLen tempBufPtr(&tempBuf[0]);
+ return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssServerGatewayTimeout,
+ sRemoteHostRespondedWithAnErrorErr, &tempBufPtr);
+ }
+ else
+ return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssServerGatewayTimeout,
+ sRemoteHostRefusedConnectionErr);
+}
+
+void DeleteSessionOnError(ReflectorSession* inSession, QTSS_ClientSessionObject inCliSession)
+{
+ // Make sure to destroy the socket stream as well
+ Assert(inSession->GetSocketStream() != NULL);
+ (void)QTSS_DestroySocketStream(inSession->GetSocketStream());
+
+ OSMutexLocker locker (sSessionMap->GetMutex());
+ //decrement the ref count
+ sSessionMap->Release(inSession->GetRef());
+
+ // We are here if we are the owner of this session and we encountered an error
+ // while trying to setup the session. We have the session map mutex, so the
+ // refcount at this point *must* be 0.
+ Assert(inSession->GetRef()->GetRefCount() == 0);
+ sSessionMap->UnRegister(inSession->GetRef());
+ delete inSession;
+
+ // Make sure the session is NULLd out, because it's deleted now!
+ NullOutSessionAttr(inCliSession);
+}
+
+void NullOutSessionAttr(QTSS_ClientSessionObject inSession)
+{
+ ReflectorSession* theNull = NULL;
+ UInt32 theLen = sizeof(theNull);
+ QTSS_Error theErr = QTSS_SetValue(inSession, sSessionAttr, 0, (void*)&theNull, theLen);
+ Assert(theErr == QTSS_NoErr);
+}
+
+QTSS_Error DoSetup(QTSS_StandardRTSP_Params* inParams, ReflectorSession* inSession)
+{
+ //unless there is a digit at the end of this path (representing trackID), don't
+ //even bother with the request
+ char* theDigitStr = NULL;
+ (void)QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFileDigit, 0, &theDigitStr);
+ QTSSCharArrayDeleter theDigitStrDeleter(theDigitStr);
+ if (theDigitStr == NULL)
+ return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest, sExpectedDigitFilenameErr);
+
+ UInt32 theTrackID = ::strtol(theDigitStr, NULL, 10);
+
+ QTSS_Error theErr = QTSS_NoErr;
+
+ // Get info about this trackID
+ SourceInfo::StreamInfo* theStreamInfo = inSession->GetSourceInfo()->GetStreamInfoByTrackID(theTrackID);
+ // If theStreamInfo is NULL, we don't have a legit track, so return an error
+ if (theStreamInfo == NULL)
+ return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest,
+ sBadTrackIDErr);
+
+ StrPtrLen* thePayloadName = &theStreamInfo->fPayloadName;
+ QTSS_RTPPayloadType thePayloadType = theStreamInfo->fPayloadType;
+
+ QTSS_RTPStreamObject newStream = NULL;
+ {
+ // Ok, this is completely crazy but I can't think of a better way to do this that's
+ // safe so we'll do it this way for now. Because the ReflectorStreams use this session's
+ // stream queue, we need to make sure that each ReflectorStream is not reflecting to this
+ // session while we call QTSS_AddRTPStream. One brutal way to do this is to grab each
+ // ReflectorStream's mutex, which will stop every reflector stream from running.
+
+ for (UInt32 x = 0; x < inSession->GetNumStreams(); x++)
+ inSession->GetStreamByIndex(x)->GetMutex()->Lock();
+
+ theErr = QTSS_AddRTPStream(inParams->inClientSession, inParams->inRTSPRequest, &newStream, 0);
+
+ for (UInt32 y = 0; y < inSession->GetNumStreams(); y++)
+ inSession->GetStreamByIndex(y)->GetMutex()->Unlock();
+
+ if (theErr != QTSS_NoErr)
+ return theErr;
+ }
+
+ // Set up dictionary items for this stream
+ theErr = QTSS_SetValue(newStream, qtssRTPStrPayloadName, 0, thePayloadName->Ptr, thePayloadName->Len);
+ Assert(theErr == QTSS_NoErr);
+ theErr = QTSS_SetValue(newStream, qtssRTPStrPayloadType, 0, &thePayloadType, sizeof(thePayloadType));
+ Assert(theErr == QTSS_NoErr);
+ theErr = QTSS_SetValue(newStream, qtssRTPStrTrackID, 0, &theTrackID, sizeof(theTrackID));
+ Assert(theErr == QTSS_NoErr);
+
+ // Place the stream cookie in this stream for future reference
+ void* theStreamCookie = inSession->GetStreamCookie(theTrackID);
+ Assert(theStreamCookie != NULL);
+ theErr = QTSS_SetValue(newStream, sStreamCookieAttr, 0, &theStreamCookie, sizeof(theStreamCookie));
+ Assert(theErr == QTSS_NoErr);
+
+ // Set the number of quality levels.
+ static UInt32 sNumQualityLevels = ReflectorSession::kNumQualityLevels;
+ theErr = QTSS_SetValue(newStream, qtssRTPStrNumQualityLevels, 0, &sNumQualityLevels, sizeof(sNumQualityLevels));
+ Assert(theErr == QTSS_NoErr);
+
+ //send the setup response
+ (void)QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssCacheControlHeader,
+ kCacheControlHeader.Ptr, kCacheControlHeader.Len);
+ (void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, newStream, 0);
+ return QTSS_NoErr;
+}
+
+
+QTSS_Error DoPlay(QTSS_StandardRTSP_Params* inParams, ReflectorSession* inSession)
+{
+ // Tell the session what the bitrate of this reflection is. This is nice for logging,
+ // it also allows the server to scale the TCP buffer size appropriately if we are
+ // interleaving the data over TCP. This must be set before calling QTSS_Play so the
+ // server can use it from within QTSS_Play
+ UInt32 bitsPerSecond = inSession->GetBitRate();
+ (void)QTSS_SetValue(inParams->inClientSession, qtssCliSesMovieAverageBitRate, 0, &bitsPerSecond, sizeof(bitsPerSecond));
+
+ //Server shouldn't send RTCP (reflector does it), but the server should append the server info app packet
+ QTSS_Error theErr = QTSS_Play(inParams->inClientSession, inParams->inRTSPRequest, qtssPlayFlagsAppendServerInfo);
+ if (theErr != QTSS_NoErr)
+ return theErr;
+
+ //and send a standard play response
+ (void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, inParams->inClientSession, 0);
+ return QTSS_NoErr;
+}
+
+QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams)
+{
+ // Check and see if we are in the process of tearing down this connection already
+ ReflectorSession* theSession = NULL;
+ UInt32 theLen = sizeof(ReflectorSession*);
+ (void)QTSS_GetValue(inParams->inClientSession, sSessionAttr, 0, (void*)&theSession, &theLen);
+
+ if (theSession != NULL)
+ IssueTeardown(theSession);
+ else
+ {
+ RTPSessionOutput** theOutput = NULL;
+ QTSS_Error theErr = QTSS_GetValuePtr(inParams->inClientSession, sOutputAttr, 0, (void**)&theOutput, &theLen);
+ if ((theErr != QTSS_NoErr) || (theLen != sizeof(RTPSessionOutput*)) || (theOutput == NULL))
+ return QTSS_RequestFailed;
+
+ // This function removes the output from the ReflectorSession, then
+ // checks to see if the session should go away. If it should, this deletes it
+ theSession = (*theOutput)->GetReflectorSession();
+ theSession->RemoveOutput(*theOutput, true);
+ delete (*theOutput);
+
+ //check if the ReflectorSession should be deleted
+ //(it should if its ref count has dropped to 0)
+ OSMutexLocker locker (sSessionMap->GetMutex());
+ //decrement the ref count
+ sSessionMap->Release(theSession->GetRef());
+ if (theSession->GetRef()->GetRefCount() == 0)
+ {
+ sSessionMap->UnRegister(theSession->GetRef());
+
+ theLen = sizeof(theSession);
+ theErr = QTSS_SetValue(inParams->inClientSession, sSessionAttr, 0, (void*)&theSession, theLen);
+ if (theErr != QTSS_NoErr)
+ delete theSession;
+ else
+ IssueTeardown(theSession);
+ }
+ }
+ return QTSS_NoErr;
+}
+
+void IssueTeardown(ReflectorSession* inSession)
+{
+ // Tell the RTSPSourceInfo object to initiate or continue the Teardown process
+ QTSS_Error theErr = ((RTSPSourceInfo*)inSession->GetSourceInfo())->Teardown();
+ if ((theErr == EAGAIN) || (theErr == EINPROGRESS))
+ {
+ RTSPClient* theClient = ((RTSPSourceInfo*)inSession->GetSourceInfo())->GetRTSPClient();
+ theClient->GetSocket()->GetSocket()->DontAutoCleanup();
+ RequestSocketEvent(inSession->GetSocketStream(), theClient->GetSocket()->GetEventMask());
+ }
+ else
+ {
+ // Make sure to destroy the socket stream as well
+ Assert(inSession->GetSocketStream() != NULL);
+ (void)QTSS_DestroySocketStream(inSession->GetSocketStream());
+
+ delete inSession;
+ }
+}
+
+void RequestSocketEvent(QTSS_StreamRef inStream, UInt32 inEventMask)
+{
+ //
+ // Job of this function is to convert a CommonUtilitiesLib event mask to a QTSS Event mask
+ QTSS_EventType theEvent = 0;
+
+ if (inEventMask & EV_RE)
+ theEvent |= QTSS_ReadableEvent;
+ if (inEventMask & EV_WR)
+ theEvent |= QTSS_WriteableEvent;
+
+ QTSS_Error theErr = QTSS_RequestEvent(inStream, theEvent);
+ Assert(theErr == QTSS_NoErr);
+}
diff --git a/APIModules/QTSSReflectorModule/QTSSSplitterModule.h b/APIModules/QTSSReflectorModule/QTSSSplitterModule.h
new file mode 100644
index 0000000..34e2e52
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/QTSSSplitterModule.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * @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: QTSSSplitterModule.h
+
+ Contains: QTSS API module
+
+
+*/
+
+#ifndef _QTSSSPLITTERMODULE_H_
+#define _QTSSSPLITTERMODULE_H_
+
+#include "QTSS.h"
+
+extern "C"
+{
+ EXPORT QTSS_Error QTSSSplitterModule_Main(void* inPrivateArgs);
+}
+
+#endif //_QTSSSPLITTERMODULE_H_
diff --git a/APIModules/QTSSReflectorModule/RCFSourceInfo.cpp b/APIModules/QTSSReflectorModule/RCFSourceInfo.cpp
new file mode 100644
index 0000000..cc4d245
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/RCFSourceInfo.cpp
@@ -0,0 +1,248 @@
+/*
+ *
+ * @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: RCFSourceInfo.cpp
+
+ Contains: Implementation of object defined in .h file
+
+ Copyright: © 1998 by Apple Computer, Inc., all rights reserved.
+
+
+
+*/
+
+#include "RCFSourceInfo.h"
+#include "PrefsSource.h"
+#include "StringParser.h"
+#include "OSMemory.h"
+#include "SocketUtils.h"
+
+void RCFSourceInfo::SetName(const char* inName)
+{
+ if (inName != NULL)
+ {
+ fName = NEW char[::strlen(inName) + 1];
+ ::strcpy(fName, inName);
+ }
+}
+
+RCFSourceInfo::~RCFSourceInfo()
+{
+ if (fName != NULL)
+ delete fName;
+
+ // Not necessary anymore as the destructor of the base class will take care
+ // of deleting all allocated memory for fOutputArray and fStreamArray
+ /*
+ if (fOutputArray != NULL)
+ {
+ for (UInt32 x = 0; x < fNumOutputs; x++)
+ delete [] fOutputArray[x].fPortArray;
+
+ char* theOutputArray = (char*)fOutputArray;
+ delete [] theOutputArray;
+ }
+ if (fStreamArray != NULL)
+ {
+ char* theStreamArray = (char*)fStreamArray;
+ delete [] theStreamArray;
+ }
+ */
+}
+
+void RCFSourceInfo::Parse(XMLTag* relayTag)
+{
+ XMLTag* sourceTag = relayTag->GetEmbeddedTagByNameAndAttr("OBJECT", "CLASS", "source");
+ if (sourceTag == NULL)
+ return;
+
+ fNumStreams = 0;
+ UInt32 destIPAddr = 0;
+ UInt32 srcIPAddr = 0;
+ UInt16 ttl = 0;
+
+ XMLTag* prefTag;
+
+ prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "in_addr");
+ if (prefTag != NULL)
+ {
+ char* destAddrStr = prefTag->GetValue();
+ if (destAddrStr != NULL)
+ destIPAddr = SocketUtils::ConvertStringToAddr(destAddrStr);
+ }
+ prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "source_addr");
+ if (prefTag != NULL)
+ {
+ char* srcAddrStr = prefTag->GetValue();
+ if (srcAddrStr != NULL)
+ srcIPAddr = SocketUtils::ConvertStringToAddr(srcAddrStr);
+ }
+ prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "ttl");
+ if (prefTag != NULL)
+ {
+ char* ttlStr = prefTag->GetValue();
+ if (ttlStr != NULL)
+ ttl = atoi(ttlStr);
+ }
+ prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("LIST-PREF", "NAME", "udp_ports");
+ if (prefTag != NULL)
+ {
+ fNumStreams = prefTag->GetNumEmbeddedTags();
+
+ // Allocate a proper sized stream array
+ fStreamArray = NEW StreamInfo[fNumStreams];
+
+ for (UInt32 x = 0; x < fNumStreams; x++)
+ {
+ XMLTag* portTag = prefTag->GetEmbeddedTagByName("VALUE", x);
+ int port = 0;
+ if (portTag != NULL)
+ {
+ char* portStr = portTag->GetValue();
+ if (portStr != NULL)
+ port = atoi(portStr);
+ }
+
+ // Setup all the StreamInfo structures
+ fStreamArray[x].fSrcIPAddr = srcIPAddr;
+ fStreamArray[x].fDestIPAddr = destIPAddr;
+ fStreamArray[x].fPort = port;
+ fStreamArray[x].fTimeToLive = ttl;
+ fStreamArray[x].fPayloadType = qtssUnknownPayloadType;
+ fStreamArray[x].fTrackID = x+1;
+ }
+ }
+
+ // Now go through all the relay_destination lines (starting from the next line after the
+ // relay_source line.
+ this->ParseRelayDestinations(relayTag);
+}
+
+void RCFSourceInfo::ParseRelayDestinations(XMLTag* relayTag)
+{
+ // parse the NAME attribute of the relay tag and store it in the relayname attribute
+ char* name = relayTag->GetAttributeValue("NAME");
+ if (name != NULL)
+ {
+ fName = NEW char[::strlen(name) + 1];
+ ::strcpy(fName, name);
+ }
+
+ UInt32 numTags = relayTag->GetNumEmbeddedTags();
+ AllocateOutputArray(numTags); // not all these are relay tags, but most are
+
+ // Now actually go through and figure out what to put into these OutputInfo structures,
+ // based on what's on the relay_destination line
+ fNumOutputs = 0;
+ for (UInt32 y = 0; y < numTags; y++)
+ {
+ XMLTag* destTag = relayTag->GetEmbeddedTagByNameAndAttr("OBJECT", "CLASS", "destination", y);
+ if (destTag == NULL)
+ return;
+
+ char* destType = destTag->GetAttributeValue("TYPE");
+ if (destType == NULL)
+ return;
+
+ if (!strcmp(destType, "udp_destination"))
+ ParseDestination(destTag, y);
+ else if (!strcmp(destType, "announced_destination"))
+ ParseAnnouncedDestination(destTag, y);
+
+ fNumOutputs++;
+ }
+}
+
+void RCFSourceInfo::ParseDestination(XMLTag* destTag, UInt32 index)
+{
+ XMLTag* prefTag;
+
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "out_addr");
+ if (prefTag != NULL)
+ {
+ char* outAddrStr = prefTag->GetValue();
+ if (outAddrStr != NULL)
+ fOutputArray[index].fLocalAddr = SocketUtils::ConvertStringToAddr(outAddrStr);
+ }
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "dest_addr");
+ if (prefTag != NULL)
+ {
+ char* destAddrStr = prefTag->GetValue();
+ if (destAddrStr != NULL)
+ fOutputArray[index].fDestAddr = SocketUtils::ConvertStringToAddr(destAddrStr);
+ }
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "ttl");
+ if (prefTag != NULL)
+ {
+ char* ttlStr = prefTag->GetValue();
+ if (ttlStr != NULL)
+ fOutputArray[index].fTimeToLive = atoi(ttlStr);
+ }
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("LIST-PREF", "NAME", "udp_ports");
+ if (prefTag != NULL)
+ {
+ fOutputArray[index].fNumPorts = prefTag->GetNumEmbeddedTags();
+
+ fOutputArray[index].fPortArray = NEW UInt16[fOutputArray[index].fNumPorts];
+ ::memset(fOutputArray[index].fPortArray, 0, fOutputArray[index].fNumPorts * sizeof(UInt16));
+
+ for (UInt32 x = 0; x < fOutputArray[index].fNumPorts; x++)
+ {
+ XMLTag* portTag = prefTag->GetEmbeddedTagByName("VALUE", x);
+ if (portTag != NULL)
+ {
+ char* portStr = portTag->GetValue();
+ if (portStr != NULL)
+ {
+ fOutputArray[index].fPortArray[x] = atoi(portStr);
+ }
+ }
+ }
+ }
+ else
+ {
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "udp_base_port");
+ if(prefTag == NULL)
+ qtss_printf("Missing both 'udp_base_port' and 'udp_ports' tags.\n Cannot set up the destination!\n");
+ else
+ {
+ char* basePortStr = prefTag->GetValue();
+ if (basePortStr != NULL)
+ fOutputArray[index].fBasePort = atoi(basePortStr);
+ }
+ }
+}
+
+void RCFSourceInfo::ParseAnnouncedDestination(XMLTag* destTag, UInt32 index)
+{
+ // should log some sort of error
+ // can't announce without an sdp
+}
+
+void RCFSourceInfo::AllocateOutputArray(UInt32 numOutputs)
+{
+ // Allocate the proper number of relay outputs
+ fOutputArray = NEW OutputInfo[numOutputs];
+}
diff --git a/APIModules/QTSSReflectorModule/RCFSourceInfo.h b/APIModules/QTSSReflectorModule/RCFSourceInfo.h
new file mode 100644
index 0000000..f2b1ba9
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/RCFSourceInfo.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * @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: RCFSourceInfo.h
+
+ Contains: This object takes input RCF data, and uses it to support the SourceInfo
+ API.
+
+
+
+*/
+
+#ifndef __RCF_SOURCE_INFO_H__
+#define __RCF_SOURCE_INFO_H__
+
+#include "StrPtrLen.h"
+#include "SourceInfo.h"
+#include "XMLParser.h"
+#include "StringParser.h"
+
+class RCFSourceInfo : public SourceInfo
+{
+ public:
+
+ // Uses the SDP Data to build up the StreamInfo structures
+ RCFSourceInfo() : fName(NULL) {}
+ RCFSourceInfo(XMLTag* relayTag) : fName(NULL) { Parse(relayTag); }
+ RCFSourceInfo(const RCFSourceInfo& copy):SourceInfo(copy) { this->SetName(copy.fName); }
+ virtual ~RCFSourceInfo();
+
+ // Parses out the SDP file provided, sets up the StreamInfo structures
+ void Parse(XMLTag* relayTag);
+
+ // Parses relay_destination lines and builds OutputInfo structs
+ void ParseRelayDestinations(XMLTag* relayTag);
+
+ void SetName(const char* inName);
+ char* Name() { return fName; }
+
+ protected:
+ virtual void ParseDestination(XMLTag* destTag, UInt32 index);
+ virtual void ParseAnnouncedDestination(XMLTag* destTag, UInt32 index);
+ virtual void AllocateOutputArray(UInt32 numOutputs);
+
+ char* fName;
+
+};
+#endif // __SDP_SOURCE_INFO_H__
+
+
diff --git a/APIModules/QTSSReflectorModule/RTPSessionOutput.cpp b/APIModules/QTSSReflectorModule/RTPSessionOutput.cpp
new file mode 100644
index 0000000..91aaa71
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/RTPSessionOutput.cpp
@@ -0,0 +1,779 @@
+/*
+ *
+ * @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: RTSPReflectorOutput.cpp
+
+ Contains: Implementation of object in .h file
+
+
+*/
+
+
+#include "RTPSessionOutput.h"
+#include "ReflectorStream.h"
+
+#include
+
+
+#if DEBUG
+#define RTP_SESSION_DEBUGGING 0
+#else
+#define RTP_SESSION_DEBUGGING 0
+#endif
+
+// ATTRIBUTES
+static QTSS_AttributeID sStreamPacketCountAttr = qtssIllegalAttrID;
+
+
+static QTSS_AttributeID sNextSeqNumAttr = qtssIllegalAttrID;
+static QTSS_AttributeID sSeqNumOffsetAttr = qtssIllegalAttrID;
+static QTSS_AttributeID sLastQualityChangeAttr = qtssIllegalAttrID;
+static QTSS_AttributeID sLastRTPPacketIDAttr = qtssIllegalAttrID;
+static QTSS_AttributeID sLastRTCPPacketIDAttr = qtssIllegalAttrID;
+
+static QTSS_AttributeID sFirstRTCPCurrentTimeAttr = qtssIllegalAttrID;
+static QTSS_AttributeID sFirstRTCPArrivalTimeAttr = qtssIllegalAttrID;
+static QTSS_AttributeID sFirstRTCPTimeStampAttr = qtssIllegalAttrID;
+
+static QTSS_AttributeID sFirstRTPCurrentTimeAttr = qtssIllegalAttrID;
+static QTSS_AttributeID sFirstRTPArrivalTimeAttr = qtssIllegalAttrID;
+static QTSS_AttributeID sFirstRTPTimeStampAttr = qtssIllegalAttrID;
+
+static QTSS_AttributeID sBaseRTPTimeStampAttr = qtssIllegalAttrID;
+
+static QTSS_AttributeID sBaseArrivalTimeStampAttr = qtssIllegalAttrID;
+static QTSS_AttributeID sStreamSSRCAttr = qtssIllegalAttrID;
+
+static QTSS_AttributeID sStreamByteCountAttr = qtssIllegalAttrID;
+
+static QTSS_AttributeID sLastRTPTimeStampAttr = qtssIllegalAttrID;
+
+static QTSS_AttributeID sLastRTCPTransmitAttr = qtssIllegalAttrID;
+
+RTPSessionOutput::RTPSessionOutput(QTSS_ClientSessionObject inClientSession, ReflectorSession* inReflectorSession,
+ QTSS_Object serverPrefs, QTSS_AttributeID inCookieAddrID)
+: fClientSession(inClientSession),
+ fReflectorSession(inReflectorSession),
+ fCookieAttrID(inCookieAddrID),
+ fBufferDelayMSecs(ReflectorStream::sOverBufferInMsec),
+ fBaseArrivalTime(0),
+ fIsUDP(false),
+ fTransportInitialized(false),
+ fMustSynch(true),
+ fPreFilter(true)
+{
+ // create a bookmark for each stream we'll reflect
+ this->InititializeBookmarks( inReflectorSession->GetNumStreams() );
+
+}
+
+void RTPSessionOutput::Register()
+{
+ // Add some attributes to QTSS_RTPStream dictionary
+ static char* sNextSeqNum = "qtssNextSeqNum";
+ static char* sSeqNumOffset = "qtssSeqNumOffset";
+ static char* sLastQualityChange = "qtssLastQualityChange";
+
+ static char* sLastRTPPacketID = "qtssReflectorStreamLastRTPPacketID";
+ static char* sLastRTCPPacketID = "qtssReflectorStreamLastRTCPPacketID";
+
+
+ static char* sFirstRTCPArrivalTime = "qtssReflectorStreamStartRTCPArrivalTime";
+ static char* sFirstRTCPTimeStamp = "qtssReflectorStreamStartRTCPTimeStamp";
+ static char* sFirstRTCPCurrentTime = "qtssReflectorStreamStartRTCPCurrent";
+
+ static char* sFirstRTPArrivalTime = "qtssReflectorStreamStartRTPArrivalTime";
+ static char* sFirstRTPTimeStamp = "qtssReflectorStreamStartRTPTimeStamp";
+ static char* sFirstRTPCurrentTime = "qtssReflectorStreamStartRTPCurrent";
+
+ static char* sBaseRTPTimeStamp = "qtssReflectorStreamBaseRTPTimeStamp";
+ static char* sBaseArrivalTimeStamp = "qtssReflectorStreamBaseArrivalTime";
+
+ static char* sLastRTPTimeStamp = "qtssReflectorStreamLastRTPTimeStamp";
+ static char* sLastRTCPTransmit = "qtssReflectorStreamLastRTCPTransmit";
+
+ static char* sStreamSSRC = "qtssReflectorStreamSSRC";
+ static char* sStreamPacketCount = "qtssReflectorStreamPacketCount";
+ static char* sStreamByteCount = "qtssReflectorStreamByteCount";
+
+
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sLastRTCPTransmit, NULL, qtssAttrDataTypeUInt16);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sLastRTCPTransmit, &sLastRTCPTransmitAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sNextSeqNum, NULL, qtssAttrDataTypeUInt16);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sNextSeqNum, &sNextSeqNumAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sSeqNumOffset, NULL, qtssAttrDataTypeUInt16);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sSeqNumOffset, &sSeqNumOffsetAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sLastQualityChange, NULL, qtssAttrDataTypeSInt64);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sLastQualityChange, &sLastQualityChangeAttr);
+
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sLastRTPPacketID, NULL, qtssAttrDataTypeUInt64);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sLastRTPPacketID, &sLastRTPPacketIDAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sLastRTCPPacketID, NULL, qtssAttrDataTypeUInt64);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sLastRTCPPacketID, &sLastRTCPPacketIDAttr);
+
+
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sLastRTPTimeStamp, NULL, qtssAttrDataTypeUInt32);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sLastRTPTimeStamp, &sLastRTPTimeStampAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sFirstRTCPArrivalTime, NULL, qtssAttrDataTypeSInt64);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sFirstRTCPArrivalTime, &sFirstRTCPArrivalTimeAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sFirstRTCPTimeStamp, NULL, qtssAttrDataTypeUInt32);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sFirstRTCPTimeStamp, &sFirstRTCPTimeStampAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sFirstRTCPCurrentTime, NULL, qtssAttrDataTypeUInt32);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sFirstRTCPCurrentTime, &sFirstRTCPCurrentTimeAttr);
+
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sFirstRTPCurrentTime, NULL, qtssAttrDataTypeSInt64);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sFirstRTPCurrentTime, &sFirstRTPCurrentTimeAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sFirstRTPArrivalTime, NULL, qtssAttrDataTypeSInt64);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sFirstRTPArrivalTime, &sFirstRTPArrivalTimeAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sFirstRTPTimeStamp, NULL, qtssAttrDataTypeUInt32);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sFirstRTPTimeStamp, &sFirstRTPTimeStampAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sBaseRTPTimeStamp, NULL, qtssAttrDataTypeUInt32);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sBaseRTPTimeStamp, &sBaseRTPTimeStampAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sBaseArrivalTimeStamp, NULL, qtssAttrDataTypeVoidPointer);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sBaseArrivalTimeStamp, &sBaseArrivalTimeStampAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sStreamSSRC, NULL, qtssAttrDataTypeVoidPointer);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sStreamSSRC, &sStreamSSRCAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sStreamPacketCount, NULL, qtssAttrDataTypeUInt32);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sStreamPacketCount, &sStreamPacketCountAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sStreamByteCount, NULL, qtssAttrDataTypeUInt32);
+ (void)QTSS_IDForAttr(qtssRTPStreamObjectType, sStreamByteCount, &sStreamByteCountAttr);
+
+}
+
+Bool16 RTPSessionOutput::IsPlaying()
+{
+ QTSS_RTPSessionState* theState = NULL;
+ UInt32 theLen = 0;
+
+ if (!fClientSession)
+ return false;
+
+ (void)QTSS_GetValuePtr(fClientSession, qtssCliSesState, 0, (void**)&theState, &theLen);
+ if (theLen == 0 || theState == NULL || *theState != qtssPlayingState)
+ return false;
+
+
+ return true;
+}
+
+void RTPSessionOutput::InitializeStreams()
+{
+
+ UInt32 theLen = 0;
+ QTSS_RTPStreamObject* theStreamPtr = NULL;
+ UInt32 packetCountInitValue = 0;
+
+ for (SInt16 z = 0; QTSS_GetValuePtr(fClientSession, qtssCliSesStreamObjects, z, (void**)&theStreamPtr, &theLen) == QTSS_NoErr; z++)
+ {
+ (void) QTSS_SetValue(*theStreamPtr, sStreamPacketCountAttr, 0, &packetCountInitValue, sizeof(UInt32));
+ }
+
+}
+
+
+
+Bool16 RTPSessionOutput::IsUDP()
+{
+ if (fTransportInitialized)
+ return fIsUDP;
+
+
+ QTSS_RTPSessionState* theState = NULL;
+ UInt32 theLen = 0;
+ (void)QTSS_GetValuePtr(fClientSession, qtssCliSesState, 0, (void**)&theState, &theLen);
+ if (*theState != qtssPlayingState)
+ return true;
+
+ QTSS_RTPStreamObject *theStreamPtr = NULL;
+ QTSS_RTPTransportType *theTransportTypePtr = NULL;
+ for (SInt16 z = 0; QTSS_GetValuePtr(fClientSession, qtssCliSesStreamObjects, z, (void**)&theStreamPtr, &theLen) == QTSS_NoErr; z++)
+ {
+ (void) QTSS_GetValuePtr(*theStreamPtr, qtssRTPStrTransportType, 0, (void**) &theTransportTypePtr, &theLen);
+ if (theTransportTypePtr && *theTransportTypePtr == qtssRTPTransportTypeUDP)
+ {
+ fIsUDP = true;
+ break; // treat entire session UDP
+ }
+ else
+ {
+ fIsUDP = false;
+ }
+ }
+
+ //if (fIsUDP) printf("RTPSessionOutput::RTPSessionOutput Standard UDP client\n");
+ //else printf("RTPSessionOutput::RTPSessionOutput Buffered Client\n");
+
+ fTransportInitialized = true;
+ return fIsUDP;
+}
+
+
+Bool16 RTPSessionOutput::FilterPacket(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacket)
+{
+
+ UInt32* packetCountPtr = NULL;
+ UInt32 theLen = 0;
+
+ //see if we started sending and if so then just keep sending (reset on a play)
+ QTSS_Error writeErr = QTSS_GetValuePtr(*theStreamPtr, sStreamPacketCountAttr, 0,(void**) &packetCountPtr,&theLen);
+ if (writeErr == QTSS_NoErr && theLen > 0 && *packetCountPtr > 0)
+ return false;
+
+ Assert(theStreamPtr);
+ Assert(inPacket);
+
+ UInt16 seqnum = this->GetPacketSeqNumber(inPacket);
+ UInt16 firstSeqNum = 0;
+ theLen = sizeof(firstSeqNum);
+
+ if ( QTSS_NoErr != QTSS_GetValue(*theStreamPtr, qtssRTPStrFirstSeqNumber, 0, &firstSeqNum, &theLen) )
+ return true;
+
+ if ( seqnum < firstSeqNum )
+ {
+ //printf("RTPSessionOutput::FilterPacket don't send packet = %u < first=%lu\n", seqnum, firstSeqNum);
+ return true;
+ }
+
+ //printf("RTPSessionOutput::FilterPacket found first packet = %u \n", firstSeqNum);
+
+ fPreFilter = false;
+ return fPreFilter;
+}
+
+
+Bool16 RTPSessionOutput::PacketAlreadySent(QTSS_RTPStreamObject *theStreamPtr, UInt32 inFlags, UInt64* packetIDPtr)
+{
+ Assert(theStreamPtr);
+ Assert(packetIDPtr);
+
+ UInt32 theLen = 0;
+ UInt64 *lastPacketIDPtr = NULL;
+ Bool16 packetSent = false;
+
+ if (inFlags & qtssWriteFlagsIsRTP)
+ {
+ if ( (QTSS_NoErr == QTSS_GetValuePtr(*theStreamPtr, sLastRTPPacketIDAttr, 0, (void**)&lastPacketIDPtr, &theLen))
+ && (*packetIDPtr <= *lastPacketIDPtr)
+ )
+ {
+ //printf("RTPSessionOutput::WritePacket Don't send RTP packet id =%qu\n", *packetIDPtr);
+ packetSent = true;
+ }
+
+ } else if (inFlags & qtssWriteFlagsIsRTCP)
+ {
+ if ( QTSS_NoErr == QTSS_GetValuePtr(*theStreamPtr, sLastRTCPPacketIDAttr, 0, (void**)&lastPacketIDPtr, &theLen)
+ && (*packetIDPtr <= *lastPacketIDPtr)
+ )
+ {
+ //printf("RTPSessionOutput::WritePacket Don't send RTCP packet id =%qu last packet sent id =%qu\n", *packetIDPtr,*lastPacketIDPtr);
+ packetSent = true;
+ }
+ }
+
+ return packetSent;
+}
+
+Bool16 RTPSessionOutput::PacketReadyToSend(QTSS_RTPStreamObject *theStreamPtr,SInt64 *currentTimePtr, UInt32 inFlags, UInt64* packetIDPtr, SInt64* timeToSendThisPacketAgainPtr)
+{
+ return true;
+
+}
+
+QTSS_Error RTPSessionOutput::TrackRTCPBaseTime(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64* packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr)
+{
+ Bool16 haveBaseTime = false;
+ Bool16 haveAllFirstRTPs = true;
+
+ UInt32 streamTimeScale = 0;
+ UInt32 theLen = sizeof(streamTimeScale);
+ QTSS_Error writeErr = QTSS_GetValue(*theStreamPtr, qtssRTPStrTimescale, 0, (void *) &streamTimeScale, &theLen);
+ Assert(writeErr == QTSS_NoErr);
+
+ UInt32 baseTimeStamp = 0;
+ theLen = sizeof(baseTimeStamp);
+ if (!fMustSynch || QTSS_NoErr == QTSS_GetValue(*theStreamPtr, sBaseRTPTimeStampAttr, 0, (void*)&baseTimeStamp, &theLen) ) // we need a starting stream time that is synched
+ {
+ haveBaseTime = true;
+ }
+ else
+ {
+ UInt64 earliestArrivalTime = ~(UInt64) 0; //max value
+ UInt32 firstStreamTime = 0;
+ SInt64 firstStreamArrivalTime = 0;
+ QTSS_RTPStreamObject *findStream = NULL;
+
+ if (fMustSynch || QTSS_NoErr != QTSS_GetValuePtr(*theStreamPtr, sBaseArrivalTimeStampAttr, 0, (void**)&fBaseArrivalTime, &theLen) )
+ { // we don't have a base arrival time for the session see if we can set one now.
+
+ for (SInt32 z = 0; QTSS_GetValuePtr(fClientSession, qtssCliSesStreamObjects, z, (void**)&findStream, &theLen) == QTSS_NoErr; z++)
+ {
+ SInt64* firstArrivalTimePtr = NULL;
+ if (QTSS_NoErr != QTSS_GetValuePtr(*findStream, sFirstRTPArrivalTimeAttr, 0, (void**)&firstArrivalTimePtr, &theLen))
+ {// no packet on this stream yet
+ haveAllFirstRTPs = false; // not enough info to calc a base time
+ break;
+ }
+ else
+ { // we have an arrival time see if it is the first for all streams
+ if ( (UInt64) *firstArrivalTimePtr < earliestArrivalTime )
+ {
+ earliestArrivalTime = *firstArrivalTimePtr;
+ }
+ }
+
+ }
+
+ if (haveAllFirstRTPs) // we can now create a base arrival time and base stream time from that
+ {
+
+ writeErr = QTSS_SetValue(*theStreamPtr, sBaseArrivalTimeStampAttr, 0, &earliestArrivalTime, sizeof(SInt64));
+ Assert(writeErr == QTSS_NoErr);
+ fBaseArrivalTime = (SInt64) earliestArrivalTime;
+ }
+ }
+
+ if (haveAllFirstRTPs)//sBaseRTPTimeStamp
+ { // we don't have a base stream time but we have a base session time so calculate the base stream time.
+ theLen = sizeof(firstStreamTime);
+ if (QTSS_NoErr != QTSS_GetValue(*theStreamPtr, sFirstRTPTimeStampAttr, 0, (void*)&firstStreamTime, &theLen))
+ return QTSS_NoErr;
+
+ theLen = sizeof(firstStreamArrivalTime);
+ if (QTSS_NoErr != QTSS_GetValue(*theStreamPtr, sFirstRTPArrivalTimeAttr, 0, (void*)&firstStreamArrivalTime, &theLen))
+ return QTSS_NoErr;
+
+ SInt64 arrivalTimeDiffMSecs = (firstStreamArrivalTime - fBaseArrivalTime);// + fBufferDelayMSecs;//add the buffer delay !! not sure about faster than real time arrival times....
+ UInt32 timeDiffStreamTime = (UInt32)( ( (Float64) arrivalTimeDiffMSecs/(Float64) 1000.0) * (Float64) streamTimeScale );
+ baseTimeStamp = firstStreamTime - timeDiffStreamTime;
+ if (QTSS_NoErr == QTSS_SetValue(*theStreamPtr, sBaseRTPTimeStampAttr, 0, (void*)&baseTimeStamp, sizeof(baseTimeStamp)))
+ haveBaseTime = true;
+
+ (void) QTSS_SetValue(*theStreamPtr, qtssRTPStrFirstTimestamp, 0, &baseTimeStamp, sizeof(baseTimeStamp));
+
+ fMustSynch = false;
+ //printf("fBaseArrivalTime =%qd baseTimeStamp %"_U32BITARG_" streamStartTime=%qd diff =%qd\n", fBaseArrivalTime, baseTimeStamp, firstStreamArrivalTime, arrivalTimeDiffMSecs);
+ }
+ }
+
+ return writeErr;
+
+}
+
+QTSS_Error RTPSessionOutput::RewriteRTCP(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64* packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr)
+{
+ UInt32 theLen;
+
+ SInt64 firstRTPCurrentTime = 0;
+ theLen = sizeof(firstRTPCurrentTime);
+ QTSS_GetValue(*theStreamPtr, sFirstRTPCurrentTimeAttr, 0, (void*)&firstRTPCurrentTime, &theLen);
+
+ SInt64 firstRTPArrivalTime = 0;
+ theLen = sizeof(firstRTPArrivalTime);
+ QTSS_GetValue(*theStreamPtr, sFirstRTPArrivalTimeAttr, 0, (void*)&firstRTPArrivalTime, &theLen);
+
+
+ UInt32 rtpTime = 0;
+ theLen = sizeof(rtpTime);
+ QTSS_GetValue(*theStreamPtr, sFirstRTPTimeStampAttr, 0, (void*)&rtpTime, &theLen);
+
+
+ UInt32* theReport = (UInt32*) inPacketStrPtr->Ptr;
+ theReport+=2; // point to the NTP time stamp
+ SInt64* theNTPTimestampP = (SInt64*)theReport;
+ *theNTPTimestampP = OS::HostToNetworkSInt64(OS::TimeMilli_To_1900Fixed64Secs(*currentTimePtr)); // time now
+
+ UInt32 baseTimeStamp = 0;
+ theLen = sizeof(baseTimeStamp);
+ (void) QTSS_GetValue(*theStreamPtr, sBaseRTPTimeStampAttr, 0, (void*)&baseTimeStamp, &theLen); // we need a starting stream time that is synched
+
+ UInt32 streamTimeScale = 0;
+ theLen = sizeof(streamTimeScale);
+ QTSS_GetValue(*theStreamPtr, qtssRTPStrTimescale, 0, (void *) &streamTimeScale, &theLen);
+
+ SInt64 packetOffset = *currentTimePtr - fBaseArrivalTime; // real time that has passed
+ packetOffset -= (firstRTPCurrentTime - firstRTPArrivalTime); // less the initial buffer delay for this stream
+ if (packetOffset < 0)
+ packetOffset = 0;
+
+ Float64 rtpTimeFromStart = (Float64) packetOffset / (Float64) 1000.0;
+ UInt32 rtpTimeFromStartInScale = (UInt32) (Float64) ((Float64) streamTimeScale * rtpTimeFromStart);
+ //printf("rtptime offset time =%f in scale =%"_U32BITARG_"\n", rtpTimeFromStart, rtpTimeFromStartInScale );
+
+ theReport += 2; // point to the rtp time stamp of "now" synched and scaled in stream time
+ *theReport = htonl(baseTimeStamp + rtpTimeFromStartInScale);
+
+ theLen = sizeof(UInt32);
+ UInt32 packetCount = 0;
+ (void) QTSS_GetValue(*theStreamPtr, sStreamPacketCountAttr, 0, &packetCount,&theLen);
+ theReport += 1; // point to the rtp packets sent
+ *theReport = htonl(ntohl(*theReport) * 2);
+
+ UInt32 byteCount = 0;
+ (void) QTSS_GetValue(*theStreamPtr, sStreamByteCountAttr, 0, &byteCount,&theLen);
+ theReport += 1; // point to the rtp payload bytes sent
+ *theReport = htonl(ntohl(*theReport) * 2);
+
+ return QTSS_NoErr;
+}
+
+QTSS_Error RTPSessionOutput::TrackRTCPPackets(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64* packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr)
+{
+ QTSS_Error writeErr = QTSS_NoErr;
+
+ Assert(inFlags & qtssWriteFlagsIsRTCP);
+
+ if (!(inFlags & qtssWriteFlagsIsRTCP))
+ return -1;
+
+ this->TrackRTCPBaseTime(theStreamPtr,inPacketStrPtr,currentTimePtr,inFlags, packetLatenessInMSec,timeToSendThisPacketAgain, packetIDPtr,arrivalTimeMSecPtr);
+
+ this->RewriteRTCP(theStreamPtr,inPacketStrPtr,currentTimePtr,inFlags, packetLatenessInMSec,timeToSendThisPacketAgain, packetIDPtr,arrivalTimeMSecPtr);
+
+
+ return writeErr;
+}
+
+QTSS_Error RTPSessionOutput::TrackRTPPackets(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64* packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr)
+{
+ QTSS_Error writeErr = QTSS_NoErr;
+
+ Assert(inFlags & qtssWriteFlagsIsRTP);
+
+ if (!(inFlags & qtssWriteFlagsIsRTP))
+ return QTSS_NoErr;
+
+ ReflectorPacket packetContainer;
+ packetContainer.SetPacketData(inPacketStrPtr->Ptr, inPacketStrPtr->Len);
+ packetContainer.fIsRTCP = false;
+ SInt64 *theTimePtr = NULL;
+ UInt32 theLen = 0;
+
+ if (QTSS_NoErr != QTSS_GetValuePtr(*theStreamPtr, sFirstRTPArrivalTimeAttr, 0, (void**)&theTimePtr, &theLen))
+ {
+ UInt32 theSSRC = packetContainer.GetSSRC(packetContainer.fIsRTCP);
+ (void) QTSS_SetValue(*theStreamPtr, sStreamSSRCAttr, 0, &theSSRC, sizeof(theSSRC));
+
+ UInt32 rtpTime = packetContainer.GetPacketRTPTime();
+ writeErr = QTSS_SetValue(*theStreamPtr, sFirstRTPTimeStampAttr, 0, &rtpTime, sizeof(rtpTime));
+ Assert(writeErr == QTSS_NoErr);
+
+ writeErr = QTSS_SetValue(*theStreamPtr, sFirstRTPArrivalTimeAttr, 0, arrivalTimeMSecPtr, sizeof(SInt64));
+ Assert(writeErr == QTSS_NoErr);
+
+ writeErr = QTSS_SetValue(*theStreamPtr, sFirstRTPCurrentTimeAttr, 0, currentTimePtr, sizeof(SInt64));
+ Assert(writeErr == QTSS_NoErr);
+
+ UInt32 initValue = 0;
+ writeErr = QTSS_SetValue(*theStreamPtr, sStreamByteCountAttr, 0, &initValue, sizeof(UInt32));
+ Assert(writeErr == QTSS_NoErr);
+
+ //printf("first rtp on stream stream=%"_U32BITARG_" ssrc=%"_U32BITARG_" rtpTime=%"_U32BITARG_" arrivalTimeMSecPtr=%qd currentTime=%qd\n",(UInt32) theStreamPtr, theSSRC, rtpTime, *arrivalTimeMSecPtr, *currentTimePtr);
+
+ }
+ else
+ {
+ UInt32* packetCountPtr = NULL;
+ UInt32* byteCountPtr = NULL;
+ UInt32 theLen = 0;
+
+ writeErr = QTSS_GetValuePtr(*theStreamPtr, sStreamByteCountAttr, 0, (void**) &byteCountPtr,&theLen);
+ if (writeErr == QTSS_NoErr && theLen > 0)
+ *byteCountPtr += inPacketStrPtr->Len - 12;// 12 header bytes
+
+
+ UInt32* theSSRCPtr = 0;
+ (void) QTSS_GetValuePtr(*theStreamPtr, sStreamSSRCAttr, 0, (void**)&theSSRCPtr, &theLen);
+ if (*theSSRCPtr != packetContainer.GetSSRC(packetContainer.fIsRTCP))
+ {
+
+
+ (void) QTSS_RemoveValue(*theStreamPtr,sFirstRTPArrivalTimeAttr,0);
+ (void) QTSS_RemoveValue(*theStreamPtr,sFirstRTPTimeStampAttr,0);
+ (void) QTSS_RemoveValue(*theStreamPtr,sFirstRTPCurrentTimeAttr,0);
+ (void) QTSS_RemoveValue(*theStreamPtr,sStreamPacketCountAttr,0);
+ (void) QTSS_RemoveValue(*theStreamPtr,sStreamByteCountAttr,0);
+ fMustSynch = true;
+
+ //printf("found different ssrc =%"_U32BITARG_" packetssrc=%"_U32BITARG_"\n",*theSSRCPtr, packetContainer.GetSSRC(packetContainer.fIsRTCP));
+
+ }
+
+
+
+ }
+
+ return writeErr;
+
+}
+
+QTSS_Error RTPSessionOutput::TrackPackets(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64* packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr)
+{
+ if (this->IsUDP())
+ return QTSS_NoErr;
+
+ if (inFlags & qtssWriteFlagsIsRTCP)
+ (void) this->TrackRTCPPackets(theStreamPtr, inPacketStrPtr, currentTimePtr,inFlags, packetLatenessInMSec, timeToSendThisPacketAgain, packetIDPtr,arrivalTimeMSecPtr);
+ else if (inFlags & qtssWriteFlagsIsRTP)
+ (void) this->TrackRTPPackets(theStreamPtr, inPacketStrPtr, currentTimePtr,inFlags, packetLatenessInMSec, timeToSendThisPacketAgain, packetIDPtr,arrivalTimeMSecPtr);
+
+ return QTSS_NoErr;
+}
+
+
+QTSS_Error RTPSessionOutput::WritePacket(StrPtrLen* inPacket, void* inStreamCookie, UInt32 inFlags, SInt64 packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr, Bool16 firstPacket)
+{
+ QTSS_RTPSessionState* theState = NULL;
+ UInt32 theLen = 0;
+ QTSS_Error writeErr = QTSS_NoErr;
+ SInt64 currentTime = OS::Milliseconds();
+
+ if (inPacket == NULL || inPacket->Len == 0)
+ return QTSS_NoErr;
+
+
+ (void)QTSS_GetValuePtr(fClientSession, qtssCliSesState, 0, (void**)&theState, &theLen);
+ if (theLen == 0 || theState == NULL || *theState != qtssPlayingState)
+ { //qtss_printf("QTSS_WouldBlock *theState=%d qtssPlayingState=%d\n", *theState , qtssPlayingState);
+ return QTSS_WouldBlock;
+ }
+
+ //make sure all RTP streams with this ID see this packet
+ QTSS_RTPStreamObject *theStreamPtr = NULL;
+
+ for (UInt32 z = 0; QTSS_GetValuePtr(fClientSession, qtssCliSesStreamObjects, z, (void**)&theStreamPtr, &theLen) == QTSS_NoErr; z++)
+ {
+ if (this->PacketMatchesStream(inStreamCookie, theStreamPtr))
+ {
+ if ( (inFlags & qtssWriteFlagsIsRTP) && this->FilterPacket(theStreamPtr, inPacket) )
+ return QTSS_NoErr; // keep looking at packets
+
+ if (this->PacketAlreadySent(theStreamPtr,inFlags, packetIDPtr))
+ return QTSS_NoErr; // keep looking at packets
+
+ if (!this->PacketReadyToSend(theStreamPtr,¤tTime, inFlags, packetIDPtr, timeToSendThisPacketAgain))
+ { //qtss_printf("QTSS_WouldBlock\n");
+ return QTSS_WouldBlock; // stop not ready to send packets now
+ }
+
+
+ // TrackPackets below is for re-writing the rtcps we don't use it right now-- shouldn't need to
+ // (void) this->TrackPackets(theStreamPtr, inPacket, ¤tTime,inFlags, &packetLatenessInMSec, timeToSendThisPacketAgain, packetIDPtr,arrivalTimeMSecPtr);
+
+ QTSS_PacketStruct thePacket;
+ thePacket.packetData = inPacket->Ptr;
+ SInt64 delayMSecs = fBufferDelayMSecs - (currentTime - *arrivalTimeMSecPtr);
+ thePacket.packetTransmitTime = (currentTime - packetLatenessInMSec);
+ if (fBufferDelayMSecs > 0 )
+ thePacket.packetTransmitTime += delayMSecs; // add buffer time where oldest buffered packet as now == 0 and newest is entire buffer time in the future.
+
+ writeErr = QTSS_Write(*theStreamPtr, &thePacket, inPacket->Len, NULL, inFlags | qtssWriteFlagsWriteBurstBegin);
+ if (writeErr == QTSS_WouldBlock)
+ {
+ //qtss_printf("QTSS_Write == QTSS_WouldBlock\n");
+ //
+ // We are flow controlled. See if we know when flow control will be lifted and report that
+ *timeToSendThisPacketAgain = thePacket.suggestedWakeupTime;
+
+ if (firstPacket)
+ { fBufferDelayMSecs = (currentTime - *arrivalTimeMSecPtr);
+ //qtss_printf("firstPacket fBufferDelayMSecs =%lu \n", fBufferDelayMSecs);
+ }
+ }
+ else
+ {
+ fLastIntervalMilliSec = currentTime - fLastPacketTransmitTime;
+ if (fLastIntervalMilliSec > 100) //reset interval maybe first packet or it has been blocked for awhile
+ fLastIntervalMilliSec = 5;
+ fLastPacketTransmitTime = currentTime;
+
+ if (inFlags & qtssWriteFlagsIsRTP)
+ {
+ (void) QTSS_SetValue (*theStreamPtr, sLastRTPPacketIDAttr, 0, packetIDPtr, sizeof(UInt64));
+ }
+ else if (inFlags & qtssWriteFlagsIsRTCP)
+ {
+ (void) QTSS_SetValue (*theStreamPtr, sLastRTCPPacketIDAttr, 0, packetIDPtr, sizeof(UInt64));
+ (void) QTSS_SetValue (*theStreamPtr, sLastRTCPTransmitAttr, 0, ¤tTime, sizeof(UInt64));
+ }
+
+
+
+ { // increment packet counts
+ UInt32* packetCountPtr = NULL;
+ UInt32 theLen = 0;
+
+ (void) QTSS_GetValuePtr(*theStreamPtr, sStreamPacketCountAttr, 0,(void**) &packetCountPtr,&theLen);
+ if (theLen > 0)
+ { *packetCountPtr += 1;
+ //printf("SET sStreamPacketCountAttr =%lu\n", *packetCountPtr);
+ }
+ }
+ }
+ }
+
+ if ( writeErr != QTSS_NoErr )
+ break;
+ }
+
+ return writeErr;
+}
+
+UInt16 RTPSessionOutput::GetPacketSeqNumber(StrPtrLen* inPacket)
+{
+ if (inPacket->Len < 4)
+ return 0;
+
+ //The RTP seq number is the second short of the packet
+ UInt16* seqNumPtr = (UInt16*)inPacket->Ptr;
+ return ntohs(seqNumPtr[1]);
+}
+
+void RTPSessionOutput::SetPacketSeqNumber(StrPtrLen* inPacket, UInt16 inSeqNumber)
+{
+ if (inPacket->Len < 4)
+ return;
+
+ //The RTP seq number is the second short of the packet
+ UInt16* seqNumPtr = (UInt16*)inPacket->Ptr;
+ seqNumPtr[1] = htons(inSeqNumber);
+}
+
+// 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;
+ }
+}
+
+void RTPSessionOutput::TearDown()
+{
+ QTSS_CliSesTeardownReason reason = qtssCliSesTearDownBroadcastEnded;
+ (void)QTSS_SetValue(fClientSession, qtssCliTeardownReason, 0, &reason, sizeof(reason));
+ (void)QTSS_Teardown(fClientSession);
+}
+
+
diff --git a/APIModules/QTSSReflectorModule/RTPSessionOutput.h b/APIModules/QTSSReflectorModule/RTPSessionOutput.h
new file mode 100644
index 0000000..1124fee
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/RTPSessionOutput.h
@@ -0,0 +1,111 @@
+/*
+ *
+ * @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: RTSPReflectorOutput.h
+
+ Contains: Derived from ReflectorOutput, this implements the WritePacket
+ method in terms of the QTSS API (that is, it writes to a client
+ using the QTSS_RTPSessionObject
+
+
+
+*/
+
+#ifndef __RTSP_REFLECTOR_OUTPUT_H__
+#define __RTSP_REFLECTOR_OUTPUT_H__
+
+#include "ReflectorOutput.h"
+#include "ReflectorSession.h"
+#include "QTSS.h"
+
+class RTPSessionOutput : public ReflectorOutput
+{
+ public:
+
+ // Adds some dictionary attributes
+ static void Register();
+
+ RTPSessionOutput(QTSS_ClientSessionObject inRTPSession, ReflectorSession* inReflectorSession,
+ QTSS_Object serverPrefs, QTSS_AttributeID inCookieAddrID);
+ virtual ~RTPSessionOutput() {}
+
+ ReflectorSession* GetReflectorSession() { return fReflectorSession; }
+ void InitializeStreams();
+
+ // This writes the packet out to the proper QTSS_RTPStreamObject.
+ // If this function returns QTSS_WouldBlock, timeToSendThisPacketAgain will
+ // be set to # of msec in which the packet can be sent, or -1 if unknown
+ virtual QTSS_Error WritePacket(StrPtrLen* inPacketData, void* inStreamCookie, UInt32 inFlags, SInt64 packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSec,Bool16 firstPacket );
+ virtual void TearDown();
+
+ SInt64 GetReflectorSessionInitTime() { return fReflectorSession->GetInitTimeMS(); }
+
+ virtual Bool16 IsUDP();
+
+ virtual Bool16 IsPlaying();
+
+ void SetBufferDelay (UInt32 delay) { fBufferDelayMSecs = delay; }
+
+ private:
+
+ QTSS_ClientSessionObject fClientSession;
+ ReflectorSession* fReflectorSession;
+ QTSS_AttributeID fCookieAttrID;
+ UInt32 fBufferDelayMSecs;
+ SInt64 fBaseArrivalTime;
+ Bool16 fIsUDP;
+ Bool16 fTransportInitialized;
+ Bool16 fMustSynch;
+ Bool16 fPreFilter;
+
+ UInt16 GetPacketSeqNumber(StrPtrLen* inPacket);
+ void SetPacketSeqNumber(StrPtrLen* inPacket, UInt16 inSeqNumber);
+ Bool16 PacketShouldBeThinned(QTSS_RTPStreamObject inStream, StrPtrLen* inPacket);
+ Bool16 FilterPacket(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacket);
+
+ UInt32 GetPacketRTPTime(StrPtrLen* packetStrPtr);
+inline Bool16 PacketMatchesStream(void* inStreamCookie, QTSS_RTPStreamObject *theStreamPtr);
+ Bool16 PacketReadyToSend(QTSS_RTPStreamObject *theStreamPtr,SInt64 *currentTimePtr, UInt32 inFlags, UInt64* packetIDPtr, SInt64* timeToSendThisPacketAgainPtr);
+ Bool16 PacketAlreadySent(QTSS_RTPStreamObject *theStreamPtr, UInt32 inFlags, UInt64* packetIDPtr);
+ QTSS_Error TrackRTCPBaseTime(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64 *packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr);
+ QTSS_Error RewriteRTCP(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64 *packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr);
+ QTSS_Error TrackRTPPackets(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64 *packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr);
+ QTSS_Error TrackRTCPPackets(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64 *packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr);
+ QTSS_Error TrackPackets(QTSS_RTPStreamObject *theStreamPtr, StrPtrLen* inPacketStrPtr, SInt64 *currentTimePtr, UInt32 inFlags, SInt64 *packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr);
+};
+
+
+Bool16 RTPSessionOutput::PacketMatchesStream(void* inStreamCookie, QTSS_RTPStreamObject *theStreamPtr)
+{
+ void** theStreamCookie = NULL;
+ UInt32 theLen = 0;
+ (void) QTSS_GetValuePtr(*theStreamPtr, fCookieAttrID, 0, (void**)&theStreamCookie, &theLen);
+
+ if ((theStreamCookie != NULL) && (*theStreamCookie == inStreamCookie))
+ return true;
+
+ return false;
+}
+#endif //__RTSP_REFLECTOR_OUTPUT_H__
diff --git a/APIModules/QTSSReflectorModule/RTSPSourceInfo.cpp b/APIModules/QTSSReflectorModule/RTSPSourceInfo.cpp
new file mode 100644
index 0000000..ed086b4
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/RTSPSourceInfo.cpp
@@ -0,0 +1,750 @@
+/*
+ *
+ * @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: RTSPSourceInfo.cpp
+
+ Contains:
+
+
+
+*/
+
+#include "RTSPSourceInfo.h"
+#include "StringParser.h"
+#include "SDPSourceInfo.h"
+#include "OSMemory.h"
+#include "StringFormatter.h"
+#include "SocketUtils.h"
+#include "RelayOutput.h"
+#include "StringTranslator.h"
+#include "SDPUtils.h"
+#include "OSArrayObjectDeleter.h"
+
+StrPtrLen RTSPSourceInfo::sKeyString("rtsp_source");
+StrPtrLen RTSPSourceInfo::sAnnouncedKeyString("announced_rtsp_source");
+
+// StrPtrLen's for various keywords on the relay_source & relay_destination lines
+static StrPtrLen sOutAddr("out_addr");
+static StrPtrLen sDestAddr("dest_addr");
+static StrPtrLen sDestPorts("dest_ports");
+static StrPtrLen sTtl("ttl");
+
+char* RTSPOutputInfo::CopyString(const char* srcStr)
+{
+ char* dstStr = NULL;
+
+ if(srcStr != NULL)
+ {
+ UInt32 len = ::strlen(srcStr);
+ dstStr = NEW char[len + 1];
+ ::memcpy(dstStr, srcStr, len);
+ dstStr[len] = '\0';
+ }
+
+ return dstStr;
+}
+
+void RTSPOutputInfo::Copy(const RTSPOutputInfo& copy)
+{
+ fIsAnnounced = copy.fIsAnnounced;
+ fAnnouncePort = copy.fAnnouncePort;
+
+ if(copy.fDestURl != NULL)
+ fDestURl = RTSPOutputInfo::CopyString(copy.fDestURl);
+ if(copy.fUserName != NULL)
+ fUserName = RTSPOutputInfo::CopyString(copy.fUserName);
+ if(copy.fPassword != NULL)
+ fPassword = RTSPOutputInfo::CopyString(copy.fPassword);
+}
+
+RTSPSourceInfo::RTSPSourceInfo(const RTSPSourceInfo& copy)
+ :RCFSourceInfo(copy),
+ fHostAddr(copy.fHostAddr), fHostPort(copy.fHostPort), fLocalAddr(copy.fLocalAddr),
+ fNumSetupsComplete(copy.fNumSetupsComplete), fDescribeComplete(copy.fDescribeComplete),
+ fAnnounce(copy.fAnnounce), fAnnounceIP(copy.fAnnounceIP), fAnnounceActualIP(copy.fAnnounceIP),fQueueElem()
+{
+ fQueueElem.SetEnclosingObject(this);
+ fSourceURL = RTSPOutputInfo::CopyString(copy.fSourceURL);
+ fUserName = RTSPOutputInfo::CopyString(copy.fUserName);
+ fPassword = RTSPOutputInfo::CopyString(copy.fPassword);
+ fAnnounceURL = RTSPOutputInfo::CopyString(copy.fAnnounceURL);
+
+ fRTSPInfoArray = NEW RTSPOutputInfo[fNumOutputs];
+ for (UInt32 index=0; index < fNumOutputs; index++)
+ fRTSPInfoArray[index].Copy(copy.fRTSPInfoArray[index]);
+
+ // These aren't set anyway and shouldn't be copied around
+ fClientSocket = NULL;
+ fClient = NULL;
+ fRelaySessionCreatorTask = NULL;
+ fSession = NULL;
+ fSessionQueue = NULL;
+
+ if ((copy.fLocalSDP).Ptr != NULL)
+ fLocalSDP.Set((copy.fLocalSDP).GetAsCString(), (copy.fLocalSDP).Len);
+
+}
+
+RTSPSourceInfo::~RTSPSourceInfo()
+{
+ OSMutexLocker locker(RelayOutput::GetQueueMutex());
+
+ if (fRelaySessionCreatorTask != NULL)
+ fRelaySessionCreatorTask->fInfo = NULL;
+
+ if (fDescribeComplete)
+ {
+ Assert(fClientSocket != NULL);
+ Assert(fClient != NULL);
+ // the task will delete these objects when it is done with the teardown
+ TeardownTask* task = new TeardownTask(fClientSocket, fClient);
+ task->Signal(Task::kStartEvent);
+ }
+ else
+ {
+ if (fClientSocket != NULL) delete fClientSocket;
+ if (fClient != NULL) delete fClient;
+ }
+
+ if (fQueueElem.IsMemberOfAnyQueue())
+ fQueueElem.Remove();
+
+ if (fLocalSDP.Ptr != NULL) delete fLocalSDP.Ptr;
+ fLocalSDP.Len = 0;
+
+ if (fSourceURL != NULL) delete fSourceURL;
+ if (fAnnounceURL != NULL) delete fAnnounceURL;
+
+ if (fRTSPInfoArray != NULL) delete [] fRTSPInfoArray;
+}
+
+void RTSPSourceInfo::InitClient(UInt32 inSocketType)
+{
+ fClientSocket = NEW TCPClientSocket(inSocketType);
+ fClient = NEW RTSPClient(fClientSocket, false, RelaySession::sRelayUserAgent);
+}
+
+void RTSPSourceInfo::SetClientInfo(UInt32 inAddr, UInt16 inPort, char* inURL, UInt32 inLocalAddr)
+{
+ if (fClientSocket != NULL)
+ fClientSocket->Set(inAddr, inPort);
+
+ StrPtrLen inURLPtrLen(inURL);
+
+ if (fClient != NULL)
+ fClient->Set(inURLPtrLen);
+
+ if (inLocalAddr != 0)
+ fClientSocket->GetSocket()->Bind(inLocalAddr, 0);
+}
+
+QTSS_Error RTSPSourceInfo::ParsePrefs(XMLTag* relayTag, Bool16 inAnnounce)
+{
+ XMLTag* prefTag;
+ UInt32 localAddr = 0;
+ UInt32 theHostAddr = 0;
+ UInt16 theHostPort = 554;
+ char* userName = NULL;
+ char* password = NULL;
+ StrPtrLen theURL;
+
+ fAnnounce = inAnnounce;
+
+ XMLTag* sourceTag = relayTag->GetEmbeddedTagByNameAndAttr("OBJECT", "CLASS", "source");
+ if (sourceTag == NULL)
+ return QTSS_ValueNotFound;
+
+ prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "in_addr");
+ if (prefTag != NULL)
+ {
+ char* inAddrStr = prefTag->GetValue();
+ if (inAddrStr != NULL)
+ localAddr = SocketUtils::ConvertStringToAddr(inAddrStr);
+ }
+ prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "source_addr");
+ if (prefTag != NULL)
+ {
+ char* destAddrStr = prefTag->GetValue();
+ if (destAddrStr != NULL)
+ theHostAddr = SocketUtils::ConvertStringToAddr(destAddrStr);
+ }
+ prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "rtsp_port");
+ if (prefTag != NULL)
+ {
+ char* portStr = prefTag->GetValue();
+ if (portStr != NULL)
+ theHostPort = atoi(portStr);
+ }
+ prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "name");
+ if (prefTag != NULL)
+ {
+ userName = prefTag->GetValue();
+ }
+ prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "password");
+ if (prefTag != NULL)
+ {
+ password = prefTag->GetValue();
+ }
+ prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "url");
+ char urlBuff[1024];
+ if (prefTag != NULL)
+ {
+ char* urlString = prefTag->GetValue();
+ StringTranslator::EncodeURL(urlString, strlen(urlString) + 1, urlBuff, sizeof(urlBuff));
+ theURL.Set(urlBuff);
+ }
+
+ if (fAnnounce)
+ {
+ fAnnounceURL = theURL.GetAsCString();
+ fAnnounceIP = theHostAddr;
+ }
+ else
+ {
+ fHostAddr = theHostAddr;
+ fHostPort = theHostPort;
+ fSourceURL = theURL.GetAsCString();
+ fUserName = RTSPOutputInfo::CopyString(userName);
+ fPassword = RTSPOutputInfo::CopyString(password);
+ }
+
+ return QTSS_NoErr;
+}
+
+QTSS_Error RTSPSourceInfo::Describe()
+{
+ QTSS_Error theErr = QTSS_NoErr;
+
+ if (!fDescribeComplete)
+ {
+ // Work on the describe
+ theErr = fClient->SendDescribe();
+ if (theErr != QTSS_NoErr)
+ return theErr;
+ if (fClient->GetStatus() != 200)
+ return QTSS_RequestFailed;
+
+ // If the above function returns QTSS_NoErr, we've gotten the describe response,
+ // so process it.
+ SDPSourceInfo theSourceInfo(fClient->GetContentBody(), fClient->GetContentLength());
+
+ // Copy the Source Info into our local SourceInfo.
+ fNumStreams = theSourceInfo.GetNumStreams();
+ fStreamArray = NEW StreamInfo[fNumStreams];
+
+ for (UInt32 x = 0; x < fNumStreams; x++)
+ {
+ // Copy fPayloadType, fPayloadName, fTrackID, fBufferDelay
+ fStreamArray[x].Copy(*theSourceInfo.GetStreamInfo(x));
+
+ // Copy all stream info data. Also set fSrcIPAddr to be the host addr
+ fStreamArray[x].fSrcIPAddr = fClientSocket->GetHostAddr();
+ fStreamArray[x].fDestIPAddr = fClientSocket->GetLocalAddr();
+ fStreamArray[x].fPort = 0;
+ fStreamArray[x].fTimeToLive = 0;
+ }
+ }
+
+ // Ok, describe is complete, copy out the SDP information.
+
+ fLocalSDP.Ptr = NEW char[fClient->GetContentLength() + 1];
+
+ // Look for an "a=range" line in the SDP. If there is one, remove it.
+
+ static StrPtrLen sRangeStr("a=range:");
+ StrPtrLen theSDPPtr(fClient->GetContentBody(), fClient->GetContentLength());
+ StringParser theSDPParser(&theSDPPtr);
+
+ do
+ {
+ // Loop until we reach the end of the SDP or hit a a=range line.
+ StrPtrLen theSDPLine(theSDPParser.GetCurrentPosition(), theSDPParser.GetDataRemaining());
+ if ((theSDPLine.Len > sRangeStr.Len) && (theSDPLine.NumEqualIgnoreCase(sRangeStr.Ptr, sRangeStr.Len)))
+ break;
+
+ } while (theSDPParser.GetThruEOL(NULL));
+
+ // Copy what we have so far
+ ::memcpy(fLocalSDP.Ptr, fClient->GetContentBody(), theSDPParser.GetDataParsedLen());
+ fLocalSDP.Len = theSDPParser.GetDataParsedLen();
+
+ // Skip over the range (if it exists)
+ (void)theSDPParser.GetThruEOL(NULL);
+
+ // Copy the rest of the SDP
+ ::memcpy(fLocalSDP.Ptr + fLocalSDP.Len, theSDPParser.GetCurrentPosition(), theSDPParser.GetDataRemaining());
+ fLocalSDP.Len += theSDPParser.GetDataRemaining();
+
+#define _WRITE_SDP_ 0
+
+#if _WRITE_SDP_
+ FILE* outputFile = ::fopen("rtspclient.sdp", "w");
+ if (outputFile != NULL)
+ {
+ fLocalSDP.Ptr[fLocalSDP.Len] = '\0';
+ qtss_fprintf(outputFile, "%s", fLocalSDP.Ptr);
+ ::fclose(outputFile);
+ qtss_printf("Wrote sdp to rtspclient.sdp\n");
+ }
+ else
+ qtss_printf("Failed to write sdp\n");
+#endif
+ fDescribeComplete = true;
+ return QTSS_NoErr;
+}
+
+QTSS_Error RTSPSourceInfo::SetupAndPlay()
+{
+ QTSS_Error theErr = QTSS_NoErr;
+
+ // Do all the setups. This is async, so when a setup doesn't complete
+ // immediately, return an error, and we'll pick up where we left off.
+ while (fNumSetupsComplete < fNumStreams)
+ {
+ theErr = fClient->SendUDPSetup(fStreamArray[fNumSetupsComplete].fTrackID, fStreamArray[fNumSetupsComplete].fPort);
+ if (theErr != QTSS_NoErr)
+ return theErr;
+ else if (fClient->GetStatus() != 200)
+ return QTSS_RequestFailed;
+ else
+ fNumSetupsComplete++;
+ }
+
+ // We've done all the setups. Now send a play.
+ theErr = fClient->SendPlay(0);
+ if (theErr != QTSS_NoErr)
+ return theErr;
+ if (fClient->GetStatus() != 200)
+ return QTSS_RequestFailed;
+
+ return QTSS_NoErr;
+}
+
+QTSS_Error RTSPSourceInfo::Teardown()
+{
+ return (QTSS_Error)fClient->SendTeardown();
+}
+
+char* RTSPSourceInfo::GetLocalSDP(UInt32* newSDPLen)
+{
+ *newSDPLen = fLocalSDP.Len;
+ return fLocalSDP.GetAsCString();
+}
+
+char* RTSPSourceInfo::GetAnnounceSDP(UInt32 ipAddr, UInt32* newSDPLen)
+{
+ char *announceSDP = NEW char[fLocalSDP.Len * 2];
+ StringFormatter announceSDPFormatter(announceSDP, fLocalSDP.Len * 2);
+
+ StrPtrLen sdpLine;
+ StringParser sdpParser(&fLocalSDP);
+ bool added = false;
+
+ while (sdpParser.GetDataRemaining() > 0)
+ {
+ //stop when we reach an empty line.
+ sdpParser.GetThruEOL(&sdpLine);
+ if (sdpLine.Len == 0)
+ continue;
+
+ switch (*sdpLine.Ptr)
+ {
+ case 'c':
+ break; // remove any existing c lines
+ case 'm':
+ {
+ if (!added)
+ {
+ added = true;
+ // add a c line before the first m line
+ char ipStr[50];
+ char buff[50];
+ StrPtrLen temp(buff);
+
+ struct in_addr theIPAddr;
+ theIPAddr.s_addr = htonl(ipAddr);
+ SocketUtils::ConvertAddrToString(theIPAddr, &temp);
+
+ qtss_sprintf(ipStr, "c=IN IP4 %s", buff);
+ StrPtrLen tempLine(ipStr);
+ announceSDPFormatter.Put(tempLine);
+ announceSDPFormatter.PutEOL();
+ }
+
+ announceSDPFormatter.Put(sdpLine);
+ announceSDPFormatter.PutEOL();
+ break;//ignore connection information
+ }
+ default:
+ {
+ announceSDPFormatter.Put(sdpLine);
+ announceSDPFormatter.PutEOL();
+ }
+ }
+ }
+
+ *newSDPLen = (UInt32)announceSDPFormatter.GetCurrentOffset();
+ announceSDP[*newSDPLen] = 0;
+
+ StrPtrLen theSDPStr(announceSDP);
+ SDPContainer rawSDPContainer;
+ if (!rawSDPContainer.SetSDPBuffer( &theSDPStr ))
+ { return NULL; // it is screwed up do nothing the sdp will be deleted automatically
+ }
+
+ SDPLineSorter sortedSDP(&rawSDPContainer);
+ return sortedSDP.GetSortedSDPCopy(); // return a new copy of the sorted SDP
+}
+
+void RTSPSourceInfo::ParseAnnouncedDestination(XMLTag* destTag, UInt32 index)
+{
+ XMLTag* prefTag;
+
+ fRTSPInfoArray[index].fIsAnnounced = true;
+
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "out_addr");
+ if (prefTag != NULL)
+ {
+ char* outAddrStr = prefTag->GetValue();
+ if (outAddrStr != NULL)
+ fOutputArray[index].fLocalAddr = SocketUtils::ConvertStringToAddr(outAddrStr);
+ }
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "dest_addr");
+ if (prefTag != NULL)
+ {
+ char* destAddrStr = prefTag->GetValue();
+ if (destAddrStr != NULL)
+ fOutputArray[index].fDestAddr = SocketUtils::ConvertStringToAddr(destAddrStr);
+ }
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "rtsp_port");
+ if (prefTag != NULL)
+ {
+ char* portStr = prefTag->GetValue();
+ if (portStr != NULL)
+ fRTSPInfoArray[index].fAnnouncePort = atoi(portStr);
+ }
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "name");
+ if (prefTag != NULL)
+ {
+ StrPtrLen userName(prefTag->GetValue());
+ fRTSPInfoArray[index].fUserName = userName.GetAsCString();
+ }
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "url");
+ if (prefTag != NULL)
+ {
+ char urlBuff[1024];
+ char* urlString = prefTag->GetValue();
+ StringTranslator::EncodeURL(urlString, strlen(urlString) + 1, urlBuff, sizeof(urlBuff));
+ StrPtrLen destURL(urlBuff);
+ fRTSPInfoArray[index].fDestURl = destURL.GetAsCString();
+ }
+ prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "password");
+ if (prefTag != NULL)
+ {
+ StrPtrLen password(prefTag->GetValue());
+ fRTSPInfoArray[index].fPassword = password.GetAsCString();
+ }
+}
+
+void RTSPSourceInfo::AllocateOutputArray(UInt32 numOutputs)
+{
+ // Allocate the proper number of relay outputs
+ RCFSourceInfo::AllocateOutputArray(numOutputs);
+ fRTSPInfoArray = new RTSPOutputInfo[numOutputs];
+}
+
+Bool16 RTSPSourceInfo::Equal(SourceInfo* inInfo)
+{
+ if (!inInfo->IsRTSPSourceInfo())
+ return false;
+
+// RTSPSourceInfo* info = dynamic_cast(inInfo);
+ RTSPSourceInfo* info = (RTSPSourceInfo *)(inInfo);
+
+// if (info == NULL)
+// return false;
+
+ if (!fAnnounce)
+ {
+ StrPtrLen source(fSourceURL);
+ if( source.Equal(info->GetSourceURL()) && (fHostAddr == info->GetHostAddr())
+ && (fHostPort == info->GetHostPort()) )
+ return true;
+ }
+ else
+ {
+ StrPtrLen announceURL(fAnnounceURL);
+ if ((fAnnounceIP == info->GetAnnounceIP()) && announceURL.Equal(info->GetAnnounceURL()))
+ return true;
+ }
+
+ return false;
+}
+
+void RTSPSourceInfo::SetSourceParameters(UInt32 inHostAddr, UInt16 inHostPort, StrPtrLen& inURL)
+{
+ fHostAddr = inHostAddr;
+ fHostPort = inHostPort;
+ fSourceURL = inURL.GetAsCString();
+}
+
+void RTSPSourceInfo::StartSessionCreatorTask(OSQueue* inSessionQueue, OSQueue* inSourceQueue)
+{
+ InitClient(Socket::kNonBlockingSocketType);
+ SetClientInfo(fHostAddr, fHostPort, fSourceURL, fLocalAddr);
+ if (fUserName != NULL)
+ fClient->SetName(fUserName);
+ if (fPassword != NULL)
+ fClient->SetPassword(fPassword);
+
+ fSessionQueue = inSessionQueue;
+
+ if(fAnnounce)
+ inSourceQueue->EnQueue(&fQueueElem);
+
+ fSessionCreationState = kSendingDescribe;
+ fRelaySessionCreatorTask = NEW RelaySessionCreator(this);
+ fRelaySessionCreatorTask->Signal(Task::kStartEvent);
+}
+
+SInt64 RTSPSourceInfo::RelaySessionCreator::Run()
+{
+ OSMutexLocker locker(RelayOutput::GetQueueMutex());
+ SInt64 result = -1;
+ if (fInfo != NULL)
+ result = fInfo->RunCreateSession();
+
+ return result;
+}
+
+SInt64 RTSPSourceInfo::RunCreateSession()
+{
+ OS_Error osErr = OS_NoErr;
+ SInt64 result = 500;
+
+ if (fSessionCreationState == kSendingDescribe)
+ {
+ if (!fDescribeComplete)
+ {
+ osErr = fClient->SendDescribe();
+
+ if (osErr == OS_NoErr)
+ {
+ if (fClient->GetStatus() == 200)
+ {
+ // we've gotten the describe response, so process it.
+ SDPSourceInfo theSourceInfo(fClient->GetContentBody(), fClient->GetContentLength());
+
+ // Copy the Source Info into our local SourceInfo.
+ fNumStreams = theSourceInfo.GetNumStreams();
+ fStreamArray = NEW StreamInfo[fNumStreams];
+
+ for (UInt32 x = 0; x < fNumStreams; x++)
+ {
+ // Copy fPayloadType, fPayloadName, fTrackID, fBufferDelay
+ fStreamArray[x].Copy(*theSourceInfo.GetStreamInfo(x));
+
+ // Copy all stream info data. Also set fSrcIPAddr to be the host addr
+ fStreamArray[x].fSrcIPAddr = fClientSocket->GetHostAddr();
+ fStreamArray[x].fDestIPAddr = fClientSocket->GetLocalAddr();
+ fStreamArray[x].fPort = 0;
+ fStreamArray[x].fTimeToLive = 0;
+ }
+ }
+ else
+ osErr = ENOTCONN;
+ }
+ }
+
+ //describe is complete
+ if(osErr == OS_NoErr)
+ {
+ //copy out the SDP information
+ fLocalSDP.Ptr = NEW char[fClient->GetContentLength() + 1];
+
+ // Look for an "a=range" line in the SDP. If there is one, remove it.
+ static StrPtrLen sRangeStr("a=range:");
+ StrPtrLen theSDPPtr(fClient->GetContentBody(), fClient->GetContentLength());
+ StringParser theSDPParser(&theSDPPtr);
+
+ do
+ {
+ // Loop until we reach the end of the SDP or hit a a=range line.
+ StrPtrLen theSDPLine(theSDPParser.GetCurrentPosition(), theSDPParser.GetDataRemaining());
+ if ((theSDPLine.Len > sRangeStr.Len) && (theSDPLine.NumEqualIgnoreCase(sRangeStr.Ptr, sRangeStr.Len)))
+ break;
+ } while (theSDPParser.GetThruEOL(NULL));
+
+ // Copy what we have so far
+ ::memcpy(fLocalSDP.Ptr, fClient->GetContentBody(), theSDPParser.GetDataParsedLen());
+ fLocalSDP.Len = theSDPParser.GetDataParsedLen();
+
+ // Skip over the range (if it exists)
+ (void)theSDPParser.GetThruEOL(NULL);
+
+ // Copy the rest of the SDP
+ ::memcpy(fLocalSDP.Ptr + fLocalSDP.Len, theSDPParser.GetCurrentPosition(), theSDPParser.GetDataRemaining());
+ fLocalSDP.Len += theSDPParser.GetDataRemaining();
+
+#define _WRITE_SDP_ 0
+
+#if _WRITE_SDP_
+ FILE* outputFile = ::fopen("rtspclient.sdp", "w");
+ if (outputFile != NULL)
+ {
+ fLocalSDP.Ptr[fLocalSDP.Len] = '\0';
+ qtss_fprintf(outputFile, "%s", fLocalSDP.Ptr);
+ ::fclose(outputFile);
+ qtss_printf("Wrote sdp to rtspclient.sdp\n");
+ }
+ else
+ qtss_printf("Failed to write sdp\n");
+#endif
+ fDescribeComplete = true;
+
+ fSession = NEW RelaySession(NULL, this);
+ if (fSession->SetupRelaySession(this) == OS_NoErr)
+ {
+ fSessionCreationState = kSendingSetup;
+ }
+ else
+ {
+ osErr = ENOTCONN;
+ }
+ }
+ }
+
+ while ((fSessionCreationState == kSendingSetup) && (osErr == OS_NoErr))
+ {
+ osErr = fClient->SendUDPSetup(fStreamArray[fNumSetupsComplete].fTrackID, fStreamArray[fNumSetupsComplete].fPort);
+ if(osErr == OS_NoErr)
+ {
+ if(fClient->GetStatus() == 200)
+ {
+ fNumSetupsComplete++;
+ if (fNumSetupsComplete == fNumStreams)
+ fSessionCreationState = kSendingPlay;
+ }
+ else
+ osErr = ENOTCONN;
+ }
+ }
+
+ if (fSessionCreationState == kSendingPlay)
+ {
+ osErr = fClient->SendPlay(0);
+ if (osErr == OS_NoErr)
+ {
+ if (fClient->GetStatus() == 200)
+ fSessionCreationState = kDone;
+ else
+ osErr = ENOTCONN;
+ }
+ }
+
+ if (fSessionCreationState == kDone)
+ {
+ // If session was correctly set up,
+ // add the outputs
+ if(fSession != NULL)
+ {
+ // Format SourceInfo HTML for the stats web page
+ fSession->FormatHTML(fClient->GetURL());
+
+ OSMutexLocker locker(RelayOutput::GetQueueMutex());
+ fSessionQueue->EnQueue(fSession->GetQueueElem());
+
+ for (UInt32 x = 0; x < fNumOutputs; x++)
+ {
+ SourceInfo::OutputInfo* theOutputInfo = GetOutputInfo(x);
+ if (theOutputInfo->fAlreadySetup)
+ continue; // shouldn't ever happen
+
+ RelayOutput* theOutput = NEW RelayOutput(this, x, fSession, true);
+ if (theOutput->IsValid())
+ fSession->AddOutput(theOutput, false);
+ else
+ delete theOutput;
+ }
+ }
+ fClientSocket->GetSocket()->SetTask(NULL); //detach the task from the socket
+ result = -1; // let the task die
+ fRelaySessionCreatorTask = NULL;
+ }
+
+ if ((osErr == EINPROGRESS) || (osErr == EAGAIN))
+ {
+ // Request an async event
+ fClientSocket->GetSocket()->SetTask(fRelaySessionCreatorTask);
+ fClientSocket->GetSocket()->RequestEvent(fClientSocket->GetEventMask() );
+ }
+ else if (osErr != OS_NoErr)
+ {
+ // We encountered some fatal error with the socket. Record this as a connection failure
+ // delete the session
+ // delete the session
+ if(fSession != NULL)
+ {
+ delete fSession;
+ fSession = NULL;
+ }
+
+ fClientSocket->GetSocket()->SetTask(NULL); //detach the task from the socket
+ result = -1; // let the task die
+ fRelaySessionCreatorTask = NULL;
+ }
+
+ return result;
+}
+
+
+RTSPSourceInfo::TeardownTask::TeardownTask(TCPClientSocket* clientSocket, RTSPClient* client)
+{
+ this->SetTaskName("RTSPSourceInfo::TeardownTask");
+ fClientSocket = clientSocket;
+ fClient = client;
+}
+
+RTSPSourceInfo::TeardownTask::~TeardownTask()
+{
+ delete fClientSocket;
+ delete fClient;
+}
+
+SInt64 RTSPSourceInfo::TeardownTask::Run()
+{
+ OS_Error err = fClient->SendTeardown();
+
+ if ((err == EINPROGRESS) || (err == EAGAIN))
+ {
+ // Request an async event
+ fClientSocket->GetSocket()->SetTask(this);
+ fClientSocket->GetSocket()->RequestEvent(fClientSocket->GetEventMask() );
+ return 250;
+ }
+ fClientSocket->GetSocket()->SetTask(NULL); //detach the task from the socket
+ return -1; // we're out of here, this will cause the destructor to be called
+}
+
diff --git a/APIModules/QTSSReflectorModule/RTSPSourceInfo.h b/APIModules/QTSSReflectorModule/RTSPSourceInfo.h
new file mode 100644
index 0000000..5748e1f
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/RTSPSourceInfo.h
@@ -0,0 +1,243 @@
+/*
+ *
+ * @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: RTSPSourceInfo.h
+
+ Contains:
+
+
+*/
+
+#ifndef __RTSP_SOURCE_INFO_H__
+#define __RTSP_SOURCE_INFO_H__
+
+#include "QTSS.h"
+#include "StrPtrLen.h"
+#include "RCFSourceInfo.h"
+#include "RTSPClient.h"
+#include "XMLParser.h"
+#include "ClientSocket.h"
+#include "RelaySession.h"
+
+class RelaySessionCreator;
+
+class RTSPOutputInfo
+{
+public:
+ RTSPOutputInfo() : fIsAnnounced(false),
+ fAnnouncePort(554),
+ fDestURl(NULL),
+ fUserName(NULL),
+ fPassword(NULL) {}
+
+ ~RTSPOutputInfo()
+ {
+ if (fDestURl != NULL) delete fDestURl;
+ if (fUserName != NULL) delete fUserName;
+ if (fPassword != NULL) delete fPassword;
+ }
+
+ static char* CopyString(const char* srcStr);
+ void Copy(const RTSPOutputInfo& copy); // copies dynamically allocated data too
+ Bool16 Equal(const RTSPOutputInfo* inInfo)
+ { return ((inInfo != NULL) && (fIsAnnounced == inInfo->fIsAnnounced) && (fAnnouncePort == inInfo->fAnnouncePort)
+ && (strcmp(fDestURl, inInfo->fDestURl) == 0)); }
+
+
+ Bool16 fIsAnnounced;
+ UInt16 fAnnouncePort;
+ char* fDestURl;
+ char* fUserName;
+ char* fPassword;
+};
+
+class RTSPSourceInfo : public RCFSourceInfo
+{
+ public:
+
+ // Specify whether the client should be blocking or non-blocking
+ RTSPSourceInfo(Bool16 inAnnounce) : fSourceURL(NULL),
+ fHostAddr(0),
+ fHostPort(0),
+ fLocalAddr(0),
+ fUserName(NULL),
+ fPassword(NULL),
+ fRTSPInfoArray(NULL),
+ fClientSocket(NULL),
+ fClient(NULL),
+ fNumSetupsComplete(0),
+ fDescribeComplete(false),
+ fAnnounce(inAnnounce),
+ fAnnounceURL(NULL),
+ fAnnounceIP(0),
+ fAnnounceActualIP(0),
+ fRelaySessionCreatorTask(NULL),
+ fSession(NULL),
+ fSessionQueue(NULL),
+ fQueueElem() { fQueueElem.SetEnclosingObject(this); }
+
+ RTSPSourceInfo(const RTSPSourceInfo& copy); // Does copy dynamically allocated data
+ // Doesn't copy fClientSocket and fClient ptrs
+
+ virtual ~RTSPSourceInfo();
+
+ // Call this before calling ParsePrefs / Describe
+ void InitClient(UInt32 inSocketType);
+
+ void SetClientInfo(UInt32 inAddr, UInt16 inPort, char* inURL, UInt32 inLocalAddr = 0);
+
+ // Call this immediately after the constructor. This object will parse
+ // the config file and extract the necessary information to connect to an rtsp server.
+ // Specify the config file line index where the "rtsp_source" line resides
+ QTSS_Error ParsePrefs(XMLTag* relayTag, Bool16 inAnnounce);
+
+ // Connects, sends a DESCRIBE, and parses the incoming SDP data. After this
+ // function completes sucessfully, GetLocalSDP returns the data, and the
+ // SourceInfo & DestInfo arrays will be set up. Also sends SETUPs for all the
+ // tracks, and finishes by issuing a PLAY.
+ //
+ // These functions return QTSS_NoErr if the transaction has completed
+ // successfully. Otherwise, they return:
+ //
+ // EAGAIN: the transaction is still in progress, the call should be reissued
+ // QTSS_RequestFailed: the remote host responded with an error.
+ // Any other error means that the remote host was unavailable or refused the connection
+ QTSS_Error Describe();
+ QTSS_Error SetupAndPlay();
+
+ // This function works the same way as the above ones, and should be
+ // called before destroying the object to let the remote host know that
+ // we are going away.
+ QTSS_Error Teardown();
+
+ // This function uses the Parsed SDP file, and strips out all the network information,
+ // producing an SDP file that appears to be local.
+ virtual char* GetLocalSDP(UInt32* newSDPLen);
+ virtual char* GetAnnounceSDP(UInt32 ipAddr, UInt32* newSDPLen);
+ virtual StrPtrLen* GetSourceID() { return fClient->GetURL(); }
+
+ // This object looks for this keyword in the FilePrefsSource, where it
+ // expects the IP address, port, and URL.
+ static StrPtrLen& GetRTSPSourceString() { return sKeyString; }
+
+ RTSPClient* GetRTSPClient() { return fClient; }
+ TCPClientSocket* GetClientSocket() { return fClientSocket; }
+
+ Bool16 IsDescribeComplete(){ return fDescribeComplete; }
+
+ RTSPOutputInfo* GetRTSPOutputInfo(UInt32 index) { return &fRTSPInfoArray[index]; }
+ char* GetSourceURL() { return fSourceURL; }
+
+ virtual Bool16 IsRTSPSourceInfo() { return true; }
+ virtual Bool16 Equal(SourceInfo* inInfo);
+
+ Bool16 IsAnnounce() { return fAnnounce; }
+
+ char* GetAnnounceURL() { return fAnnounceURL; }
+ UInt32 GetAnnounceIP() { return fAnnounceIP; }
+
+ UInt32 GetAnnounceActualIP() { return fAnnounceActualIP; }
+ void SetAnnounceActualIP(UInt32 inActualIP) { fAnnounceActualIP = inActualIP; }
+
+ UInt32 GetHostAddr() { return fHostAddr; }
+ UInt32 GetHostPort() { return fHostPort; }
+
+ char* GetUsername() { return fUserName; }
+ char* GetPassword() { return fPassword; }
+
+ RelaySession* GetRelaySession() { return fSession; }
+
+ void SetSourceParameters(UInt32 inHostAddr, UInt16 inHostPort, StrPtrLen& inURL);
+
+ void StartSessionCreatorTask(OSQueue* inSessionQueue, OSQueue* inSourceQueue);
+
+ SInt64 RunCreateSession();
+
+ protected:
+ virtual void ParseAnnouncedDestination(XMLTag* destTag, UInt32 index);
+ virtual void AllocateOutputArray(UInt32 numOutputs);
+
+ private:
+ class RelaySessionCreator : public Task
+ {
+ public:
+ RelaySessionCreator(RTSPSourceInfo* inInfo) : fInfo(inInfo) {this->SetTaskName("RTSPSourceInfo::RelaySessionCreator");}
+
+ virtual SInt64 Run();
+
+ RTSPSourceInfo* fInfo;
+ };
+
+ class TeardownTask : public Task
+ {
+ public:
+ TeardownTask(TCPClientSocket* clientSocket, RTSPClient* client);
+ virtual ~TeardownTask();
+
+ virtual SInt64 Run();
+
+ private:
+ TCPClientSocket* fClientSocket;
+ RTSPClient* fClient;
+ };
+
+ char* fSourceURL;
+ UInt32 fHostAddr;
+ UInt16 fHostPort;
+ UInt32 fLocalAddr;
+ char* fUserName;
+ char* fPassword;
+ RTSPOutputInfo* fRTSPInfoArray;
+ TCPClientSocket* fClientSocket;
+ RTSPClient* fClient;
+ UInt32 fNumSetupsComplete;
+ Bool16 fDescribeComplete;
+ StrPtrLen fLocalSDP;
+
+ Bool16 fAnnounce;
+ char* fAnnounceURL;
+ UInt32 fAnnounceIP;
+ UInt32 fAnnounceActualIP;
+ RelaySessionCreator* fRelaySessionCreatorTask;
+
+ enum // relay session creation states
+ {
+ kSendingDescribe = 0,
+ kSendingSetup = 1,
+ kSendingPlay = 2,
+ kDone = 3
+ };
+ UInt32 fSessionCreationState;
+
+ RelaySession* fSession;
+ OSQueue* fSessionQueue;
+
+ OSQueueElem fQueueElem;
+
+ static StrPtrLen sKeyString;
+ static StrPtrLen sAnnouncedKeyString;
+};
+#endif // __RTSP_SOURCE_INFO_H__
+
diff --git a/APIModules/QTSSReflectorModule/ReflectorOutput.h b/APIModules/QTSSReflectorModule/ReflectorOutput.h
new file mode 100644
index 0000000..20d5e6e
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/ReflectorOutput.h
@@ -0,0 +1,167 @@
+/*
+ *
+ * @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: ReflectorOutput.h
+
+ Contains: VERY simple abstract base class that defines one virtual method, WritePacket.
+ This is extremely useful to the reflector, which, using one of these objects,
+ can transparently reflect a packet, not being aware of how it will actually be
+ written to the network
+
+
+
+*/
+
+#ifndef __REFLECTOR_OUTPUT_H__
+#define __REFLECTOR_OUTPUT_H__
+
+#include "QTSS.h"
+#include "StrPtrLen.h"
+#include "OSHeaders.h"
+#include "MyAssert.h"
+#include "OS.h"
+#include "OSQueue.h"
+
+
+class ReflectorOutput
+{
+ public:
+
+ ReflectorOutput() : fBookmarkedPacketsElemsArray(NULL), fNumBookmarks(0), fAvailPosition(0), fLastIntervalMilliSec(5), fLastPacketTransmitTime(0) {}
+
+ virtual ~ReflectorOutput()
+ {
+ if ( fBookmarkedPacketsElemsArray )
+ { ::memset( fBookmarkedPacketsElemsArray, 0, sizeof ( OSQueueElem* ) * fNumBookmarks );
+ delete [] fBookmarkedPacketsElemsArray;
+ }
+ }
+
+ // an array of packet elements ( from fPacketQueue in ReflectorSender )
+ // possibly one for each ReflectorSender that sends data to this ReflectorOutput
+ OSQueueElem **fBookmarkedPacketsElemsArray;
+ UInt32 fNumBookmarks;
+ SInt32 fAvailPosition;
+ QTSS_TimeVal fLastIntervalMilliSec;
+ QTSS_TimeVal fLastPacketTransmitTime;
+
+ Bool16 fNewOutput;
+inline OSQueueElem* GetBookMarkedPacket(OSQueue *thePacketQueue);
+inline Bool16 SetBookMarkPacket(OSQueueElem* thePacketElemPtr);
+
+ // WritePacket
+ //
+ // Pass in the packet contents, the cookie of the stream to which it will be written,
+ // and the QTSS API write flags (this should either be qtssWriteFlagsIsRTP or IsRTCP
+ // packetLateness is how many MSec's late this packet is in being delivered ( will be < 0 if its early )
+ // If this function returns QTSS_WouldBlock, timeToSendThisPacketAgain will
+ // be set to # of msec in which the packet can be sent, or -1 if unknown
+ virtual QTSS_Error WritePacket(StrPtrLen* inPacket, void* inStreamCookie, UInt32 inFlags, SInt64 packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSec, Bool16 firstPacket ) = 0;
+
+ virtual void TearDown() = 0;
+ virtual Bool16 IsUDP() = 0;
+ virtual Bool16 IsPlaying() = 0;
+
+ enum { kWaitMilliSec = 5, kMaxWaitMilliSec = 1000 };
+
+ protected:
+ void InititializeBookmarks( UInt32 numStreams )
+ {
+ // need 2 bookmarks for each stream ( include RTCPs )
+ UInt32 numBookmarks = numStreams * 2;
+
+ fBookmarkedPacketsElemsArray = new OSQueueElem*[numBookmarks];
+ ::memset( fBookmarkedPacketsElemsArray, 0, sizeof ( OSQueueElem* ) * numBookmarks );
+
+ fNumBookmarks = numBookmarks;
+ }
+
+};
+
+Bool16 ReflectorOutput::SetBookMarkPacket(OSQueueElem* thePacketElemPtr)
+{
+ if (fAvailPosition != -1 && thePacketElemPtr)
+ {
+ fBookmarkedPacketsElemsArray[fAvailPosition] = thePacketElemPtr;
+
+ for (UInt32 i = 0; i < fNumBookmarks; i++)
+ {
+ if (fBookmarkedPacketsElemsArray[i] == NULL)
+ {
+ fAvailPosition = i;
+ return true;
+ }
+ }
+ }
+
+ return false;
+
+}
+
+OSQueueElem* ReflectorOutput::GetBookMarkedPacket(OSQueue *thePacketQueue)
+{
+ Assert(thePacketQueue != NULL);
+
+ OSQueueElem* packetElem = NULL;
+ UInt32 curBookmark = 0;
+
+ fAvailPosition = -1;
+
+ Assert( curBookmark < fNumBookmarks );
+
+ // see if we've bookmarked a held packet for this Sender in this Output
+ while ( curBookmark < fNumBookmarks )
+ {
+ OSQueueElem* bookmarkedElem = fBookmarkedPacketsElemsArray[curBookmark];
+
+ if ( bookmarkedElem ) // there may be holes in this array
+ {
+ if ( bookmarkedElem->IsMember( *thePacketQueue ) )
+ {
+ // this packet was previously bookmarked for this specific queue
+ // remove if from the bookmark list and use it
+ // to jump ahead into the Sender's over all packet queue
+ fBookmarkedPacketsElemsArray[curBookmark] = NULL;
+ fAvailPosition = curBookmark;
+ packetElem = bookmarkedElem;
+ break;
+ }
+ }
+ else
+ {
+ fAvailPosition = curBookmark;
+ }
+
+ curBookmark++;
+
+ }
+
+ return packetElem;
+}
+
+
+
+
+#endif //__REFLECTOR_OUTPUT_H__
diff --git a/APIModules/QTSSReflectorModule/ReflectorSession.cpp b/APIModules/QTSSReflectorModule/ReflectorSession.cpp
new file mode 100644
index 0000000..f7ff13f
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/ReflectorSession.cpp
@@ -0,0 +1,404 @@
+/*
+ *
+ * @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: ReflectorSession.cpp
+
+ Contains: Implementation of object defined in ReflectorSession.h.
+
+
+
+*/
+
+
+#include "ReflectorSession.h"
+#include "RTCPPacket.h"
+#include "SocketUtils.h"
+#include "EventContext.h"
+
+#include "OSMemory.h"
+#include "OS.h"
+#include "atomic.h"
+
+#include "QTSSModuleUtils.h"
+
+#include
+
+
+#ifndef __Win32__
+ #include
+#endif
+
+#if DEBUG
+#define REFLECTOR_SESSION_DEBUGGING 0
+#else
+#define REFLECTOR_SESSION_DEBUGGING 0
+#endif
+
+
+FileDeleter::FileDeleter(StrPtrLen* inSDPPath)
+{
+ Assert (inSDPPath);
+ fFilePath.Len = inSDPPath->Len;
+ fFilePath.Ptr = NEW char[inSDPPath->Len + 1];
+ Assert (fFilePath.Ptr);
+ memcpy(fFilePath.Ptr, inSDPPath->Ptr,inSDPPath->Len);
+ fFilePath.Ptr[inSDPPath->Len] = 0;
+}
+
+
+FileDeleter::~FileDeleter()
+{
+ //qtss_printf("FileDeleter::~FileDeleter delete = %s \n",fFilePath.Ptr);
+ ::unlink(fFilePath.Ptr);
+ delete fFilePath.Ptr;
+ fFilePath.Ptr = NULL;
+ fFilePath.Len = 0;
+}
+
+
+
+static OSRefTable* sStreamMap = NULL;
+
+
+
+void ReflectorSession::Initialize()
+{
+ if (sStreamMap == NULL)
+ sStreamMap = NEW OSRefTable();
+}
+
+ReflectorSession::ReflectorSession(StrPtrLen* inSourceID, SourceInfo* inInfo)
+: fIsSetup(false),
+ fQueueElem(),
+ fNumOutputs(0),
+ fStreamArray(NULL),
+ fFormatter(fHTMLBuf, kMaxHTMLSize),
+ fSourceInfo(inInfo),
+ fSocketStream(NULL),
+ fBroadcasterSession(NULL),
+ fInitTimeMS(OS::Milliseconds()),
+ fHasBufferedStreams(false)
+{
+
+ fQueueElem.SetEnclosingObject(this);
+ if (inSourceID != NULL)
+ {
+ fSourceID.Ptr = NEW char[inSourceID->Len + 1];
+ ::memcpy(fSourceID.Ptr, inSourceID->Ptr, inSourceID->Len);
+ fSourceID.Len = inSourceID->Len;
+ fRef.Set(fSourceID, this);
+ }
+}
+
+
+ReflectorSession::~ReflectorSession()
+{
+#if REFLECTOR_SESSION_DEBUGGING
+ qtss_printf("Removing ReflectorSession: %s\n", fSourceInfoHTML.Ptr);
+#endif
+
+ // For each stream, check to see if the ReflectorStream should be deleted
+ OSMutexLocker locker (sStreamMap->GetMutex());
+ for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
+ {
+ if (fStreamArray[x] == NULL)
+ continue;
+
+ UInt32 refCount = fStreamArray[x]->GetRef()->GetRefCount();
+ Bool16 unregisterNow = (refCount == 1) ? true : false;
+
+ //qtss_printf("ReflectorSession::~ReflectorSession stream index=%"_U32BITARG_" refcount=%"_U32BITARG_"\n",x,refCount);
+ //decrement the ref count
+
+ if (refCount > 0) // Refcount may be 0 if there was some error setting up the stream
+ sStreamMap->Release(fStreamArray[x]->GetRef()); // decrement the refcount
+
+ refCount = fStreamArray[x]->GetRef()->GetRefCount();
+ if (refCount == 0)
+ { // Delete this stream if the refcount has dropped to 0
+ if (unregisterNow)
+ sStreamMap->UnRegister(fStreamArray[x]->GetRef()); // Refcount may be 0 if there was some error setting up the stream
+ //qtss_printf("delete stream index=%"_U32BITARG_" refcount=%"_U32BITARG_"\n",x,refCount);
+ delete fStreamArray[x];
+ fStreamArray[x] = NULL;
+ }
+ }
+
+ // We own this object when it is given to us, so delete it now
+ delete [] fStreamArray;
+ delete fSourceInfo;
+ fLocalSDP.Delete();
+ fSourceID.Delete();
+}
+
+QTSS_Error ReflectorSession::SetupReflectorSession(SourceInfo* inInfo, QTSS_StandardRTSP_Params* inParams, UInt32 inFlags, Bool16 filterState, UInt32 filterTimeout)
+{
+ if (inInfo == NULL) // use the current SourceInfo
+ inInfo = fSourceInfo;
+
+ // Store a reference to this sourceInfo permanently
+ Assert((fSourceInfo == NULL) || (inInfo == fSourceInfo));
+ fSourceInfo = inInfo;
+
+ fLocalSDP.Delete();// this must be set to the new SDP.
+ fLocalSDP.Ptr = inInfo->GetLocalSDP(&fLocalSDP.Len);
+
+ // Allocate all our ReflectorStreams, using the SourceInfo
+
+ if (fStreamArray != NULL)
+ { for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
+ if (fSourceInfo->GetStreamInfo(x)->fPort > 0 && fStreamArray[x] != NULL)
+ sStreamMap->Release(fStreamArray[x]->GetRef());
+ }
+ delete fStreamArray; // keep the array list synchronized with the source info.
+ fStreamArray = NEW ReflectorStream*[fSourceInfo->GetNumStreams()];
+ ::memset(fStreamArray, 0, fSourceInfo->GetNumStreams() * sizeof(ReflectorStream*));
+
+ for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
+ {
+ // For each ReflectorStream, check and see if there is one that matches
+ // this stream ID
+ char theStreamID[ReflectorStream::kStreamIDSize];
+ StrPtrLen theStreamIDPtr(theStreamID, ReflectorStream::kStreamIDSize);
+ ReflectorStream::GenerateSourceID(fSourceInfo->GetStreamInfo(x), &theStreamID[0]);
+
+ OSMutexLocker locker(sStreamMap->GetMutex());
+ OSRef* theStreamRef = NULL;
+
+ if (false && (inFlags & kIsPushSession)) // always setup our own ports when pushed.
+ fSourceInfo->GetStreamInfo(x)->fPort = 0;
+ else
+ // If the port # of this stream is 0, that means "any port".
+ // We don't know what the dest port of this stream is yet (this
+ // can happen while acting as an RTSP client). Never share these streams.
+ // This can also happen if the incoming data is interleaved in the TCP connection or a dynamic UDP port is requested
+ if (fSourceInfo->GetStreamInfo(x)->fPort > 0)
+ { theStreamRef = sStreamMap->Resolve(&theStreamIDPtr);
+ #if REFLECTOR_SESSION_DEBUGGING
+ if (theStreamRef != NULL)
+ {
+ ReflectorStream* theRef = (ReflectorStream*)theStreamRef->GetObject();
+ UInt32 refCount = theRef->GetRef()->GetRefCount();
+ qtss_printf("stream has port stream index=%"_U32BITARG_" refcount=%"_U32BITARG_"\n",x,refCount);
+ }
+ #endif
+ }
+
+ if (theStreamRef == NULL)
+ {
+ fStreamArray[x] = NEW ReflectorStream(fSourceInfo->GetStreamInfo(x));
+ // Obviously, we may encounter an error binding the reflector sockets.
+ // If that happens, we'll just abort here, which will leave the ReflectorStream
+ // array in an inconsistent state, so we need to make sure in our cleanup
+ // code to check for NULL.
+ QTSS_Error theError = fStreamArray[x]->BindSockets(inParams,inFlags, filterState, filterTimeout);
+ if (theError != QTSS_NoErr)
+ {
+ delete fStreamArray[x];
+ fStreamArray[x] = NULL;
+ return theError;
+ }
+ fStreamArray[x]->SetEnableBuffer(this->fHasBufferedStreams);// buffering is done by the stream's sender
+
+ // If the port was 0, update it to reflect what the actual RTP port is.
+ fSourceInfo->GetStreamInfo(x)->fPort = fStreamArray[x]->GetStreamInfo()->fPort;
+ //qtss_printf("ReflectorSession::SetupReflectorSession fSourceInfo->GetStreamInfo(x)->fPort= %u\n",fSourceInfo->GetStreamInfo(x)->fPort);
+
+ ReflectorStream::GenerateSourceID(fSourceInfo->GetStreamInfo(x), &theStreamID[0]);
+
+ theError = sStreamMap->Register(fStreamArray[x]->GetRef());
+ Assert(theError == QTSS_NoErr);
+
+ //unless we do this, the refcount won't increment (and we'll delete the session prematurely
+ OSRef* debug = sStreamMap->Resolve(&theStreamIDPtr);
+ Assert(debug == fStreamArray[x]->GetRef());
+
+ //UInt32 refCount = fStreamArray[x]->GetRef()->GetRefCount();
+ //qtss_printf("stream index=%"_U32BITARG_" refcount=%"_U32BITARG_"\n",x,refCount);
+
+ }
+ else
+ fStreamArray[x] = (ReflectorStream*)theStreamRef->GetObject();
+
+ }
+
+
+ if (inFlags & kMarkSetup)
+ fIsSetup = true;
+
+ return QTSS_NoErr;
+}
+
+void ReflectorSession::AddBroadcasterClientSession(QTSS_StandardRTSP_Params* inParams)
+{
+ if (NULL == fStreamArray || NULL == inParams)
+ return;
+
+ for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
+ {
+ if (fStreamArray[x] != NULL)
+ { //qtss_printf("AddBroadcasterSession=%"_U32BITARG_"\n",inParams->inClientSession);
+ ((ReflectorSocket*)fStreamArray[x]->GetSocketPair()->GetSocketA())->AddBroadcasterSession(inParams->inClientSession);
+ ((ReflectorSocket*)fStreamArray[x]->GetSocketPair()->GetSocketB())->AddBroadcasterSession(inParams->inClientSession);
+ }
+ }
+ fBroadcasterSession = inParams->inClientSession;
+}
+void ReflectorSession::FormatHTML(StrPtrLen* inURL)
+{
+ // Begin writing our source description HTML (used by the relay)
+ // Line looks like: Relay Source: 17.221.98.239, Ports: 5430 5432 5434
+ static StrPtrLen sHTMLStart("Relay Source: ");
+ static StrPtrLen sPorts(", Ports: ");
+ static StrPtrLen sHTMLEnd("
");
+
+ // Begin writing the HTML
+ fFormatter.Put(sHTMLStart);
+
+ if (inURL == NULL)
+ {
+ // If no URL is provided, format the source IP addr as a string.
+ char theIPAddrBuf[20];
+ StrPtrLen theIPAddr(theIPAddrBuf, 20);
+ struct in_addr theAddr;
+ theAddr.s_addr = htonl(fSourceInfo->GetStreamInfo(0)->fSrcIPAddr);
+ SocketUtils::ConvertAddrToString(theAddr, &theIPAddr);
+ fFormatter.Put(theIPAddr);
+ }
+ else
+ fFormatter.Put(*inURL);
+
+ fFormatter.Put(sPorts);
+
+ for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
+ {
+ fFormatter.Put(fSourceInfo->GetStreamInfo(x)->fPort);
+ fFormatter.PutSpace();
+ }
+ fFormatter.Put(sHTMLEnd);
+
+ // Setup the StrPtrLen to point to the right stuff
+ fSourceInfoHTML.Ptr = fFormatter.GetBufPtr();
+ fSourceInfoHTML.Len = fFormatter.GetCurrentOffset();
+
+ fFormatter.PutTerminator();
+}
+
+
+void ReflectorSession::AddOutput(ReflectorOutput* inOutput, Bool16 isClient)
+{
+ Assert(fSourceInfo->GetNumStreams() > 0);
+
+ // We need to make sure that this output goes into the same bucket for each ReflectorStream.
+ SInt32 bucket = -1;
+ SInt32 lastBucket = -1;
+
+ while (true)
+ {
+ UInt32 x = 0;
+ for ( ; x < fSourceInfo->GetNumStreams(); x++)
+ {
+ bucket = fStreamArray[x]->AddOutput(inOutput, bucket);
+ if (bucket == -1) // If this output couldn't be added to this bucket,
+ break; // break and try again
+ else
+ {
+ lastBucket = bucket; // Remember the last successful bucket placement.
+ if (isClient)
+ fStreamArray[x]->IncEyeCount();
+ }
+ }
+
+ if (bucket == -1)
+ {
+ // If there was some kind of conflict adding this output to this bucket,
+ // we need to remove it from the streams to which it was added.
+ for (UInt32 y = 0; y < x; y++)
+ {
+ fStreamArray[y]->RemoveOutput(inOutput);
+ if (isClient)
+ fStreamArray[y]->DecEyeCount();
+ }
+
+ // Because there was an error, we need to start the whole process over again,
+ // this time starting from a higher bucket
+ lastBucket = bucket = lastBucket + 1;
+ }
+ else
+ break;
+ }
+ (void)atomic_add(&fNumOutputs, 1);
+}
+
+void ReflectorSession::RemoveOutput(ReflectorOutput* inOutput, Bool16 isClient)
+{
+ (void)atomic_sub(&fNumOutputs, 1);
+ for (UInt32 y = 0; y < fSourceInfo->GetNumStreams(); y++)
+ {
+ fStreamArray[y]->RemoveOutput(inOutput);
+ if (isClient)
+ fStreamArray[y]->DecEyeCount();
+ }
+}
+
+void ReflectorSession::TearDownAllOutputs()
+{
+ for (UInt32 y = 0; y < fSourceInfo->GetNumStreams(); y++)
+ fStreamArray[y]->TearDownAllOutputs();
+}
+
+void ReflectorSession::RemoveSessionFromOutput(QTSS_ClientSessionObject inSession)
+{
+ for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
+ { ((ReflectorSocket*)fStreamArray[x]->GetSocketPair()->GetSocketA())->RemoveBroadcasterSession(inSession);
+ ((ReflectorSocket*)fStreamArray[x]->GetSocketPair()->GetSocketB())->RemoveBroadcasterSession(inSession);
+ }
+ fBroadcasterSession = NULL;
+}
+
+
+UInt32 ReflectorSession::GetBitRate()
+{
+ UInt32 retval = 0;
+ for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
+ retval += fStreamArray[x]->GetBitRate();
+ return retval;
+}
+
+Bool16 ReflectorSession::Equal(SourceInfo* inInfo)
+{
+ return fSourceInfo->Equal(inInfo);
+}
+
+void* ReflectorSession::GetStreamCookie(UInt32 inStreamID)
+{
+ for (UInt32 x = 0; x < fSourceInfo->GetNumStreams(); x++)
+ {
+ if (fSourceInfo->GetStreamInfo(x)->fTrackID == inStreamID)
+ return fStreamArray[x]->GetStreamCookie();
+ }
+ return NULL;
+}
+
diff --git a/APIModules/QTSSReflectorModule/ReflectorSession.h b/APIModules/QTSSReflectorModule/ReflectorSession.h
new file mode 100644
index 0000000..69a14a4
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/ReflectorSession.h
@@ -0,0 +1,201 @@
+/*
+ *
+ * @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: ReflectorSession.h
+
+ Contains: This object supports reflecting an RTP multicast stream to N
+ RTPStreams. It spaces out the packet send times in order to
+ maximize the randomness of the sending pattern and smooth
+ the stream.
+
+
+
+*/
+
+
+
+#include "QTSS.h"
+#include "OSRef.h"
+#include "StrPtrLen.h"
+#include "ResizeableStringFormatter.h"
+#include "MyAssert.h"
+
+#include "ReflectorStream.h"
+#include "SourceInfo.h"
+#include "OSArrayObjectDeleter.h"
+
+
+#ifndef _FILE_DELETER_
+#define _FILE_DELETER_
+
+class FileDeleter
+{ public:
+ FileDeleter(StrPtrLen* inSDPPath);
+ ~FileDeleter();
+
+ private:
+ StrPtrLen fFilePath;
+};
+
+#endif
+
+
+#ifndef __REFLECTOR_SESSION__
+#define __REFLECTOR_SESSION__
+class ReflectorSession
+{
+ public:
+
+ // Public interface to generic RTP packet forwarding engine
+
+ //
+ // Initialize
+ //
+ // Call initialize before calling any other function in this class
+ static void Initialize();
+
+ // Create one of these ReflectorSessions per source broadcast. For mapping purposes,
+ // the object can be constructred using an optional source ID.
+ //
+ // Caller may also provide a SourceInfo object, though it is not needed and
+ // will also need to be provided to SetupReflectorSession when that is called.
+ ReflectorSession(StrPtrLen* inSourceID, SourceInfo* inInfo = NULL);
+ virtual ~ReflectorSession();
+
+ //
+ // MODIFIERS
+
+ // Call this to initialize and setup the source sockets. Once this function
+ // completes sucessfully, Outputs may be added through the function calls below.
+ //
+ // The SourceInfo object passed in here will be owned by the ReflectorSession. Do not
+ // delete it.
+
+ enum
+ {
+ kMarkSetup = 0, //After SetupReflectorSession is called, IsSetup returns true
+ kDontMarkSetup = 1, //After SetupReflectorSession is called, IsSetup returns false
+ kIsPushSession = 2 // When setting up streams handle port conflicts by allocating.
+ };
+
+ QTSS_Error SetupReflectorSession(SourceInfo* inInfo, QTSS_StandardRTSP_Params* inParams,
+ UInt32 inFlags = kMarkSetup, Bool16 filterState = true, UInt32 filterTimeout = 30);
+
+ // Packets get forwarded by attaching ReflectorOutput objects to a ReflectorSession.
+
+ void AddOutput(ReflectorOutput* inOutput, Bool16 isClient);
+ void RemoveOutput(ReflectorOutput* inOutput, Bool16 isClient);
+ void TearDownAllOutputs();
+ void RemoveSessionFromOutput(QTSS_ClientSessionObject inSession);
+ void ManuallyMarkSetup() { fIsSetup = true; }
+
+ // For the Relay's status, a ReflectorSession can format an informative bit of
+ // HTML to describe the source. This must be called after the ReflectorSession
+ // is all setup.
+
+ void FormatHTML(StrPtrLen* inURL);
+
+ //
+ // ACCESSORS
+
+ OSRef* GetRef() { return &fRef; }
+ OSQueueElem* GetQueueElem() { return &fQueueElem; }
+ UInt32 GetNumOutputs() { return fNumOutputs; }
+ UInt32 GetNumStreams() { return fSourceInfo->GetNumStreams(); }
+ StrPtrLen* GetSourceInfoHTML() { return &fSourceInfoHTML; }
+ SourceInfo* GetSourceInfo() { return fSourceInfo; }
+ StrPtrLen* GetLocalSDP() { return &fLocalSDP; }
+ StrPtrLen* GetSourcePath() { return &fSourceID; }
+ Bool16 IsSetup() { return fIsSetup; }
+ ReflectorStream*GetStreamByIndex(UInt32 inIndex) { return fStreamArray[inIndex]; }
+ void AddBroadcasterClientSession(QTSS_StandardRTSP_Params* inParams);
+ QTSS_ClientSessionObject GetBroadcasterSession() { return fBroadcasterSession;}
+
+ // For the QTSSSplitterModule, this object can cache a QTSS_StreamRef
+ void SetSocketStream(QTSS_StreamRef inStream) { fSocketStream = inStream; }
+ QTSS_StreamRef GetSocketStream() { return fSocketStream; }
+
+ // A ReflectorSession keeps track of the aggregate bit rate each
+ // stream is reflecting (RTP only). Initially, this will return 0
+ // until enough time passes to compute an accurate average.
+ UInt32 GetBitRate();
+
+ // Returns true if this SourceInfo structure is equivalent to this
+ // ReflectorSession.
+ Bool16 Equal(SourceInfo* inInfo);
+
+ // Each stream has a cookie associated with it. When the stream writes a packet
+ // to an output, this cookie is used to identify which stream is writing the packet.
+ // The below function is useful so outputs can get the cookie value for a stream ID,
+ // and therefore mux the cookie to the right output stream.
+ void* GetStreamCookie(UInt32 inStreamID);
+
+ //Reflector quality levels:
+ enum
+ {
+ kMaxHTMLSize = 128,
+ kAudioOnlyQuality = 1, //UInt32
+ kNormalQuality = 0, //UInt32
+ kNumQualityLevels = 2 //UInt32
+ };
+
+ SInt64 GetInitTimeMS() { return fInitTimeMS; }
+
+ void SetHasBufferedStreams(Bool16 enableBuffer) { fHasBufferedStreams = enableBuffer; }
+
+ private:
+
+ // Is this session setup?
+ Bool16 fIsSetup;
+
+ // For storage in the session map
+ OSRef fRef;
+ StrPtrLen fSourceID;
+ OSQueueElem fQueueElem; // Relay uses this.
+
+ unsigned int fNumOutputs;
+
+ ReflectorStream** fStreamArray;
+
+ char fHTMLBuf[kMaxHTMLSize];
+ StrPtrLen fSourceInfoHTML;
+ ResizeableStringFormatter fFormatter;
+
+ // The reflector session needs to hang onto the source info object
+ // for it's entire lifetime. Right now, this is used for reflector-as-client.
+ SourceInfo* fSourceInfo;
+ StrPtrLen fLocalSDP;
+
+ // For the QTSSSplitterModule, this object can cache a QTSS_StreamRef
+ QTSS_StreamRef fSocketStream;
+ QTSS_ClientSessionObject fBroadcasterSession;
+ SInt64 fInitTimeMS;
+
+ Bool16 fHasBufferedStreams;
+
+};
+
+#endif
+
diff --git a/APIModules/QTSSReflectorModule/ReflectorStream.cpp b/APIModules/QTSSReflectorModule/ReflectorStream.cpp
new file mode 100644
index 0000000..705074b
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/ReflectorStream.cpp
@@ -0,0 +1,1617 @@
+/*
+ *
+ * @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: ReflectorStream.cpp
+
+ Contains: Implementation of object defined in ReflectorStream.h.
+
+
+
+*/
+
+#include "ReflectorStream.h"
+#include "QTSSModuleUtils.h"
+#include "OSMemory.h"
+#include "SocketUtils.h"
+#include "atomic.h"
+#include "RTCPPacket.h"
+#include "ReflectorSession.h"
+
+
+#if DEBUG
+#define REFLECTOR_STREAM_DEBUGGING 0
+#else
+#define REFLECTOR_STREAM_DEBUGGING 0
+#endif
+
+
+static ReflectorSocketPool sSocketPool;
+
+// ATTRIBUTES
+
+static QTSS_AttributeID sCantBindReflectorSocketErr = qtssIllegalAttrID;
+static QTSS_AttributeID sCantJoinMulticastGroupErr = qtssIllegalAttrID;
+
+// PREFS
+
+static UInt32 sDefaultOverBufferInSec = 10;
+static UInt32 sDefaultBucketDelayInMsec = 73;
+static Bool16 sDefaultUsePacketReceiveTime = false;
+static UInt32 sDefaultMaxFuturePacketTimeSec = 60;
+static UInt32 sDefaultFirstPacketOffsetMsec = 500;
+
+UInt32 ReflectorStream::sBucketSize = 16;
+UInt32 ReflectorStream::sOverBufferInMsec = 10000; // more or less what the client over buffer will be
+UInt32 ReflectorStream::sMaxFuturePacketMSec = 60000; // max packet future time
+UInt32 ReflectorStream::sMaxPacketAgeMSec = 10000;
+
+UInt32 ReflectorStream::sMaxFuturePacketSec = 60; // max packet future time
+UInt32 ReflectorStream::sOverBufferInSec = 10;
+UInt32 ReflectorStream::sBucketDelayInMsec = 73;
+Bool16 ReflectorStream::sUsePacketReceiveTime = false;
+UInt32 ReflectorStream::sFirstPacketOffsetMsec = 500;
+
+void ReflectorStream::Register()
+{
+ // Add text messages attributes
+ static char* sCantBindReflectorSocket= "QTSSReflectorModuleCantBindReflectorSocket";
+ static char* sCantJoinMulticastGroup = "QTSSReflectorModuleCantJoinMulticastGroup";
+
+ (void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sCantBindReflectorSocket, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssTextMessagesObjectType, sCantBindReflectorSocket, &sCantBindReflectorSocketErr);
+
+ (void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sCantJoinMulticastGroup, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssTextMessagesObjectType, sCantJoinMulticastGroup, &sCantJoinMulticastGroupErr);
+}
+
+void ReflectorStream::Initialize(QTSS_ModulePrefsObject inPrefs)
+{
+
+ QTSSModuleUtils::GetAttribute(inPrefs, "reflector_bucket_offset_delay_msec", qtssAttrDataTypeUInt32,
+ &ReflectorStream::sBucketDelayInMsec, &sDefaultBucketDelayInMsec, sizeof(sBucketDelayInMsec));
+
+ QTSSModuleUtils::GetAttribute(inPrefs, "reflector_buffer_size_sec", qtssAttrDataTypeUInt32,
+ &ReflectorStream::sOverBufferInSec, &sDefaultOverBufferInSec, sizeof(sDefaultOverBufferInSec));
+
+ QTSSModuleUtils::GetAttribute(inPrefs, "reflector_use_in_packet_receive_time", qtssAttrDataTypeBool16,
+ &ReflectorStream::sUsePacketReceiveTime, &sDefaultUsePacketReceiveTime, sizeof(sDefaultUsePacketReceiveTime));
+
+ QTSSModuleUtils::GetAttribute(inPrefs, "reflector_in_packet_max_receive_sec", qtssAttrDataTypeUInt32,
+ &ReflectorStream::sMaxFuturePacketSec, &sDefaultMaxFuturePacketTimeSec, sizeof(sDefaultMaxFuturePacketTimeSec));
+
+ QTSSModuleUtils::GetAttribute(inPrefs, "reflector_rtp_info_offset_msec", qtssAttrDataTypeUInt32,
+ &ReflectorStream::sFirstPacketOffsetMsec, &sDefaultFirstPacketOffsetMsec, sizeof(sDefaultFirstPacketOffsetMsec));
+
+ ReflectorStream::sOverBufferInMsec = sOverBufferInSec * 1000;
+ ReflectorStream::sMaxFuturePacketMSec = sMaxFuturePacketSec * 1000;
+ ReflectorStream::sMaxPacketAgeMSec = (UInt32) (sOverBufferInMsec * 1.5); //allow a little time before deleting.
+
+}
+
+void ReflectorStream::GenerateSourceID(SourceInfo::StreamInfo* inInfo, char* ioBuffer)
+{
+
+ ::memcpy(ioBuffer, &inInfo->fSrcIPAddr, sizeof(inInfo->fSrcIPAddr));
+ ::memcpy(&ioBuffer[sizeof(inInfo->fSrcIPAddr)], &inInfo->fPort, sizeof(inInfo->fPort));
+}
+
+
+ReflectorStream::ReflectorStream(SourceInfo::StreamInfo* inInfo)
+: fPacketCount(0),
+ fSockets(NULL),
+ fRTPSender(NULL, qtssWriteFlagsIsRTP),
+ fRTCPSender(NULL, qtssWriteFlagsIsRTCP),
+ fOutputArray(NULL),
+ fNumBuckets(kMinNumBuckets),
+ fNumElements(0),
+ fBucketMutex(),
+
+ fDestRTCPAddr(0),
+ fDestRTCPPort(0),
+
+ fCurrentBitRate(0),
+ fLastBitRateSample(OS::Milliseconds()), // don't calculate our first bit rate until kBitRateAvgIntervalInMilSecs has passed!
+ fBytesSentInThisInterval(0),
+
+ fRTPChannel(-1),
+ fRTCPChannel(-1),
+ fHasFirstRTCPPacket(false),
+ fHasFirstRTPPacket(false),
+ fEnableBuffer(false),
+ fEyeCount(0),
+ fFirst_RTCP_RTP_Time(0),
+ fFirst_RTCP_Arrival_Time(0)
+{
+
+ fRTPSender.fStream = this;
+ fRTCPSender.fStream = this;
+
+ fStreamInfo.Copy(*inInfo);
+
+ // ALLOCATE BUCKET ARRAY
+ this->AllocateBucketArray(fNumBuckets);
+
+ // WRITE RTCP PACKET
+
+ //write as much of the RTCP RR as is possible right now (most of it never changes)
+ UInt32 theSsrc = (UInt32)::rand();
+ char theTempCName[RTCPSRPacket::kMaxCNameLen];
+ UInt32 cNameLen = RTCPSRPacket::GetACName(theTempCName);
+
+ //write the RR (just header + ssrc)
+ UInt32* theRRWriter = (UInt32*)&fReceiverReportBuffer[0];
+ *theRRWriter = htonl(0x80c90001);
+ theRRWriter++;
+ *theRRWriter = htonl(theSsrc);
+ theRRWriter++;
+
+ //SDES length is the length of the CName, plus 2 32bit words, minus 1
+ *theRRWriter = htonl(0x81ca0000 + (cNameLen >> 2) + 1);
+ theRRWriter++;
+ *theRRWriter = htonl(theSsrc);
+ theRRWriter++;
+ ::memcpy(theRRWriter, theTempCName, cNameLen);
+ theRRWriter += cNameLen >> 2;
+
+ //APP packet format, QTSS specific stuff
+ *theRRWriter = htonl(0x80cc0008);
+ theRRWriter++;
+ *theRRWriter = htonl(theSsrc);
+ theRRWriter++;
+ *theRRWriter = htonl(FOUR_CHARS_TO_INT('Q','T','S','S'));
+ theRRWriter++;
+ *theRRWriter = htonl(0);
+ theRRWriter++;
+ *theRRWriter = htonl(0x00000004);
+ theRRWriter++;
+ *theRRWriter = htonl(0x6579000c);
+ theRRWriter++;
+
+ fEyeLocation = theRRWriter;
+ fReceiverReportSize = kReceiverReportSize + kAppSize + cNameLen;
+
+ // If the source is a multicast, we should send our receiver reports
+ // to the multicast address
+ if (SocketUtils::IsMulticastIPAddr(fStreamInfo.fDestIPAddr))
+ {
+ fDestRTCPAddr = fStreamInfo.fDestIPAddr;
+ fDestRTCPPort = fStreamInfo.fPort + 1;
+ }
+}
+
+
+ReflectorStream::~ReflectorStream()
+{
+ Assert(fNumElements == 0);
+
+ if (fSockets != NULL)
+ {
+ //first things first, let's take this stream off the socket's queue
+ //of streams. This will basically ensure that no reflecting activity
+ //can happen on this stream.
+ ((ReflectorSocket*)fSockets->GetSocketA())->RemoveSender(&fRTPSender);
+ ((ReflectorSocket*)fSockets->GetSocketB())->RemoveSender(&fRTCPSender);
+
+ //leave the multicast group. Because this socket is shared amongst several
+ //potential multicasts, we don't want to remain a member of a stale multicast
+ if (SocketUtils::IsMulticastIPAddr(fStreamInfo.fDestIPAddr))
+ {
+ fSockets->GetSocketA()->LeaveMulticast(fStreamInfo.fDestIPAddr);
+ fSockets->GetSocketB()->LeaveMulticast(fStreamInfo.fDestIPAddr);
+ }
+ //now release the socket pair
+ sSocketPool.ReleaseUDPSocketPair(fSockets);
+ }
+
+ //qtss_printf("Deleting stream %x\n", this);
+
+ //delete every client Bucket
+ for (UInt32 y = 0; y < fNumBuckets; y++)
+ delete [] fOutputArray[y];
+ delete [] fOutputArray;
+}
+
+void ReflectorStream::AllocateBucketArray(UInt32 inNumBuckets)
+{
+ Bucket* oldArray = fOutputArray;
+ //allocate the 2-dimensional array
+ fOutputArray = NEW Bucket[inNumBuckets];
+ for (UInt32 x = 0; x < inNumBuckets; x++)
+ {
+ fOutputArray[x] = NEW ReflectorOutput*[sBucketSize];
+ ::memset(fOutputArray[x], 0, sizeof(ReflectorOutput*) * sBucketSize);
+ }
+
+ //copy over the old information if there was an old array
+ if (oldArray != NULL)
+ {
+ Assert(inNumBuckets > fNumBuckets);
+ for (UInt32 y = 0; y < fNumBuckets; y++)
+ {
+ ::memcpy(fOutputArray[y],oldArray[y], sBucketSize * sizeof(ReflectorOutput*));
+ delete [] oldArray[y];
+ }
+ delete [] oldArray;
+ }
+ fNumBuckets = inNumBuckets;
+}
+
+
+SInt32 ReflectorStream::AddOutput(ReflectorOutput* inOutput, SInt32 putInThisBucket)
+{
+ OSMutexLocker locker(&fBucketMutex);
+
+#if DEBUG
+ // We should never be adding an output twice to a stream
+ for (UInt32 dOne = 0; dOne < fNumBuckets; dOne++)
+ for (UInt32 dTwo = 0; dTwo < sBucketSize; dTwo++)
+ Assert(fOutputArray[dOne][dTwo] != inOutput);
+#endif
+
+ // If caller didn't specify a bucket, find a bucket
+ if (putInThisBucket < 0)
+ putInThisBucket = this->FindBucket();
+
+ Assert(putInThisBucket >= 0);
+
+ if (fNumBuckets <= (UInt32)putInThisBucket)
+ this->AllocateBucketArray(putInThisBucket * 2);
+
+ for(UInt32 y = 0; y < sBucketSize; y++)
+ {
+ if (fOutputArray[putInThisBucket][y] == NULL)
+ {
+ fOutputArray[putInThisBucket][y] = inOutput;
+#if REFLECTOR_STREAM_DEBUGGING
+ qtss_printf("Adding new output (0x%lx) to bucket %"_S32BITARG_", index %"_S32BITARG_",\nnum buckets %li bucketSize: %li \n",(SInt32)inOutput, putInThisBucket, y, (SInt32)fNumBuckets, (SInt32)sBucketSize);
+#endif
+ fNumElements++;
+ return putInThisBucket;
+ }
+ }
+ // There was no empty spot in the specified bucket. Return an error
+ return -1;
+}
+
+SInt32 ReflectorStream::FindBucket()
+{
+ // If we need more buckets, allocate them.
+ if (fNumElements == (sBucketSize * fNumBuckets))
+ this->AllocateBucketArray(fNumBuckets * 2);
+
+ //find the first open spot in the array
+ for (SInt32 putInThisBucket = 0; (UInt32)putInThisBucket < fNumBuckets; putInThisBucket++)
+ {
+ for(UInt32 y = 0; y < sBucketSize; y++)
+ if (fOutputArray[putInThisBucket][y] == NULL)
+ return putInThisBucket;
+ }
+ Assert(0);
+ return 0;
+}
+
+void ReflectorStream::RemoveOutput(ReflectorOutput* inOutput)
+{
+ OSMutexLocker locker(&fBucketMutex);
+ Assert(fNumElements > 0);
+
+ //look at all the indexes in the array
+ for (UInt32 x = 0; x < fNumBuckets; x++)
+ {
+ for (UInt32 y = 0; y < sBucketSize; y++)
+ {
+ //The array may have blank spaces!
+ if (fOutputArray[x][y] == inOutput)
+ {
+ fOutputArray[x][y] = NULL;//just clear out the pointer
+
+#if REFLECTOR_STREAM_DEBUGGING
+ qtss_printf("Removing output %x from bucket %"_S32BITARG_", index %"_S32BITARG_"\n",inOutput,x,y);
+#endif
+ fNumElements--;
+ return;
+ }
+ }
+ }
+ Assert(0);
+}
+
+void ReflectorStream::TearDownAllOutputs()
+{
+
+ OSMutexLocker locker(&fBucketMutex);
+
+ //look at all the indexes in the array
+ for (UInt32 x = 0; x < fNumBuckets; x++)
+ {
+ for (UInt32 y = 0; y < sBucketSize; y++)
+ { ReflectorOutput* theOutputPtr= fOutputArray[x][y];
+ //The array may have blank spaces!
+ if (theOutputPtr != NULL)
+ { theOutputPtr->TearDown();
+#if REFLECTOR_STREAM_DEBUGGING
+ qtss_printf("TearDownAllOutputs Removing output from bucket %"_S32BITARG_", index %"_S32BITARG_"\n",x,y);
+#endif
+ }
+ }
+ }
+}
+
+
+QTSS_Error ReflectorStream::BindSockets(QTSS_StandardRTSP_Params* inParams, UInt32 inReflectorSessionFlags, Bool16 filterState, UInt32 timeout)
+{
+ // If the incoming data is RTSP interleaved, we don't need to do anything here
+ if (inReflectorSessionFlags & ReflectorSession::kIsPushSession)
+ fStreamInfo.fSetupToReceive = true;
+
+ QTSS_RTSPRequestObject inRequest = NULL;
+ if (inParams != NULL)
+ inRequest = inParams->inRTSPRequest;
+
+ // Set the transport Type a Broadcaster
+ QTSS_RTPTransportType transportType = qtssRTPTransportTypeUDP;
+ if (inParams != NULL)
+ { UInt32 theLen = sizeof(transportType);
+ (void) QTSS_GetValue(inParams->inRTSPRequest, qtssRTSPReqTransportType, 0, (void*)&transportType, &theLen);
+ }
+
+ // get a pair of sockets. The socket must be bound on INADDR_ANY because we don't know
+ // which interface has access to this broadcast. If there is a source IP address
+ // specified by the source info, we can use that to demultiplex separate broadcasts on
+ // the same port. If the src IP addr is 0, we cannot do this and must dedicate 1 port per
+ // broadcast
+
+ // changing INADDR_ANY to fStreamInfo.fDestIPAddr to deal with NATs (need to track this change though)
+ // change submitted by denis@berlin.ccc.de
+ Bool16 isMulticastDest = (SocketUtils::IsMulticastIPAddr(fStreamInfo.fDestIPAddr));
+ if (isMulticastDest) {
+ fSockets = sSocketPool.GetUDPSocketPair(INADDR_ANY, fStreamInfo.fPort, fStreamInfo.fSrcIPAddr, 0);
+ } else {
+ fSockets = sSocketPool.GetUDPSocketPair(fStreamInfo.fDestIPAddr, fStreamInfo.fPort, fStreamInfo.fSrcIPAddr, 0);
+ }
+
+ if ((fSockets == NULL) && fStreamInfo.fSetupToReceive)
+ {
+ fStreamInfo.fPort = 0;
+ if (isMulticastDest) {
+ fSockets = sSocketPool.GetUDPSocketPair(INADDR_ANY, fStreamInfo.fPort, fStreamInfo.fSrcIPAddr, 0);
+ } else {
+ fSockets = sSocketPool.GetUDPSocketPair(fStreamInfo.fDestIPAddr, fStreamInfo.fPort, fStreamInfo.fSrcIPAddr, 0);
+ }
+ }
+ if (fSockets == NULL)
+ return QTSSModuleUtils::SendErrorResponse(inRequest, qtssServerInternal,
+ sCantBindReflectorSocketErr);
+
+ // If we know the source IP address of this broadcast, we can demux incoming traffic
+ // on the same port by that source IP address. If we don't know the source IP addr,
+ // it is impossible for us to demux, and therefore we shouldn't allow multiple
+ // broadcasts on the same port.
+ if (((ReflectorSocket*)fSockets->GetSocketA())->HasSender() && (fStreamInfo.fSrcIPAddr == 0))
+ return QTSSModuleUtils::SendErrorResponse(inRequest, qtssServerInternal,
+ sCantBindReflectorSocketErr);
+
+ //also put this stream onto the socket's queue of streams
+ ((ReflectorSocket*)fSockets->GetSocketA())->AddSender(&fRTPSender);
+ ((ReflectorSocket*)fSockets->GetSocketB())->AddSender(&fRTCPSender);
+
+ // A broadcaster is setting up a UDP session so let the sockets update the session
+ if (fStreamInfo.fSetupToReceive && qtssRTPTransportTypeUDP == transportType && inParams != NULL)
+ { ((ReflectorSocket*)fSockets->GetSocketA())->AddBroadcasterSession(inParams->inClientSession);
+ ((ReflectorSocket*)fSockets->GetSocketB())->AddBroadcasterSession(inParams->inClientSession);
+ }
+
+ ((ReflectorSocket*)fSockets->GetSocketA())->SetSSRCFilter(filterState, timeout);
+ ((ReflectorSocket*)fSockets->GetSocketB())->SetSSRCFilter(filterState, timeout);
+
+#if 1
+ // Always set the Rcv buf size for the sockets. This is important because the
+ // server is going to be getting many packets on these sockets.
+ fSockets->GetSocketA()->SetSocketRcvBufSize(512 * 1024);
+ fSockets->GetSocketB()->SetSocketRcvBufSize(512 * 1024);
+#endif
+
+ //If the broadcaster is sending RTP directly to us, we don't
+ //need to join a multicast group because we're not using multicast
+ if (isMulticastDest)
+ {
+ QTSS_Error err = fSockets->GetSocketA()->JoinMulticast(fStreamInfo.fDestIPAddr);
+ if (err == QTSS_NoErr)
+ err = fSockets->GetSocketB()->JoinMulticast(fStreamInfo.fDestIPAddr);
+ // If we get an error when setting the TTL, this isn't too important (TTL on
+ // these sockets is only useful for RTCP RRs.
+ if (err == QTSS_NoErr)
+ (void)fSockets->GetSocketA()->SetTtl(fStreamInfo.fTimeToLive);
+ if (err == QTSS_NoErr)
+ (void)fSockets->GetSocketB()->SetTtl(fStreamInfo.fTimeToLive);
+ if (err != QTSS_NoErr)
+ return QTSSModuleUtils::SendErrorResponse(inRequest, qtssServerInternal,
+ sCantJoinMulticastGroupErr);
+ }
+
+ // If the port is 0, update the port to be the actual port value
+ fStreamInfo.fPort = fSockets->GetSocketA()->GetLocalPort();
+
+ //finally, register these sockets for events
+ fSockets->GetSocketA()->RequestEvent(EV_RE);
+ fSockets->GetSocketB()->RequestEvent(EV_RE);
+
+ // Copy the source ID and setup the ref
+ StrPtrLen theSourceID(fSourceIDBuf, kStreamIDSize);
+ ReflectorStream::GenerateSourceID(&fStreamInfo, fSourceIDBuf);
+ fRef.Set(theSourceID, this);
+ return QTSS_NoErr;
+}
+
+void ReflectorStream::SendReceiverReport()
+{
+ // Check to see if our destination RTCP addr & port are setup. They may
+ // not be if the source is unicast and we haven't gotten any incoming packets yet
+ if (fDestRTCPAddr == 0)
+ return;
+
+ UInt32 theEyeCount = this->GetEyeCount();
+ UInt32* theEyeWriter = fEyeLocation;
+ *theEyeWriter = htonl(theEyeCount) & 0x7fffffff;//no idea why we do this!
+ theEyeWriter++;
+ *theEyeWriter = htonl(theEyeCount) & 0x7fffffff;
+ theEyeWriter++;
+ *theEyeWriter = htonl(0) & 0x7fffffff;
+
+ //send the packet to the multicast RTCP addr & port for this stream
+ (void)fSockets->GetSocketB()->SendTo(fDestRTCPAddr, fDestRTCPPort, fReceiverReportBuffer, fReceiverReportSize);
+}
+
+void ReflectorStream::PushPacket(char *packet, UInt32 packetLen, Bool16 isRTCP)
+{
+
+ if (packetLen > 0)
+ {
+ ReflectorPacket* thePacket = NULL;
+ if (isRTCP)
+ { //qtss_printf("ReflectorStream::PushPacket RTCP packetlen = %"_U32BITARG_"\n",packetLen);
+ thePacket = ((ReflectorSocket*)fSockets->GetSocketB())->GetPacket();
+ if (thePacket == NULL)
+ { //qtss_printf("ReflectorStream::PushPacket RTCP GetPacket() is NULL\n");
+ return;
+ }
+
+ OSMutexLocker locker( ((ReflectorSocket*)(fSockets->GetSocketB()) )->GetDemuxer()->GetMutex());
+ thePacket->SetPacketData(packet, packetLen);
+ ((ReflectorSocket*)fSockets->GetSocketB())->ProcessPacket(OS::Milliseconds(),thePacket,0,0);
+ ((ReflectorSocket*)fSockets->GetSocketB())->Signal(Task::kIdleEvent);
+ }
+ else
+ { //qtss_printf("ReflectorStream::PushPacket RTP packetlen = %"_U32BITARG_"\n",packetLen);
+ thePacket = ((ReflectorSocket*)fSockets->GetSocketA())->GetPacket();
+ if (thePacket == NULL)
+ { //qtss_printf("ReflectorStream::PushPacket GetPacket() is NULL\n");
+ return;
+ }
+
+ OSMutexLocker locker(((ReflectorSocket*)(fSockets->GetSocketA()))->GetDemuxer()->GetMutex());
+ thePacket->SetPacketData(packet, packetLen);
+ ((ReflectorSocket*)fSockets->GetSocketA())->ProcessPacket(OS::Milliseconds(),thePacket,0,0);
+ ((ReflectorSocket*)fSockets->GetSocketA())->Signal(Task::kIdleEvent);
+ }
+ }
+}
+
+
+
+
+ReflectorSender::ReflectorSender(ReflectorStream* inStream, UInt32 inWriteFlag)
+: fStream(inStream),
+ fWriteFlag(inWriteFlag),
+ fFirstNewPacketInQueue(NULL),
+ fFirstPacketInQueueForNewOutput(NULL),
+ fHasNewPackets(false),
+ fNextTimeToRun(0),
+ fLastRRTime(0),
+ fSocketQueueElem()
+{
+ fSocketQueueElem.SetEnclosingObject(this);
+}
+
+ReflectorSender::~ReflectorSender()
+{
+ //dequeue and delete every buffer
+ while (fPacketQueue.GetLength() > 0)
+ {
+ ReflectorPacket* packet = (ReflectorPacket*)fPacketQueue.DeQueue()->GetEnclosingObject();
+ delete packet;
+ }
+}
+
+
+Bool16 ReflectorSender::ShouldReflectNow(const SInt64& inCurrentTime, SInt64* ioWakeupTime)
+{
+ Assert(ioWakeupTime != NULL);
+ //check to make sure there actually is work to do for this stream.
+ if ((!fHasNewPackets) && ((fNextTimeToRun == 0) || (inCurrentTime < fNextTimeToRun)))
+ {
+ //We don't need to do work right now, but
+ //this stream must still communicate when it needs to be woken up next
+ SInt64 theWakeupTime = fNextTimeToRun + inCurrentTime;
+ //qtss_printf("ReflectorSender::ShouldReflectNow theWakeupTime=%qd newWakeUpTime=%qd ioWakepTime=%qd\n", theWakeupTime, fNextTimeToRun + inCurrentTime,*ioWakeupTime);
+ if ((fNextTimeToRun > 0) && (theWakeupTime < *ioWakeupTime))
+ *ioWakeupTime = theWakeupTime;
+ return false;
+ }
+ return true;
+}
+
+UInt32 ReflectorSender::GetOldestPacketRTPTime(Bool16 *foundPtr)
+{
+ if (foundPtr != NULL)
+ *foundPtr = false;
+ OSMutexLocker locker(&fStream->fBucketMutex);
+ OSQueueElem* packetElem = this->GetClientBufferStartPacket();
+ if (packetElem == NULL)
+ return 0;
+
+ ReflectorPacket* thePacket = (ReflectorPacket*)packetElem->GetEnclosingObject();
+ if (thePacket == NULL)
+ return 0;
+
+ if (foundPtr != NULL)
+ *foundPtr = true;
+
+ return thePacket->GetPacketRTPTime();
+}
+
+UInt16 ReflectorSender::GetFirstPacketRTPSeqNum(Bool16 *foundPtr)
+{
+ if (foundPtr != NULL)
+ *foundPtr = false;
+
+ UInt16 resultSeqNum = 0;
+ OSMutexLocker locker(&fStream->fBucketMutex);
+ OSQueueElem* packetElem = this->GetClientBufferStartPacket();
+
+ if (packetElem == NULL)
+ return 0;
+
+ ReflectorPacket* thePacket = (ReflectorPacket*)packetElem->GetEnclosingObject();
+ if (thePacket == NULL)
+ return 0;
+
+ if (foundPtr != NULL)
+ *foundPtr = true;
+
+ resultSeqNum = thePacket->GetPacketRTPSeqNum();
+
+ return resultSeqNum;
+}
+
+OSQueueElem* ReflectorSender::GetClientBufferNextPacketTime(UInt32 inRTPTime)
+{
+
+ OSQueueIter qIter(&fPacketQueue);// start at oldest packet in q
+ OSQueueElem* requestedPacket = NULL;
+ OSQueueElem* elem = NULL;
+
+ while ( !qIter.IsDone() ) // start at oldest packet in q
+ {
+ elem = qIter.GetCurrent();
+
+ if (requestedPacket == NULL)
+ requestedPacket = elem;
+
+ if (requestedPacket == NULL)
+ break;
+
+ ReflectorPacket* thePacket = (ReflectorPacket*)elem->GetEnclosingObject();
+ Assert( thePacket );
+
+ if (thePacket->GetPacketRTPTime() > inRTPTime)
+ {
+ requestedPacket = elem; // return the first packet we have that has a later time
+ break; // found the packet we need: done processing
+ }
+ qIter.Next();
+
+
+ }
+
+ return requestedPacket;
+}
+
+Bool16 ReflectorSender::GetFirstRTPTimePacket(UInt16* outSeqNumPtr, UInt32* outRTPTimePtr, SInt64* outArrivalTimePtr)
+{
+ OSMutexLocker locker(&fStream->fBucketMutex);
+ OSQueueElem* packetElem = this->GetClientBufferStartPacketOffset(ReflectorStream::sFirstPacketOffsetMsec);
+
+ if (packetElem == NULL)
+ return false;
+
+ ReflectorPacket* thePacket = (ReflectorPacket*)packetElem->GetEnclosingObject();
+ if (thePacket == NULL)
+ return false;
+
+ packetElem = GetClientBufferNextPacketTime(thePacket->GetPacketRTPTime());
+ if (packetElem == NULL)
+ return false;
+
+ thePacket = (ReflectorPacket*)packetElem->GetEnclosingObject();
+ if (thePacket == NULL)
+ return false;
+
+ if (outSeqNumPtr)
+ *outSeqNumPtr = thePacket->GetPacketRTPSeqNum();
+
+ if (outRTPTimePtr)
+ *outRTPTimePtr = thePacket->GetPacketRTPTime();
+
+ if (outArrivalTimePtr)
+ *outArrivalTimePtr = thePacket->fTimeArrived;
+
+ return true;
+}
+
+Bool16 ReflectorSender::GetFirstPacketInfo(UInt16* outSeqNumPtr, UInt32* outRTPTimePtr, SInt64* outArrivalTimePtr)
+{
+ OSMutexLocker locker(&fStream->fBucketMutex);
+ OSQueueElem* packetElem = this->GetClientBufferStartPacketOffset(ReflectorStream::sFirstPacketOffsetMsec);
+// OSQueueElem* packetElem = this->GetClientBufferStartPacket();
+
+ if (packetElem == NULL)
+ return false;
+
+ ReflectorPacket* thePacket = (ReflectorPacket*)packetElem->GetEnclosingObject();
+ if (thePacket == NULL)
+ return false;
+
+ if (outSeqNumPtr)
+ *outSeqNumPtr = thePacket->GetPacketRTPSeqNum();
+
+ if (outRTPTimePtr)
+ *outRTPTimePtr = thePacket->GetPacketRTPTime();
+
+ if (outArrivalTimePtr)
+ *outArrivalTimePtr = thePacket->fTimeArrived;
+
+ thePacket->fNeededByOutput = true;
+
+ return true;
+}
+
+
+#if REFLECTOR_STREAM_DEBUGGING
+static UInt16 DGetPacketSeqNumber(StrPtrLen* inPacket)
+{
+ if (inPacket->Len < 4)
+ return 0;
+
+ //The RTP seq number is the second short of the packet
+ UInt16* seqNumPtr = (UInt16*)inPacket->Ptr;
+ return ntohs(seqNumPtr[1]);
+}
+
+
+
+#endif
+
+
+void ReflectorSender::ReflectRelayPackets(SInt64* ioWakeupTime, OSQueue* inFreeQueue)
+{
+ //Most of this code is useless i.e. buckets and bookmarks. This code will get cleaned up eventually
+
+ //printf("ReflectorSender::ReflectPackets %qd %qd\n",*ioWakeupTime,fNextTimeToRun);
+#if DEBUG
+ Assert(ioWakeupTime != NULL);
+#endif
+ #if REFLECTOR_STREAM_DEBUGGING > 2
+ Bool16 printQueueLenOnExit = false;
+ #endif
+
+ SInt64 currentTime = OS::Milliseconds();
+
+ //make sure to reset these state variables
+ fHasNewPackets = false;
+ fNextTimeToRun = 1000; // init to 1 secs
+
+ //determine if we need to send a receiver report to the multicast source
+ if ((fWriteFlag == qtssWriteFlagsIsRTCP) && (currentTime > (fLastRRTime + kRRInterval)))
+ {
+ fLastRRTime = currentTime;
+ fStream->SendReceiverReport();
+ #if REFLECTOR_STREAM_DEBUGGING > 2
+ printQueueLenOnExit = true;
+ printf( "fPacketQueue len %li\n", (SInt32)fPacketQueue.GetLength() );
+ #endif
+ }
+
+ //the rest of this function must be atomic wrt the ReflectorSession, because
+ //it involves iterating through the RTPSession array, which isn't thread safe
+ OSMutexLocker locker(&fStream->fBucketMutex);
+
+ // Check to see if we should update the session's bitrate average
+ if ((fStream->fLastBitRateSample + ReflectorStream::kBitRateAvgIntervalInMilSecs) < currentTime)
+ {
+ unsigned int intervalBytes = fStream->fBytesSentInThisInterval;
+ (void)atomic_sub(&fStream->fBytesSentInThisInterval, intervalBytes);
+
+ // Multiply by 1000 to convert from milliseconds to seconds, and by 8 to convert from bytes to bits
+ Float32 bps = (Float32)(intervalBytes * 8) / (Float32)(currentTime - fStream->fLastBitRateSample);
+ bps *= 1000;
+ fStream->fCurrentBitRate = (UInt32)bps;
+
+ // Don't check again for awhile!
+ fStream->fLastBitRateSample = currentTime;
+ }
+
+ for (UInt32 bucketIndex = 0; bucketIndex < fStream->fNumBuckets; bucketIndex++)
+ {
+ for (UInt32 bucketMemberIndex = 0; bucketMemberIndex < fStream->sBucketSize; bucketMemberIndex++)
+ {
+ ReflectorOutput* theOutput = fStream->fOutputArray[bucketIndex][bucketMemberIndex];
+
+
+ if (theOutput != NULL)
+ {
+ SInt32 availBookmarksPosition = -1; // -1 == invalid position
+ OSQueueElem* packetElem = NULL;
+ UInt32 curBookmark = 0;
+
+ Assert( curBookmark < theOutput->fNumBookmarks );
+
+ // see if we've bookmarked a held packet for this Sender in this Output
+ while ( curBookmark < theOutput->fNumBookmarks )
+ {
+ OSQueueElem* bookmarkedElem = theOutput->fBookmarkedPacketsElemsArray[curBookmark];
+
+ if ( bookmarkedElem ) // there may be holes in this array
+ {
+ if ( bookmarkedElem->IsMember( fPacketQueue ) )
+ {
+ // this packet was previously bookmarked for this specific queue
+ // remove if from the bookmark list and use it
+ // to jump ahead into the Sender's over all packet queue
+ theOutput->fBookmarkedPacketsElemsArray[curBookmark] = NULL;
+ availBookmarksPosition = curBookmark;
+ packetElem = bookmarkedElem;
+ break;
+ }
+
+ }
+ else
+ {
+ availBookmarksPosition = curBookmark;
+ }
+
+ curBookmark++;
+
+ }
+
+ Assert( availBookmarksPosition != -1 );
+
+ #if REFLECTOR_STREAM_DEBUGGING > 1
+ if ( packetElem ) // show 'em what we got johnny
+ { ReflectorPacket* thePacket = (ReflectorPacket*)packetElem->GetEnclosingObject();
+ printf("Bookmarked packet time: %li, packetSeq %i\n", (SInt32)thePacket->fTimeArrived, DGetPacketSeqNumber( &thePacket->fPacketPtr ) );
+ }
+ #endif
+
+ // the output did not have a bookmarked packet if it's own
+ // so show it the first new packet we have in this sender.
+ // ( since TCP flow control may delay the sending of packets, this may not
+ // be the same as the first packet in the queue
+ if ( packetElem == NULL )
+ {
+ packetElem = fFirstNewPacketInQueue;
+
+ #if REFLECTOR_STREAM_DEBUGGING > 1
+ if ( packetElem ) // show 'em what we got johnny
+ {
+ ReflectorPacket* thePacket = (ReflectorPacket*)packetElem->GetEnclosingObject();
+ printf("1st NEW packet from Sender sess 0x%lx time: %li, packetSeq %i\n", (SInt32)theOutput, (SInt32)thePacket->fTimeArrived, DGetPacketSeqNumber( &thePacket->fPacketPtr ) );
+ }
+ else
+ printf("no new packets\n" );
+ #endif
+ }
+
+ OSQueueIter qIter(&fPacketQueue, packetElem); // starts from beginning if packetElem == NULL, else from packetElem
+
+ Bool16 dodBookmarkPacket = false;
+
+ while ( !qIter.IsDone() )
+ {
+ packetElem = qIter.GetCurrent();
+
+ ReflectorPacket* thePacket = (ReflectorPacket*)packetElem->GetEnclosingObject();
+ QTSS_Error err = QTSS_NoErr;
+
+ #if REFLECTOR_STREAM_DEBUGGING > 2
+ printf("packet time: %li, packetSeq %i\n", (SInt32)thePacket->fTimeArrived, DGetPacketSeqNumber( &thePacket->fPacketPtr ) );
+ #endif
+
+ // once we see a packet we cant' send, we need to stop trying
+ // during this pass mark remaining as still needed
+ if ( !dodBookmarkPacket )
+ {
+ SInt64 packetLateness = currentTime - thePacket->fTimeArrived - (ReflectorStream::sBucketDelayInMsec * (SInt64)bucketIndex);
+ // packetLateness measures how late this packet it after being corrected for the bucket delay
+
+ #if REFLECTOR_STREAM_DEBUGGING > 2
+ printf("packetLateness %li, seq# %li\n", (SInt32)packetLateness, (SInt32) DGetPacketSeqNumber( &thePacket->fPacketPtr ) );
+ #endif
+
+ SInt64 timeToSendPacket = -1;
+ err = theOutput->WritePacket(&thePacket->fPacketPtr, fStream, fWriteFlag, packetLateness, &timeToSendPacket, NULL, NULL, false);
+
+ if ( err == QTSS_WouldBlock )
+ {
+ #if REFLECTOR_STREAM_DEBUGGING > 2
+ printf("EAGAIN bookmark: %li, packetSeq %i\n", (SInt32)packetLateness, DGetPacketSeqNumber( &thePacket->fPacketPtr ) );
+ #endif
+ // tag it and bookmark it
+ thePacket->fNeededByOutput = true;
+
+ Assert( availBookmarksPosition != -1 );
+ if ( availBookmarksPosition != -1 )
+ theOutput->fBookmarkedPacketsElemsArray[availBookmarksPosition] = packetElem;
+
+ dodBookmarkPacket = true;
+
+ // call us again in # ms to retry on an EAGAIN
+ if ((timeToSendPacket > 0) && (fNextTimeToRun > timeToSendPacket ))
+ fNextTimeToRun = timeToSendPacket;
+ if ( timeToSendPacket == -1 )
+ this->SetNextTimeToRun(5); // keep in synch with delay on would block for on-demand lower is better for high-bit rate movies.
+
+ }
+ }
+ else
+ {
+ if ( thePacket->fNeededByOutput ) // optimization: if the packet is already marked, another Output has been through this already
+ break;
+ thePacket->fNeededByOutput = true;
+ }
+
+ qIter.Next();
+ }
+
+ }
+ }
+ }
+
+ // reset our first new packet bookmark
+ fFirstNewPacketInQueue = NULL;
+
+ // iterate one more through the senders queue to clear out
+ // the unneeded packets
+ OSQueueIter removeIter(&fPacketQueue);
+ while ( !removeIter.IsDone() )
+ {
+ OSQueueElem* elem = removeIter.GetCurrent();
+ Assert( elem );
+
+ //at this point, move onto the next queue element, because we may be altering
+ //the queue itself in the code below
+ removeIter.Next();
+
+ ReflectorPacket* thePacket = (ReflectorPacket*)elem->GetEnclosingObject();
+ Assert( thePacket );
+
+ if ( thePacket->fNeededByOutput == false )
+ {
+ thePacket->fNeededByOutput = true;
+ fPacketQueue.Remove( elem );
+ inFreeQueue->EnQueue( elem );
+
+ }
+ else // reset for next call to ReflectPackets
+ {
+ thePacket->fNeededByOutput = false;
+ }
+ }
+
+ //Don't forget that the caller also wants to know when we next want to run
+ if (*ioWakeupTime == 0)
+ *ioWakeupTime = fNextTimeToRun;
+ else if ((fNextTimeToRun > 0) && (*ioWakeupTime > fNextTimeToRun))
+ *ioWakeupTime = fNextTimeToRun;
+ // exit with fNextTimeToRun in real time, not relative time.
+ fNextTimeToRun += currentTime;
+
+ #if REFLECTOR_STREAM_DEBUGGING > 2
+ if ( printQueueLenOnExit )
+ printf( "EXIT fPacketQueue len %li\n", (SInt32)fPacketQueue.GetLength() );
+ #endif
+}
+
+/***********************************************************************************************
+/ ReflectorSender::ReflectPackets
+/
+/ There are n ReflectorSender's for n output streams per presentation.
+/
+/ Each sender is associated with an array of ReflectorOutput's. Each
+/ output represents a client connection. Each output has # RTPStream's.
+/
+/ When we write a packet to the ReflectorOutput he matches it's payload
+/ to one of his streams and sends it there.
+/
+/ To smooth the bandwitdth (server, not user) requirements of the reflected streams, the Sender
+/ groups the ReflectorOutput's into buckets. The input streams are reflected to
+/ each bucket progressively later in time. So rather than send a single packet
+/ to say 1000 clients all at once, we send it to just the first 16, then then next 16
+/ 100 ms later and so on.
+/
+/
+/ intputs ioWakeupTime - relative time to call us again in MSec
+/ inFreeQueue - queue of free packets.
+*/
+
+void ReflectorSender::ReflectPackets(SInt64* ioWakeupTime, OSQueue* inFreeQueue)
+{
+ if (!fStream->BufferEnabled()) // Call old routine for relays; they don't want buffering.
+ {
+ this->ReflectRelayPackets(ioWakeupTime,inFreeQueue);
+ return;
+ }
+
+ SInt64 currentTime = OS::Milliseconds();
+
+ //make sure to reset these state variables
+ fHasNewPackets = false;
+
+ fNextTimeToRun = 1000; // init to 1 secs
+
+ if (fWriteFlag == qtssWriteFlagsIsRTCP)
+ fNextTimeToRun = 1000;
+
+ //determine if we need to send a receiver report to the multicast source
+ if ((fWriteFlag == qtssWriteFlagsIsRTCP) && (currentTime > (fLastRRTime + kRRInterval)))
+ {
+ fLastRRTime = currentTime;
+ fStream->SendReceiverReport();
+ }
+
+ //the rest of this function must be atomic wrt the ReflectorSession, because
+ //it involves iterating through the RTPSession array, which isn't thread safe
+ OSMutexLocker locker(&fStream->fBucketMutex);
+
+ // Check to see if we should update the session's bitrate average
+ fStream->UpdateBitRate(currentTime);
+
+ // where to start new clients in the q
+ fFirstPacketInQueueForNewOutput = this->GetClientBufferStartPacketOffset(0);
+
+#if (0) //test code
+ if (NULL != fFirstPacketInQueueForNewOutput)
+ printf("ReflectorSender::ReflectPackets SET first packet fFirstPacketInQueueForNewOutput %d \n", DGetPacketSeqNumber( &( (ReflectorPacket*) ( fFirstPacketInQueueForNewOutput->GetEnclosingObject()))->fPacketPtr ));
+
+ ReflectorPacket* thePacket = NULL;
+ if (fFirstPacketInQueueForNewOutput != NULL)
+ thePacket = (ReflectorPacket*) fFirstPacketInQueueForNewOutput->GetEnclosingObject();
+ if (thePacket == NULL)
+ { printf("fFirstPacketInQueueForNewOutput is NULL \n");
+
+ }
+
+#endif
+
+ Bool16 firstPacket =false;
+
+ for (UInt32 bucketIndex = 0; bucketIndex < fStream->fNumBuckets; bucketIndex++)
+ {
+ for (UInt32 bucketMemberIndex = 0; bucketMemberIndex < fStream->sBucketSize; bucketMemberIndex++)
+ {
+ ReflectorOutput* theOutput = fStream->fOutputArray[bucketIndex][bucketMemberIndex];
+ if (theOutput != NULL)
+ {
+ if ( false == theOutput->IsPlaying() )
+ continue;
+
+ OSQueueElem* packetElem = theOutput->GetBookMarkedPacket(&fPacketQueue);
+ if ( packetElem == NULL ) // should only be a new output
+ {
+ packetElem = fFirstPacketInQueueForNewOutput; // everybody starts at the oldest packet in the buffer delay or uses a bookmark
+ firstPacket = true;
+ theOutput->fNewOutput = false;
+ //if (packetElem) printf("ReflectorSender::ReflectPackets Sending first packet in Queue packetElem=fFirstPacketInQueueForNewOutput %d \n", ( (ReflectorPacket*) (packetElem->GetEnclosingObject() ) )->GetPacketRTPSeqNum());
+
+ }
+
+ SInt64 bucketDelay = ReflectorStream::sBucketDelayInMsec * (SInt64)bucketIndex;
+ packetElem = this->SendPacketsToOutput(theOutput, packetElem,currentTime, bucketDelay, firstPacket);
+ if (packetElem)
+ {
+ ReflectorPacket* thePacket = (ReflectorPacket*)packetElem->GetEnclosingObject();
+ thePacket->fNeededByOutput = true; // flag to prevent removal in RemoveOldPackets
+ (void) theOutput->SetBookMarkPacket(packetElem); // store a reference to the packet
+ }
+ }
+ }
+ }
+
+ this->RemoveOldPackets(inFreeQueue);
+ fFirstNewPacketInQueue = NULL;
+
+ //Don't forget that the caller also wants to know when we next want to run
+ if (*ioWakeupTime == 0)
+ *ioWakeupTime = fNextTimeToRun;
+ else if ((fNextTimeToRun > 0) && (*ioWakeupTime > fNextTimeToRun))
+ *ioWakeupTime = fNextTimeToRun;
+ // exit with fNextTimeToRun in real time, not relative time.
+ fNextTimeToRun += currentTime;
+
+ // qtss_printf("SetNextTimeToRun fNextTimeToRun=%qd + currentTime=%qd\n", fNextTimeToRun, currentTime);
+ // qtss_printf("ReflectorSender::ReflectPackets *ioWakeupTime = %qd\n", *ioWakeupTime);
+
+}
+
+OSQueueElem* ReflectorSender::SendPacketsToOutput(ReflectorOutput* theOutput, OSQueueElem* currentPacket, SInt64 currentTime, SInt64 bucketDelay, Bool16 firstPacket)
+{
+ OSQueueElem* lastPacket = currentPacket;
+ OSQueueIter qIter(&fPacketQueue, currentPacket); // starts from beginning if currentPacket == NULL, else from currentPacket
+
+ UInt32 count = 0;
+ QTSS_Error err = QTSS_NoErr;
+ while ( !qIter.IsDone() )
+ {
+ currentPacket = qIter.GetCurrent();
+ lastPacket = currentPacket;
+
+ ReflectorPacket* thePacket = (ReflectorPacket*)currentPacket->GetEnclosingObject();
+ SInt64 packetLateness = bucketDelay;
+ SInt64 timeToSendPacket = -1;
+
+ //printf("packetLateness %qd, seq# %li\n", packetLateness, (SInt32) DGetPacketSeqNumber( &thePacket->fPacketPtr ) );
+
+ err = theOutput->WritePacket(&thePacket->fPacketPtr, fStream, fWriteFlag, packetLateness, &timeToSendPacket,&thePacket->fStreamCountID,&thePacket->fTimeArrived, firstPacket );
+
+ if (err == QTSS_WouldBlock)
+ { // call us again in # ms to retry on an EAGAIN
+
+ if ((timeToSendPacket > 0) && ( (fNextTimeToRun + currentTime) > timeToSendPacket )) // blocked but we are scheduled to wake up later
+ fNextTimeToRun = timeToSendPacket - currentTime;
+
+ if (theOutput->fLastIntervalMilliSec < 5 )
+ theOutput->fLastIntervalMilliSec = 5;
+
+ if ( timeToSendPacket < 0 ) // blocked and we are behind
+ { //qtss_printf("fNextTimeToRun = theOutput->fLastIntervalMilliSec=%qd;\n", theOutput->fLastIntervalMilliSec); // Use the last packet interval
+ this->SetNextTimeToRun(theOutput->fLastIntervalMilliSec);
+ }
+
+ if (fNextTimeToRun > 100) //don't wait that long
+ { //qtss_printf("fNextTimeToRun = %qd now 100;\n", fNextTimeToRun);
+ this->SetNextTimeToRun(100);
+ }
+
+ if (fNextTimeToRun < 5) //wait longer
+ { //qtss_printf("fNextTimeToRun = 5;\n");
+ this->SetNextTimeToRun(5);
+ }
+
+ if (theOutput->fLastIntervalMilliSec >= 100) // allow up to 1 second max -- allow some time for the socket to clear and don't go into a tight loop if the client is gone.
+ theOutput->fLastIntervalMilliSec = 100;
+ else
+ theOutput->fLastIntervalMilliSec *= 2; // scale upwards over time
+
+ //qtss_printf ( "Blocked ReflectorSender::SendPacketsToOutput timeToSendPacket=%qd fLastIntervalMilliSec=%qd fNextTimeToRun=%qd \n", timeToSendPacket, theOutput->fLastIntervalMilliSec, fNextTimeToRun);
+
+ break;
+ }
+
+ count++;
+ qIter.Next();
+
+ }
+
+ return lastPacket;
+}
+
+OSQueueElem* ReflectorSender::GetClientBufferStartPacketOffset(SInt64 offsetMsec)
+{
+
+ OSQueueIter qIter(&fPacketQueue);// start at oldest packet in q
+ SInt64 theCurrentTime = OS::Milliseconds();
+ SInt64 packetDelay = 0;
+ OSQueueElem* oldestPacketInClientBufferTime = NULL;
+
+
+ while ( !qIter.IsDone() ) // start at oldest packet in q
+ {
+ OSQueueElem* elem = qIter.GetCurrent();
+ Assert( elem );
+ qIter.Next();
+
+ ReflectorPacket* thePacket = (ReflectorPacket*)elem->GetEnclosingObject();
+ Assert( thePacket );
+
+ packetDelay = theCurrentTime - thePacket->fTimeArrived;
+ if (offsetMsec > ReflectorStream::sOverBufferInMsec)
+ offsetMsec = ReflectorStream::sOverBufferInMsec;
+
+ if ( packetDelay <= (ReflectorStream::sOverBufferInMsec - offsetMsec) )
+ {
+ oldestPacketInClientBufferTime = &thePacket->fQueueElem;
+ break; // found the packet we need: done processing
+ }
+
+ }
+
+ return oldestPacketInClientBufferTime;
+}
+
+void ReflectorSender::RemoveOldPackets(OSQueue* inFreeQueue)
+{
+
+// Iterate through the senders queue to clear out packets
+// Start at the oldest packet and walk forward to the newest packet
+//
+ OSQueueIter removeIter(&fPacketQueue);
+ SInt64 theCurrentTime = OS::Milliseconds();
+ SInt64 packetDelay = 0;
+ SInt64 currentMaxPacketDelay = ReflectorStream::sMaxPacketAgeMSec;
+
+
+ while ( !removeIter.IsDone() )
+ {
+ OSQueueElem* elem = removeIter.GetCurrent();
+ Assert( elem );
+
+ //at this point, move onto the next queue element, because we may be altering
+ //the queue itself in the code below
+ removeIter.Next();
+
+ ReflectorPacket* thePacket = (ReflectorPacket*)elem->GetEnclosingObject();
+ Assert( thePacket );
+ //printf("ReflectorSender::RemoveOldPackets Packet %d in queue is %qd milliseconds old\n", DGetPacketSeqNumber( &thePacket->fPacketPtr ) ,theCurrentTime - thePacket->fTimeArrived);
+
+
+ packetDelay = theCurrentTime - thePacket->fTimeArrived;
+
+ // walk q and remove packets that are too old
+ if ( !thePacket->fNeededByOutput && packetDelay > currentMaxPacketDelay) // delete based on late tolerance and whether a client is blocked on the packet
+ { // not needed and older than our required buffer
+ thePacket->Reset();
+ fPacketQueue.Remove( elem );
+ inFreeQueue->EnQueue( elem );
+ }
+ else
+ { // we want to keep all of these but we should reset the ones that should be aged out unless marked
+ // as need the next time through reflect packets.
+
+ thePacket->fNeededByOutput = false; //mark not needed.. will be set next time through reflect packets
+ if (packetDelay <= currentMaxPacketDelay) // this packet is going to be kept around as well as the ones that follow.
+ break;
+ }
+ }
+
+
+}
+
+void ReflectorSocketPool::SetUDPSocketOptions(UDPSocketPair* inPair)
+{
+ // Fix add ReuseAddr for compatibility with MPEG4IP broadcaster which likes to use the same
+ //sockets.
+
+ //Make sure this works with PlaylistBroadcaster
+ //inPair->GetSocketA()->ReuseAddr();
+ //inPair->GetSocketA()->ReuseAddr();
+
+}
+
+
+UDPSocketPair* ReflectorSocketPool::ConstructUDPSocketPair()
+{
+ return NEW UDPSocketPair
+ (NEW ReflectorSocket(), NEW ReflectorSocket());
+}
+
+void ReflectorSocketPool::DestructUDPSocket(ReflectorSocket* socket)
+{
+ if (socket) // allocated
+ {
+ if (socket->GetLocalPort() > 0) // bound and active
+ { //The socket's run function may be executing RIGHT NOW! So we can't
+ //just delete the thing, we need to send the sockets kill events.
+ //qtss_printf("ReflectorSocketPool::DestructUDPSocketPair Signal kKillEvent socket=%p\n",socket);
+ socket->Signal(Task::kKillEvent);
+ }
+ else // not bound ok to delete
+ { //qtss_printf("ReflectorSocketPool::DestructUDPSocketPair delete socket=%p\n",socket);
+ delete socket;
+ }
+ }
+}
+
+void ReflectorSocketPool::DestructUDPSocketPair(UDPSocketPair *inPair)
+{
+ //qtss_printf("ReflectorSocketPool::DestructUDPSocketPair inPair=%p socketA=%p\n", inPair,(ReflectorSocket*)inPair->GetSocketA());
+ this->DestructUDPSocket((ReflectorSocket*)inPair->GetSocketA());
+
+ //qtss_printf("ReflectorSocketPool::DestructUDPSocketPair inPair=%p socketB=%p\n", inPair,(ReflectorSocket*)inPair->GetSocketB());
+ this->DestructUDPSocket((ReflectorSocket*)inPair->GetSocketB());
+
+ delete inPair;
+}
+
+ReflectorSocket::ReflectorSocket()
+: IdleTask(),
+ UDPSocket(NULL, Socket::kNonBlockingSocketType | UDPSocket::kWantsDemuxer),
+ fBroadcasterClientSession(NULL),
+ fLastBroadcasterTimeOutRefresh(0),
+ fSleepTime(0),
+ fValidSSRC(0),
+ fLastValidSSRCTime(0),
+ fFilterSSRCs(true),
+ fTimeoutSecs(30),
+ fHasReceiveTime(false),
+ fFirstReceiveTime(0),
+ fFirstArrivalTime(0),
+ fCurrentSSRC(0)
+
+{
+ //construct all the preallocated packets
+ this->SetTaskName("ReflectorSocket");
+ this->SetTask(this);
+
+ for (UInt32 numPackets = 0; numPackets < kNumPreallocatedPackets; numPackets++)
+ {
+ //If the local port # of this socket is odd, then all the packets
+ //used for this socket are rtcp packets.
+ ReflectorPacket* packet = NEW ReflectorPacket();
+ fFreeQueue.EnQueue(&packet->fQueueElem);//put this packet onto the free queue
+ }
+}
+
+ReflectorSocket::~ReflectorSocket()
+{
+ //printf("ReflectorSocket::~ReflectorSocket\n");
+ while (fFreeQueue.GetLength() > 0)
+ {
+ ReflectorPacket* packet = (ReflectorPacket*)fFreeQueue.DeQueue()->GetEnclosingObject();
+ delete packet;
+ }
+}
+
+void ReflectorSocket::AddSender(ReflectorSender* inSender)
+{
+ OSMutexLocker locker(this->GetDemuxer()->GetMutex());
+ QTSS_Error err = this->GetDemuxer()->RegisterTask(inSender->fStream->fStreamInfo.fSrcIPAddr, 0, inSender);
+ Assert(err == QTSS_NoErr);
+ fSenderQueue.EnQueue(&inSender->fSocketQueueElem);
+}
+
+void ReflectorSocket::RemoveSender(ReflectorSender* inSender)
+{
+ OSMutexLocker locker(this->GetDemuxer()->GetMutex());
+ fSenderQueue.Remove(&inSender->fSocketQueueElem);
+ QTSS_Error err = this->GetDemuxer()->UnregisterTask(inSender->fStream->fStreamInfo.fSrcIPAddr, 0, inSender);
+ Assert(err == QTSS_NoErr);
+}
+
+SInt64 ReflectorSocket::Run()
+{
+ //We want to make sure we can't get idle events WHILE we are inside
+ //this function. That will cause us to run the queues unnecessarily
+ //and just get all confused.
+ this->CancelTimeout();
+
+ Task::EventFlags theEvents = this->GetEvents();
+ //if we have been told to delete ourselves, do so.
+ if (theEvents & Task::kKillEvent)
+ return -1;
+
+ OSMutexLocker locker(this->GetDemuxer()->GetMutex());
+ SInt64 theMilliseconds = OS::Milliseconds();
+
+ //Only check for data on the socket if we've actually been notified to that effect
+ if (theEvents & Task::kReadEvent)
+ this->GetIncomingData(theMilliseconds);
+
+#if DEBUG
+ //make sure that we haven't gotten here prematurely! This wouldn't mess
+ //anything up, but it would waste CPU.
+ if (theEvents & Task::kIdleEvent)
+ {
+ SInt32 temp = (SInt32)(fSleepTime - theMilliseconds);
+ char tempBuf[20];
+ qtss_sprintf(tempBuf,"%"_S32BITARG_"",temp);
+ WarnV(fSleepTime <= theMilliseconds, tempBuf);
+ }
+#endif
+
+ fSleepTime = 0;
+ //Now that we've gotten all available packets, have the streams reflect
+ for (OSQueueIter iter2(&fSenderQueue); !iter2.IsDone(); iter2.Next())
+ {
+ ReflectorSender* theSender2 = (ReflectorSender*)iter2.GetCurrent()->GetEnclosingObject();
+ if (theSender2 != NULL && theSender2->ShouldReflectNow(theMilliseconds, &fSleepTime))
+ theSender2->ReflectPackets(&fSleepTime, &fFreeQueue);
+ }
+
+#if DEBUG
+ theMilliseconds = OS::Milliseconds();
+#endif
+
+ //For smoothing purposes, the streams can mark when they want to wakeup.
+ if (fSleepTime > 0)
+ this->SetIdleTimer(fSleepTime);
+#if DEBUG
+ //The debugging check above expects real time.
+ fSleepTime += theMilliseconds;
+#endif
+
+ return 0;
+}
+
+
+void ReflectorSocket::FilterInvalidSSRCs(ReflectorPacket* thePacket,Bool16 isRTCP)
+{ // assume the first SSRC we see is valid and all others are to be ignored.
+ if ( thePacket->fPacketPtr.Len > 0) do
+ {
+ SInt64 currentTime = OS::Milliseconds() / 1000;
+ if (0 == fValidSSRC)
+ { fValidSSRC = thePacket->GetSSRC(isRTCP); // SSRC of 0 is allowed
+ fLastValidSSRCTime = currentTime;
+ //qtss_printf("socket=%"_U32BITARG_" FIRST PACKET fValidSSRC=%"_U32BITARG_" \n", (UInt32) this,fValidSSRC);
+ break;
+ }
+
+ UInt32 packetSSRC = thePacket->GetSSRC(isRTCP);
+ if (packetSSRC != 0)
+ {
+ if (packetSSRC == fValidSSRC)
+ { fLastValidSSRCTime = currentTime;
+ //qtss_printf("socket=%"_U32BITARG_" good packet\n", (UInt32) this );
+ break;
+ }
+
+ //qtss_printf("socket=%"_U32BITARG_" bad packet packetSSRC= %"_U32BITARG_" fValidSSRC=%"_U32BITARG_" \n", (UInt32) this,packetSSRC,fValidSSRC);
+ thePacket->fPacketPtr.Len = 0; // ignore this packet wrong SSRC
+ }
+
+ // this executes whenever an invalid SSRC is found -- maybe the original stream ended and a new one is now active
+ if ( (fLastValidSSRCTime + fTimeoutSecs) < currentTime) // fValidSSRC timed out --no packets with this SSRC seen for awhile
+ { fValidSSRC = 0; // reset the valid SSRC with the next packet's SSRC
+ //qtss_printf("RESET fValidSSRC\n");
+ }
+
+ }while (false);
+}
+
+Bool16 ReflectorSocket::ProcessPacket(const SInt64& inMilliseconds,ReflectorPacket* thePacket,UInt32 theRemoteAddr,UInt16 theRemotePort)
+{
+ Bool16 done = false; // stop when result is true
+ if (thePacket != NULL) do
+ {
+ if (GetLocalPort() & 1)
+ thePacket->fIsRTCP = true;
+ else
+ thePacket->fIsRTCP = false;
+
+ if (fBroadcasterClientSession != NULL) // alway refresh timeout even if we are filtering.
+ { if ( (inMilliseconds - fLastBroadcasterTimeOutRefresh) > kRefreshBroadcastSessionIntervalMilliSecs)
+ { QTSS_RefreshTimeOut(fBroadcasterClientSession);
+ fLastBroadcasterTimeOutRefresh = inMilliseconds;
+ }
+ }
+
+
+ if (thePacket->fPacketPtr.Len == 0)
+ {
+ //put the packet back on the free queue, because we didn't actually
+ //get any data here.
+ fFreeQueue.EnQueue(&thePacket->fQueueElem);
+ this->RequestEvent(EV_RE);
+ done = true;
+ //qtss_printf("ReflectorSocket::ProcessPacket no more packets on this socket!\n");
+ break;//no more packets on this socket!
+ }
+
+ if (thePacket->IsRTCP())
+ {
+ //if this is a new RTCP packet, check to see if it is a sender report.
+ //We should only reflect sender reports. Because RTCP packets can't have both
+ //an SR & an RR, and because the SR & the RR must be the first packet in a
+ //compound RTCP packet, all we have to do to determine this is look at the
+ //packet type of the first packet in the compound packet.
+ RTCPPacket theRTCPPacket;
+ if ((!theRTCPPacket.ParsePacket((UInt8*)thePacket->fPacketPtr.Ptr, thePacket->fPacketPtr.Len)) ||
+ (theRTCPPacket.GetPacketType() != RTCPSRPacket::kSRPacketType))
+ {
+ //pretend as if we never got this packet
+ fFreeQueue.EnQueue(&thePacket->fQueueElem);
+ done = true;
+ break;
+ }
+ }
+
+ // Only reflect one SSRC stream at a time.
+ // Pass the packet and whether it is an RTCP or RTP packet based on the port number.
+ if (fFilterSSRCs)
+ this->FilterInvalidSSRCs(thePacket,GetLocalPort() & 1);// thePacket->fPacketPtr.Len is set to 0 for invalid SSRCs.
+
+ // Find the appropriate ReflectorSender for this packet.
+ ReflectorSender* theSender = (ReflectorSender*)this->GetDemuxer()->GetTask(theRemoteAddr, 0);
+ // If there is a generic sender for this socket, use it.
+ if (theSender == NULL)
+ theSender = (ReflectorSender*)this->GetDemuxer()->GetTask(0, 0);
+
+ if (theSender == NULL)
+ {
+ //UInt16* theSeqNumberP = (UInt16*)thePacket->fPacketPtr.Ptr;
+ //qtss_printf("ReflectorSocket::ProcessPacket no sender found for packet! sequence number=%d\n",ntohs(theSeqNumberP[1]));
+ fFreeQueue.EnQueue(&thePacket->fQueueElem); // don't process the packet
+ done = true;
+ break;
+ }
+
+ Assert(theSender != NULL); // at this point we have a sender
+
+ const UInt32 maxQSize = 4000;
+
+ // Check to see if we need to set the remote RTCP address
+ // for this stream. This will be necessary if the source is unicast.
+#ifdef NAT_WORKAROUND
+ if ((theRemoteAddr != 0) && ((theSender->fStream->fDestRTCPAddr == 0) || (thePacket->IsRTCP()))) // Submitted fix from denis@berlin.ccc.de
+ {
+ Assert(!SocketUtils::IsMulticastIPAddr(theSender->fStream->fStreamInfo.fDestIPAddr));
+ Assert(theRemotePort != 0);
+ theSender->fStream->fDestRTCPAddr = theRemoteAddr;
+ theSender->fStream->fDestRTCPPort = theRemotePort;
+
+ // RTCPs are always on odd ports, so check to see if this port is an
+ // RTP port, and if so, just add 1.
+ if (!(thePacket->IsRTCP()) && !(theRemotePort & 1))
+ theSender->fStream->fDestRTCPPort++;
+ }
+#else
+ if ((theRemoteAddr != 0) && (theSender->fStream->fDestRTCPAddr == 0))
+ {
+ // If the source is multicast, this shouldn't be necessary
+ Assert(!SocketUtils::IsMulticastIPAddr(theSender->fStream->fStreamInfo.fDestIPAddr));
+ Assert(theRemotePort != 0);
+ theSender->fStream->fDestRTCPAddr = theRemoteAddr;
+ theSender->fStream->fDestRTCPPort = theRemotePort;
+
+ // RTCPs are always on odd ports, so check to see if this port is an
+ // RTP port, and if so, just add 1.
+ if (!(theRemotePort & 1))
+ theSender->fStream->fDestRTCPPort++;
+ }
+#endif //NAT_WORKAROUND
+ thePacket->fStreamCountID = ++(theSender->fStream->fPacketCount);
+ thePacket->fBucketsSeenThisPacket = 0;
+ thePacket->fTimeArrived = inMilliseconds;
+ theSender->fPacketQueue.EnQueue(&thePacket->fQueueElem);
+ if ( theSender->fFirstNewPacketInQueue == NULL )
+ theSender->fFirstNewPacketInQueue = &thePacket->fQueueElem;
+ theSender->fHasNewPackets = true;
+
+
+ if (!(thePacket->IsRTCP()))
+ {
+ // don't check for duplicate packets, they may be needed to keep in sync.
+ // Because this is an RTP packet make sure to atomic add this because
+ // multiple sockets can be adding to this variable simultaneously
+ (void)atomic_add(&theSender->fStream->fBytesSentInThisInterval, thePacket->fPacketPtr.Len);
+ //printf("ReflectorSocket::ProcessPacket received RTP id=%qu\n", thePacket->fStreamCountID);
+ theSender->fStream->SetHasFirstRTP(true);
+ }
+ else
+ {
+ //printf("ReflectorSocket::ProcessPacket received RTCP id=%qu\n", thePacket->fStreamCountID);
+ theSender->fStream->SetHasFirstRTCP(true);
+ theSender->fStream->SetFirst_RTCP_RTP_Time(thePacket->GetPacketRTPTime());
+ theSender->fStream->SetFirst_RTCP_Arrival_Time(thePacket->fTimeArrived);
+ }
+
+
+ if (ReflectorStream::sUsePacketReceiveTime && thePacket->fPacketPtr.Len > 12)
+ {
+ UInt32 offset = thePacket->fPacketPtr.Len;
+ char* theTag = ((char*) thePacket->fPacketPtr.Ptr + offset) - 12;
+ UInt64* theValue = (UInt64*) ((char*) ( (char*) thePacket->fPacketPtr.Ptr + offset) - 8);
+
+ if (0 == ::strncmp(theTag,"aktt",4))
+ {
+ UInt64 theReceiveTime = OS::NetworkToHostSInt64(*theValue);
+ UInt32 theSSRC = thePacket->GetSSRC(theRemotePort & 1); // use to check if broadcast has restarted so we can reset
+
+ if ( !this->fHasReceiveTime || (this->fCurrentSSRC != theSSRC) )
+ {
+ this->fCurrentSSRC = theSSRC;
+ this->fFirstArrivalTime = thePacket->fTimeArrived;
+ this->fFirstReceiveTime = theReceiveTime;
+ this->fHasReceiveTime = true;
+ }
+
+
+ SInt64 packetOffsetFromStart = theReceiveTime - this->fFirstReceiveTime; // packets arrive at time 0 and fill forward into the future
+ thePacket->fTimeArrived = this->fFirstArrivalTime + packetOffsetFromStart; // offset starts negative by over buffer amount
+ thePacket->fPacketPtr.Len -= 12;
+
+ SInt64 arrivalTimeOffset = thePacket->fTimeArrived - inMilliseconds;
+ if ( arrivalTimeOffset > ReflectorStream::sMaxFuturePacketMSec ) // way out in the future.
+ thePacket->fTimeArrived = inMilliseconds + ReflectorStream::sMaxFuturePacketMSec; //keep it but only for sMaxFuturePacketMSec = (sMaxPacketAgeMSec <-- current --> sMaxFuturePacketMSec)
+
+ // if it was in the past we leave it alone because it will be deleted after processing.
+
+
+ //printf("ReflectorSocket::ProcessPacket packetOffsetFromStart=%f\n", (Float32) packetOffsetFromStart / 1000);
+ }
+
+ }
+
+ //printf("ReflectorSocket::GetIncomingData has packet from time=%qd src addr=%"_U32BITARG_" src port=%u packetlen=%"_U32BITARG_"\n",inMilliseconds, theRemoteAddr,theRemotePort,thePacket->fPacketPtr.Len);
+ if (0) //turn on / off buffer size checking -- pref can go here if we find we need to adjust this
+ if (theSender->fPacketQueue.GetLength() > maxQSize) //don't grow memory too big
+ {
+ char outMessage[256];
+ sprintf(outMessage,"Packet Queue for port=%d qsize = %"_S32BITARG_" hit max qSize=%"_U32BITARG_"", theRemotePort,theSender->fPacketQueue.GetLength(), maxQSize);
+ WarnV(false, outMessage);
+ }
+
+
+
+ } while(false);
+
+ return done;
+}
+
+
+void ReflectorSocket::GetIncomingData(const SInt64& inMilliseconds)
+{
+ OSMutexLocker locker(this->GetDemuxer()->GetMutex());
+ UInt32 theRemoteAddr = 0;
+ UInt16 theRemotePort = 0;
+ //get all the outstanding packets for this socket
+ while (true)
+ {
+ //get a packet off the free queue.
+ ReflectorPacket* thePacket = this->GetPacket();
+
+ thePacket->fPacketPtr.Len = 0;
+ (void)this->RecvFrom(&theRemoteAddr, &theRemotePort, thePacket->fPacketPtr.Ptr,
+ ReflectorPacket::kMaxReflectorPacketSize, &thePacket->fPacketPtr.Len);
+
+ if (this->ProcessPacket(inMilliseconds,thePacket,theRemoteAddr, theRemotePort))
+ break;
+
+ //printf("ReflectorSocket::GetIncomingData \n");
+ }
+
+}
+
+
+
+
+ReflectorPacket* ReflectorSocket::GetPacket()
+{
+ OSMutexLocker locker(this->GetDemuxer()->GetMutex());
+ if (fFreeQueue.GetLength() == 0)
+ //if the port number of this socket is odd, this packet is an RTCP packet.
+ return NEW ReflectorPacket();
+ else
+ return (ReflectorPacket*)fFreeQueue.DeQueue()->GetEnclosingObject();
+}
diff --git a/APIModules/QTSSReflectorModule/ReflectorStream.h b/APIModules/QTSSReflectorModule/ReflectorStream.h
new file mode 100644
index 0000000..0313cd1
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/ReflectorStream.h
@@ -0,0 +1,513 @@
+/*
+ *
+ * @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: ReflectorStream.h
+
+ Contains: This object supports reflecting an RTP multicast stream to N
+ RTPStreams. It spaces out the packet send times in order to
+ maximize the randomness of the sending pattern and smooth
+ the stream.
+
+
+
+*/
+
+#ifndef _REFLECTOR_STREAM_H_
+#define _REFLECTOR_STREAM_H_
+
+#include "QTSS.h"
+
+#include "IdleTask.h"
+#include "SourceInfo.h"
+
+#include "UDPSocket.h"
+#include "UDPSocketPool.h"
+#include "UDPDemuxer.h"
+#include "EventContext.h"
+#include "SequenceNumberMap.h"
+
+#include "OSMutex.h"
+#include "OSQueue.h"
+#include "OSRef.h"
+
+#include "RTCPSRPacket.h"
+#include "ReflectorOutput.h"
+#include "atomic.h"
+
+//This will add some printfs that are useful for checking the thinning
+#define REFLECTOR_THINNING_DEBUGGING 0
+
+//Define to use new potential workaround for NAT problems
+#define NAT_WORKAROUND 1
+
+class ReflectorPacket;
+class ReflectorSender;
+class ReflectorStream;
+class RTPSessionOutput;
+
+class ReflectorPacket
+{
+ public:
+
+ ReflectorPacket() : fQueueElem() { fQueueElem.SetEnclosingObject(this); this->Reset();}
+ void Reset() { // make packet ready to reuse fQueueElem is always in use
+ fBucketsSeenThisPacket = 0;
+ fTimeArrived = 0;
+ //fQueueElem -- should be set to this
+ fPacketPtr.Set(fPacketData, 0);
+ fIsRTCP = false;
+ fStreamCountID = 0;
+ fNeededByOutput = false;
+ }
+
+ ~ReflectorPacket() {}
+
+ void SetPacketData(char *data, UInt32 len) { Assert(kMaxReflectorPacketSize > len); if (len > 0) memcpy(this->fPacketPtr.Ptr,data,len); this->fPacketPtr.Len = len;}
+ Bool16 IsRTCP() { return fIsRTCP; }
+inline UInt32 GetPacketRTPTime();
+inline UInt16 GetPacketRTPSeqNum();
+inline UInt32 GetSSRC(Bool16 isRTCP);
+inline SInt64 GetPacketNTPTime();
+
+ private:
+
+ enum
+ {
+ kMaxReflectorPacketSize = 2060 //jm 5/02 increased from 2048 by 12 bytes for test bytes appended to packets
+ };
+
+ UInt32 fBucketsSeenThisPacket;
+ SInt64 fTimeArrived;
+ OSQueueElem fQueueElem;
+ char fPacketData[kMaxReflectorPacketSize];
+ StrPtrLen fPacketPtr;
+ Bool16 fIsRTCP;
+ Bool16 fNeededByOutput; // is this packet still needed for output?
+ UInt64 fStreamCountID;
+
+ friend class ReflectorSender;
+ friend class ReflectorSocket;
+ friend class RTPSessionOutput;
+
+
+};
+
+UInt32 ReflectorPacket::GetSSRC(Bool16 isRTCP)
+{
+ if (fPacketPtr.Ptr == NULL || fPacketPtr.Len < 8)
+ return 0;
+
+ UInt32* theSsrcPtr = (UInt32*)fPacketPtr.Ptr;
+ if (isRTCP)// RTCP
+ return ntohl(theSsrcPtr[1]);
+
+ if (fPacketPtr.Len < 12)
+ return 0;
+
+ return ntohl(theSsrcPtr[2]); // RTP SSRC
+}
+
+UInt32 ReflectorPacket::GetPacketRTPTime()
+{
+
+ UInt32 timestamp = 0;
+ if (!fIsRTCP)
+ {
+ //The RTP timestamp number is the second long of the packet
+ if (fPacketPtr.Ptr == NULL || fPacketPtr.Len < 8)
+ return 0;
+ timestamp = ntohl( ((UInt32*)fPacketPtr.Ptr)[1]);
+ }
+ else
+ {
+ if (fPacketPtr.Ptr == NULL || fPacketPtr.Len < 20)
+ return 0;
+ timestamp = ntohl( ((UInt32*)fPacketPtr.Ptr)[4]);
+ }
+ return timestamp;
+}
+
+UInt16 ReflectorPacket::GetPacketRTPSeqNum()
+{
+ Assert(!fIsRTCP); // not a supported type
+
+ if (fPacketPtr.Ptr == NULL || fPacketPtr.Len < 4 || fIsRTCP)
+ return 0;
+
+ UInt16 sequence = ntohs( ((UInt16*)fPacketPtr.Ptr)[1]); //The RTP sequenc number is the second short of the packet
+ return sequence;
+}
+
+
+SInt64 ReflectorPacket::GetPacketNTPTime()
+{
+ Assert(fIsRTCP); // not a supported type
+ if (fPacketPtr.Ptr == NULL || fPacketPtr.Len < 16 || !fIsRTCP)
+ return 0;
+
+ UInt32* theReport = (UInt32*)fPacketPtr.Ptr;
+ theReport +=2;
+ SInt64 ntp = 0;
+ ::memcpy(&ntp, theReport, sizeof(SInt64));
+
+ return OS::Time1900Fixed64Secs_To_TimeMilli(OS::NetworkToHostSInt64(ntp));
+
+
+}
+
+
+//Custom UDP socket classes for doing reflector packet retrieval, socket management
+class ReflectorSocket : public IdleTask, public UDPSocket
+{
+ public:
+
+ ReflectorSocket();
+ virtual ~ReflectorSocket();
+ void AddBroadcasterSession(QTSS_ClientSessionObject inSession) { OSMutexLocker locker(this->GetDemuxer()->GetMutex()); fBroadcasterClientSession = inSession; }
+ void RemoveBroadcasterSession(QTSS_ClientSessionObject inSession){ OSMutexLocker locker(this->GetDemuxer()->GetMutex()); if (inSession == fBroadcasterClientSession) fBroadcasterClientSession = NULL; }
+ void AddSender(ReflectorSender* inSender);
+ void RemoveSender(ReflectorSender* inStreamElem);
+ Bool16 HasSender() { return (this->GetDemuxer()->GetHashTable()->GetNumEntries() > 0); }
+ Bool16 ProcessPacket(const SInt64& inMilliseconds,ReflectorPacket* thePacket,UInt32 theRemoteAddr,UInt16 theRemotePort);
+ ReflectorPacket* GetPacket();
+ virtual SInt64 Run();
+ void SetSSRCFilter(Bool16 state, UInt32 timeoutSecs) { fFilterSSRCs = state; fTimeoutSecs = timeoutSecs;}
+ private:
+
+ //virtual SInt64 Run();
+ void GetIncomingData(const SInt64& inMilliseconds);
+ void FilterInvalidSSRCs(ReflectorPacket* thePacket,Bool16 isRTCP);
+
+ //Number of packets to allocate when the socket is first created
+ enum
+ {
+ kNumPreallocatedPackets = 20, //UInt32
+ kRefreshBroadcastSessionIntervalMilliSecs = 10000,
+ kSSRCTimeOut = 30000 // milliseconds before clearing the SSRC if no new ssrcs have come in
+ };
+ QTSS_ClientSessionObject fBroadcasterClientSession;
+ SInt64 fLastBroadcasterTimeOutRefresh;
+ // Queue of available ReflectorPackets
+ OSQueue fFreeQueue;
+ // Queue of senders
+ OSQueue fSenderQueue;
+ SInt64 fSleepTime;
+
+ UInt32 fValidSSRC;
+ SInt64 fLastValidSSRCTime;
+ Bool16 fFilterSSRCs;
+ UInt32 fTimeoutSecs;
+
+ Bool16 fHasReceiveTime;
+ UInt64 fFirstReceiveTime;
+ SInt64 fFirstArrivalTime;
+ UInt32 fCurrentSSRC;
+
+};
+
+
+class ReflectorSocketPool : public UDPSocketPool
+{
+ public:
+
+ ReflectorSocketPool() {}
+ virtual ~ReflectorSocketPool() {}
+
+ virtual UDPSocketPair* ConstructUDPSocketPair();
+ virtual void DestructUDPSocketPair(UDPSocketPair *inPair);
+ virtual void SetUDPSocketOptions(UDPSocketPair* inPair);
+ void DestructUDPSocket( ReflectorSocket* socket);
+
+
+};
+
+class ReflectorSender : public UDPDemuxerTask
+{
+ public:
+ ReflectorSender(ReflectorStream* inStream, UInt32 inWriteFlag);
+ virtual ~ReflectorSender();
+ // Queue of senders
+ OSQueue fSenderQueue;
+ SInt64 fSleepTime;
+
+ //Used for adjusting sequence numbers in light of thinning
+ UInt16 GetPacketSeqNumber(const StrPtrLen& inPacket);
+ void SetPacketSeqNumber(const StrPtrLen& inPacket, UInt16 inSeqNumber);
+ Bool16 PacketShouldBeThinned(QTSS_RTPStreamObject inStream, const StrPtrLen& inPacket);
+
+ //We want to make sure that ReflectPackets only gets invoked when there
+ //is actually work to do, because it is an expensive function
+ Bool16 ShouldReflectNow(const SInt64& inCurrentTime, SInt64* ioWakeupTime);
+
+ //This function gets data from the multicast source and reflects.
+ //Returns the time at which it next needs to be invoked
+ void ReflectPackets(SInt64* ioWakeupTime, OSQueue* inFreeQueue);
+
+ //this is the old way of doing reflect packets. It is only here until the relay code can be cleaned up.
+ void ReflectRelayPackets(SInt64* ioWakeupTime, OSQueue* inFreeQueue);
+
+ OSQueueElem* SendPacketsToOutput(ReflectorOutput* theOutput, OSQueueElem* currentPacket, SInt64 currentTime, SInt64 bucketDelay, Bool16 firstPacket);
+
+ UInt32 GetOldestPacketRTPTime(Bool16 *foundPtr);
+ UInt16 GetFirstPacketRTPSeqNum(Bool16 *foundPtr);
+ Bool16 GetFirstPacketInfo(UInt16* outSeqNumPtr, UInt32* outRTPTimePtr, SInt64* outArrivalTimePtr);
+
+ OSQueueElem*GetClientBufferNextPacketTime(UInt32 inRTPTime);
+ Bool16 GetFirstRTPTimePacket(UInt16* outSeqNumPtr, UInt32* outRTPTimePtr, SInt64* outArrivalTimePtr);
+
+ void RemoveOldPackets(OSQueue* inFreeQueue);
+ OSQueueElem* GetClientBufferStartPacketOffset(SInt64 offsetMsec);
+ OSQueueElem* GetClientBufferStartPacket() { return this->GetClientBufferStartPacketOffset(0); };
+
+ ReflectorStream* fStream;
+ UInt32 fWriteFlag;
+
+ OSQueue fPacketQueue;
+ OSQueueElem* fFirstNewPacketInQueue;
+ OSQueueElem* fFirstPacketInQueueForNewOutput;
+
+ //these serve as an optimization, keeping track of when this
+ //sender needs to run so it doesn't run unnecessarily
+
+ inline void SetNextTimeToRun(SInt64 nextTime) { fNextTimeToRun = nextTime;
+ //qtss_printf("SetNextTimeToRun =%"_64BITARG_"d\n", fNextTimeToRun);
+ }
+
+ Bool16 fHasNewPackets;
+ SInt64 fNextTimeToRun;
+
+ //how often to send RRs to the source
+ enum
+ {
+ kRRInterval = 5000 //SInt64 (every 5 seconds)
+ };
+
+ SInt64 fLastRRTime;
+ OSQueueElem fSocketQueueElem;
+
+ friend class ReflectorSocket;
+ friend class ReflectorStream;
+};
+
+class ReflectorStream
+{
+ public:
+
+ enum
+ {
+ // A ReflectorStream is uniquely identified by the
+ // destination IP address & destination port of the broadcast.
+ // This ID simply contains that information.
+ //
+ // A unicast broadcast can also be identified by source IP address. If
+ // you are attempting to demux by source IP, this ID will not guarentee
+ // uniqueness and special care should be used.
+ kStreamIDSize = sizeof(UInt32) + sizeof(UInt16)
+ };
+
+ // Uses a StreamInfo to generate a unique ID
+ static void GenerateSourceID(SourceInfo::StreamInfo* inInfo, char* ioBuffer);
+
+ ReflectorStream(SourceInfo::StreamInfo* inInfo);
+ ~ReflectorStream();
+
+ //
+ // SETUP
+ //
+ // Call Register from the Register role, as this object has some QTSS API
+ // attributes to setup
+ static void Register();
+ static void Initialize(QTSS_ModulePrefsObject inPrefs);
+
+ //
+ // MODIFIERS
+
+ // Call this to initialize the reflector sockets. Uses the QTSS_RTSPRequestObject
+ // if provided to report any errors that occur
+ // Passes the QTSS_ClientSessionObject to the socket so the socket can update the session if needed.
+ QTSS_Error BindSockets(QTSS_StandardRTSP_Params* inParams, UInt32 inReflectorSessionFlags, Bool16 filterState, UInt32 timeout);
+
+ // This stream reflects packets from the broadcast to specific ReflectorOutputs.
+ // You attach outputs to ReflectorStreams this way. You can force the ReflectorStream
+ // to put this output into a certain bucket by passing in a certain bucket index.
+ // Pass in -1 if you don't care. AddOutput returns the bucket index this output was
+ // placed into, or -1 on an error.
+
+ SInt32 AddOutput(ReflectorOutput* inOutput, SInt32 putInThisBucket);
+
+ // Removes the specified output from this ReflectorStream.
+ void RemoveOutput(ReflectorOutput* inOutput); // Removes this output from all tracks
+
+ void TearDownAllOutputs(); // causes a tear down and then a remove
+
+ // If the incoming data is RTSP interleaved, packets for this stream are identified
+ // by channel numbers
+ void SetRTPChannelNum(SInt16 inChannel) { fRTPChannel = inChannel; }
+ void SetRTCPChannelNum(SInt16 inChannel) { fRTCPChannel = inChannel; }
+ void PushPacket(char *packet, UInt32 packetLen, Bool16 isRTCP);
+
+ //
+ // ACCESSORS
+
+ OSRef* GetRef() { return &fRef; }
+ UInt32 GetBitRate() { return fCurrentBitRate; }
+ SourceInfo::StreamInfo* GetStreamInfo() { return &fStreamInfo; }
+ OSMutex* GetMutex() { return &fBucketMutex; }
+ void* GetStreamCookie() { return this; }
+ SInt16 GetRTPChannel() { return fRTPChannel; }
+ SInt16 GetRTCPChannel() { return fRTCPChannel; }
+ UDPSocketPair* GetSocketPair() { return fSockets;}
+ ReflectorSender* GetRTPSender() { return &fRTPSender; }
+ ReflectorSender* GetRTCPSender() { return &fRTCPSender; }
+
+ void SetHasFirstRTCP(Bool16 hasPacket) { fHasFirstRTCPPacket = hasPacket; }
+ Bool16 HasFirstRTCP() { return fHasFirstRTCPPacket; }
+
+ void SetFirst_RTCP_RTP_Time(UInt32 time) { fFirst_RTCP_RTP_Time = time; }
+ UInt32 GetFirst_RTCP_RTP_Time() { return fFirst_RTCP_RTP_Time; }
+
+ void SetFirst_RTCP_Arrival_Time(SInt64 time) { fFirst_RTCP_Arrival_Time = time; }
+ SInt64 GetFirst_RTCP_Arrival_Time() { return fFirst_RTCP_Arrival_Time; }
+
+
+ void SetHasFirstRTP(Bool16 hasPacket) { fHasFirstRTPPacket = hasPacket; }
+ Bool16 HasFirstRTP() { return fHasFirstRTPPacket; }
+
+ UInt32 GetBufferDelay() { return ReflectorStream::sOverBufferInMsec; }
+ UInt32 GetTimeScale() { return fStreamInfo.fTimeScale; }
+ UInt64 fPacketCount;
+
+ void SetEnableBuffer(Bool16 enableBuffer) { fEnableBuffer = enableBuffer; }
+ Bool16 BufferEnabled() { return fEnableBuffer; }
+inline void UpdateBitRate(SInt64 currentTime);
+ static UInt32 sOverBufferInMsec;
+
+ void IncEyeCount() { OSMutexLocker locker(&fBucketMutex); fEyeCount ++; }
+ void DecEyeCount() { OSMutexLocker locker(&fBucketMutex); fEyeCount --; }
+ UInt32 GetEyeCount() { OSMutexLocker locker(&fBucketMutex); return fEyeCount; }
+
+ private:
+
+ //Sends an RTCP receiver report to the broadcast source
+ void SendReceiverReport();
+ void AllocateBucketArray(UInt32 inNumBuckets);
+ SInt32 FindBucket();
+ // Unique ID & OSRef. ReflectorStreams can be mapped & shared
+ OSRef fRef;
+ char fSourceIDBuf[kStreamIDSize];
+
+ // Reflector sockets, retrieved from the socket pool
+ UDPSocketPair* fSockets;
+
+ ReflectorSender fRTPSender;
+ ReflectorSender fRTCPSender;
+ SequenceNumberMap fSequenceNumberMap; //for removing duplicate packets
+
+ // All the necessary info about this stream
+ SourceInfo::StreamInfo fStreamInfo;
+
+ enum
+ {
+ kReceiverReportSize = 16, //UInt32
+ kAppSize = 36, //UInt32
+ kMinNumBuckets = 16, //UInt32
+ kBitRateAvgIntervalInMilSecs = 30000 // time between bitrate averages
+ };
+
+ // BUCKET ARRAY
+
+ //ReflectorOutputs are kept in a 2-dimensional array, "Buckets"
+ typedef ReflectorOutput** Bucket;
+ Bucket* fOutputArray;
+
+ UInt32 fNumBuckets; //Number of buckets currently
+ UInt32 fNumElements; //Number of reflector outputs in the array
+
+ //Bucket array can't be modified while we are sending packets.
+ OSMutex fBucketMutex;
+
+ // RTCP RR information
+
+ char fReceiverReportBuffer[kReceiverReportSize + kAppSize +
+ RTCPSRPacket::kMaxCNameLen];
+ UInt32* fEyeLocation;//place in the buffer to write the eye information
+ UInt32 fReceiverReportSize;
+
+ // This is the destination address & port for RTCP
+ // receiver reports.
+ UInt32 fDestRTCPAddr;
+ UInt16 fDestRTCPPort;
+
+ // Used for calculating average bit rate
+ UInt32 fCurrentBitRate;
+ SInt64 fLastBitRateSample;
+ unsigned int fBytesSentInThisInterval;// unsigned int because we need to atomic_add
+
+ // If incoming data is RTSP interleaved
+ SInt16 fRTPChannel; //These will be -1 if not set to anything
+ SInt16 fRTCPChannel;
+
+ Bool16 fHasFirstRTCPPacket;
+ Bool16 fHasFirstRTPPacket;
+
+ Bool16 fEnableBuffer;
+ UInt32 fEyeCount;
+
+ UInt32 fFirst_RTCP_RTP_Time;
+ SInt64 fFirst_RTCP_Arrival_Time;
+
+ static UInt32 sBucketSize;
+ static UInt32 sMaxPacketAgeMSec;
+ static UInt32 sMaxFuturePacketSec;
+
+ static UInt32 sMaxFuturePacketMSec;
+ static UInt32 sOverBufferInSec;
+ static UInt32 sBucketDelayInMsec;
+ static Bool16 sUsePacketReceiveTime;
+ static UInt32 sFirstPacketOffsetMsec;
+
+ friend class ReflectorSocket;
+ friend class ReflectorSender;
+};
+
+
+void ReflectorStream::UpdateBitRate(SInt64 currentTime)
+{
+ if ((fLastBitRateSample + ReflectorStream::kBitRateAvgIntervalInMilSecs) < currentTime)
+ {
+ unsigned int intervalBytes = fBytesSentInThisInterval;
+ (void)atomic_sub(&fBytesSentInThisInterval, intervalBytes);
+
+ // Multiply by 1000 to convert from milliseconds to seconds, and by 8 to convert from bytes to bits
+ Float32 bps = (Float32)(intervalBytes * 8) / (Float32)(currentTime - fLastBitRateSample);
+ bps *= 1000;
+ fCurrentBitRate = (UInt32)bps;
+
+ // Don't check again for awhile!
+ fLastBitRateSample = currentTime;
+ }
+}
+#endif //_REFLECTOR_SESSION_H_
+
diff --git a/APIModules/QTSSReflectorModule/RelayOutput.cpp b/APIModules/QTSSReflectorModule/RelayOutput.cpp
new file mode 100644
index 0000000..fc45878
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/RelayOutput.cpp
@@ -0,0 +1,580 @@
+/*
+ *
+ * @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: RelayOutput.cpp
+
+ Contains: Implementation of object described in .h file
+
+
+
+*/
+
+#include "RelayOutput.h"
+
+#include "OSMemory.h"
+#include "SocketUtils.h"
+#include "RTSPSourceInfo.h"
+
+static StrPtrLen sUDPDestStr("udp_destination");
+static StrPtrLen sAnnouncedDestStr("announced_destination");
+
+// STATIC DATA
+OSQueue RelayOutput::sRelayOutputQueue;
+OSMutex RelayOutput::sQueueMutex;
+
+QTSS_ObjectType RelayOutput::qtssRelayOutputObjectType;
+
+QTSS_AttributeID RelayOutput::sOutputType = qtssIllegalAttrID;
+QTSS_AttributeID RelayOutput::sOutputDestAddr = qtssIllegalAttrID;
+QTSS_AttributeID RelayOutput::sOutputLocalAddr = qtssIllegalAttrID;
+QTSS_AttributeID RelayOutput::sOutputUDPPorts = qtssIllegalAttrID;
+QTSS_AttributeID RelayOutput::sOutputRTSPPort = qtssIllegalAttrID;
+QTSS_AttributeID RelayOutput::sOutputURL = qtssIllegalAttrID;
+QTSS_AttributeID RelayOutput::sOutputTTL = qtssIllegalAttrID;
+QTSS_AttributeID RelayOutput::sOutputCurPacketsPerSec = qtssIllegalAttrID;
+QTSS_AttributeID RelayOutput::sOutputCurBitsPerSec = qtssIllegalAttrID;
+QTSS_AttributeID RelayOutput::sOutputTotalPacketsSent = qtssIllegalAttrID;
+QTSS_AttributeID RelayOutput::sOutputTotalBytesSent = qtssIllegalAttrID;
+
+static char* sOutputTypeName = "output_type";
+static char* sOutputDestAddrName = "output_dest_addr";
+static char* sOutputLocalAddrName = "output_local_addr";
+static char* sOutputUDPPortsName = "output_udp_ports";
+static char* sOutputRTSPPortName = "output_rtsp_port";
+static char* sOutputURLName = "output_url";
+static char* sOutputTTLName = "output_ttl";
+
+static char* sOutputCurPacketsPerSecName = "output_cur_packetspersec";
+static char* sOutputCurBitsPerSecName = "output_cur_bitspersec";
+static char* sOutputTotalPacketsSentName = "output_total_packets_sent";
+static char* sOutputTotalBytesSentName = "output_total_bytes_sent";
+
+void RelayOutput::Register()
+{
+ // Create the relay output object type
+ (void)QTSS_CreateObjectType(&qtssRelayOutputObjectType);
+
+ // Add the static attributes to the qtssRelayOutputObjectType object
+ (void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputTypeName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputTypeName, &sOutputType); // dest type
+
+ (void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputDestAddrName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputDestAddrName, &sOutputDestAddr); // dest addr
+
+ (void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputLocalAddrName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputLocalAddrName, &sOutputLocalAddr); // interface addr
+
+ (void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputUDPPortsName, NULL, qtssAttrDataTypeUInt16);
+ (void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputUDPPortsName, &sOutputUDPPorts); // udp ports
+
+ (void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputRTSPPortName, NULL, qtssAttrDataTypeUInt16);
+ (void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputRTSPPortName, &sOutputRTSPPort); // rtsp port
+
+ (void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputURLName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputURLName, &sOutputURL); // url
+
+ (void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputTTLName, NULL, qtssAttrDataTypeUInt16);
+ (void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputTTLName, &sOutputTTL); // ttl
+
+ (void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputCurPacketsPerSecName, NULL, qtssAttrDataTypeUInt32);
+ (void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputCurPacketsPerSecName, &sOutputCurPacketsPerSec);// cur packets/sec
+
+ (void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputCurBitsPerSecName, NULL, qtssAttrDataTypeUInt32);
+ (void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputCurBitsPerSecName, &sOutputCurBitsPerSec); // cur bits/sec
+
+ (void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputTotalPacketsSentName, NULL, qtssAttrDataTypeUInt64);
+ (void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputTotalPacketsSentName, &sOutputTotalPacketsSent);// total packets
+
+ (void)QTSS_AddStaticAttribute(qtssRelayOutputObjectType, sOutputTotalBytesSentName, NULL, qtssAttrDataTypeUInt64);
+ (void)QTSS_IDForAttr(qtssRelayOutputObjectType, sOutputTotalBytesSentName, &sOutputTotalBytesSent); // total bytes
+
+}
+
+RelayOutput::RelayOutput(SourceInfo* inInfo, UInt32 inWhichOutput, RelaySession* inRelaySession, Bool16 isRTSPSourceInfo)
+: fRelaySession(inRelaySession),
+ fOutputSocket(NULL, Socket::kNonBlockingSocketType),
+ fNumStreams(inRelaySession->GetSourceInfo()->GetNumStreams()), // use the reflector session's source info
+ fOutputInfo(*inInfo->GetOutputInfo(inWhichOutput)),
+ fQueueElem(),
+ fFormatter(&fHTMLBuf[0], kMaxHTMLSize),
+ fPacketsPerSecond(0),
+ fBitsPerSecond(0),
+ fLastUpdateTime(0),
+ fTotalPacketsSent(0),
+ fTotalBytesSent(0),
+ fLastPackets(0),
+ fLastBytes(0),
+ fClientSocket(NULL),
+ fClient(NULL),
+ fDoingAnnounce(false),
+ fValid(true),
+ fOutgoingSDP(NULL),
+ fAnnounceTask(NULL),
+ fRTSPOutputInfo(NULL)
+{
+ Assert(fNumStreams > 0);
+
+ fQueueElem.SetEnclosingObject(this);
+ fStreamCookieArray = NEW void*[fNumStreams];
+ fTrackIDArray = NEW UInt32[fNumStreams];
+ fOutputInfo.fPortArray = NEW UInt16[fNumStreams];//copy constructor doesn't do this
+ ::memset(fOutputInfo.fPortArray, 0, fNumStreams * sizeof(UInt16));
+
+ // create a bookmark for each stream we'll reflect
+ this->InititializeBookmarks( inRelaySession->GetNumStreams() );
+
+ // Copy out all the track IDs for each stream
+ for (UInt32 x = 0; x < fNumStreams; x++)
+ {
+ fTrackIDArray[x] = inRelaySession->GetSourceInfo()->GetStreamInfo(x)->fTrackID;
+ fStreamCookieArray[x] = inRelaySession->GetStreamCookie(fTrackIDArray[x]);
+ }
+
+ // Copy the contents of the output port array
+
+ if (inInfo->GetOutputInfo(inWhichOutput)->fPortArray != NULL)
+ {
+ UInt32 copySize = fNumStreams;
+ if (fOutputInfo.fNumPorts < fNumStreams)
+ copySize = fOutputInfo.fNumPorts;
+ ::memcpy(fOutputInfo.fPortArray, inInfo->GetOutputInfo(inWhichOutput)->fPortArray, copySize * sizeof(UInt16));
+ }
+ else if (fOutputInfo.fBasePort != 0)
+ {
+ for (UInt32 y = 0; y < fNumStreams; y++)
+ fOutputInfo.fPortArray[y] = (UInt16) (fOutputInfo.fBasePort + (y * 2) );
+ }
+
+ OS_Error err = BindSocket();
+ if (err != OS_NoErr)
+ {
+ fValid = false;
+ return;
+ }
+
+ RTSPOutputInfo* rtspInfo = NULL;
+ if (isRTSPSourceInfo)
+ {
+ // in the case of the announce source info, the passed in source info will have the new
+ // output information, but only the session's source info will have the sdp and url.
+ RTSPSourceInfo* rtspSourceInfo = (RTSPSourceInfo *)(inInfo);
+ Assert(rtspSourceInfo != NULL);
+ RTSPSourceInfo* sessionSourceInfo = (RTSPSourceInfo *)(inRelaySession->GetSourceInfo());
+ Assert(sessionSourceInfo != NULL);
+
+ rtspInfo = rtspSourceInfo->GetRTSPOutputInfo(inWhichOutput);
+ if (rtspInfo->fIsAnnounced)
+ {
+ fRTSPOutputInfo = NEW RTSPOutputInfo();
+ fRTSPOutputInfo->Copy(*rtspInfo);
+
+ fDoingAnnounce = true;
+ // set up rtsp socket and client
+ fClientSocket = new TCPClientSocket(Socket::kNonBlockingSocketType);
+ fClient = new RTSPClient(fClientSocket, false, RelaySession::sRelayUserAgent);
+
+ // set up the outgoing socket
+ fClientSocket->Set(fOutputInfo.fDestAddr, rtspInfo->fAnnouncePort);
+ int sndBufSize = 32 * 1024;
+ int rcvBufSize=1024;
+ fClientSocket->SetOptions(sndBufSize,rcvBufSize);
+
+ // set up the client object
+ StrPtrLen url;
+ if ((rtspInfo->fDestURl != NULL) && (strlen(rtspInfo->fDestURl) > 0))
+ url.Set(rtspInfo->fDestURl);
+ else
+ url.Set(sessionSourceInfo->GetSourceURL());
+
+ fClient->Set(url);
+
+ fClient->SetTransportMode(RTSPClient::kPushMode);
+ fClient->SetName(rtspInfo->fUserName);
+ fClient->SetPassword(rtspInfo->fPassword);
+
+ UInt32 len;
+ fOutgoingSDP = sessionSourceInfo->GetAnnounceSDP(fOutputInfo.fDestAddr, &len);
+ fAnnounceState = kSendingAnnounce;
+ fCurrentSetup = 0;
+ fAnnounceTask = new RelayAnnouncer(this); // this will now go and run the async announce
+ fAnnounceTask->Signal(Task::kStartEvent);
+ }
+ }
+
+ // Write the Output HTML
+ // Looks like: Relaying to: 229.49.52.102, Ports: 16898 16900 Time to live: 15
+ static StrPtrLen sHTMLStart("Relaying to: ");
+ static StrPtrLen sPorts(", Ports: ");
+ static StrPtrLen sTimeToLive(" Time to live: ");
+ static StrPtrLen sHTMLEnd("
");
+
+ // First, format the destination addr as a dotted decimal string
+ char theIPAddrBuf[20];
+ StrPtrLen theIPAddr(theIPAddrBuf, 20);
+ struct in_addr theAddr;
+ theAddr.s_addr = htonl(fOutputInfo.fDestAddr);
+ SocketUtils::ConvertAddrToString(theAddr, &theIPAddr);
+
+ // Begin writing the HTML
+ fFormatter.Put(sHTMLStart);
+ fFormatter.Put(theIPAddr);
+ fFormatter.Put(sPorts);
+
+ for (UInt32 y = 0; y < fNumStreams; y++)
+ {
+ // Write all the destination ports
+ fFormatter.Put(fOutputInfo.fPortArray[y]);
+ fFormatter.PutSpace();
+ }
+
+ if (SocketUtils::IsMulticastIPAddr(inInfo->GetOutputInfo(inWhichOutput)->fDestAddr))
+ {
+ // Put the time to live if this is a multicast destination
+ fFormatter.Put(sTimeToLive);
+ fFormatter.Put(fOutputInfo.fTimeToLive);
+ }
+ fFormatter.Put(sHTMLEnd);
+
+ // Setup the StrPtrLen to point to the right stuff
+ fOutputInfoHTML.Ptr = fFormatter.GetBufPtr();
+ fOutputInfoHTML.Len = fFormatter.GetCurrentOffset();
+
+ OSMutexLocker locker(&sQueueMutex);
+ sRelayOutputQueue.EnQueue(&fQueueElem);
+
+ SetupRelayOutputObject(rtspInfo);
+}
+
+RelayOutput::~RelayOutput()
+{
+ OSMutexLocker locker(&sQueueMutex);
+ sRelayOutputQueue.Remove(&fQueueElem);
+
+ if (fClientSocket)
+ delete fClientSocket;
+ if (fClient)
+ delete fClient;
+
+ delete [] fStreamCookieArray;
+
+ delete fOutgoingSDP;
+ fOutgoingSDP = NULL;
+
+ if (fAnnounceTask != NULL)
+ fAnnounceTask->fOutput = NULL;
+
+ if (fRTSPOutputInfo != NULL)
+ delete fRTSPOutputInfo;
+
+ QTSS_Object outputObject;
+ UInt32 len = sizeof(QTSS_Object);
+
+ for (int x = 0; QTSS_GetValue(fRelaySessionObject, RelaySession::sRelayOutputObject, x, &outputObject, &len) == QTSS_NoErr; x++)
+ {
+ Assert(outputObject != NULL);
+ Assert(len == sizeof(QTSS_Object));
+
+ if (outputObject == fRelayOutputObject)
+ {
+ (void)QTSS_RemoveValue(fRelaySessionObject, RelaySession::sRelayOutputObject, x);
+ break;
+ }
+ }
+}
+
+
+OS_Error RelayOutput::BindSocket()
+{
+ OS_Error theErr = fOutputSocket.Open();
+ if (theErr != OS_NoErr)
+ return theErr;
+
+ // We don't care what local port we bind to
+ theErr = fOutputSocket.Bind(fOutputInfo.fLocalAddr, 0);
+ if (theErr != OS_NoErr)
+ return theErr;
+
+ // Set the ttl to be the proper value
+ return fOutputSocket.SetTtl(fOutputInfo.fTimeToLive);
+}
+
+Bool16 RelayOutput::Equal(SourceInfo* inInfo)
+{
+ // First check if the Source Info matches this RelaySession
+ if (!fRelaySession->Equal(inInfo))
+ return false;
+ for (UInt32 x = 0; x < inInfo->GetNumOutputs(); x++)
+ {
+ if (inInfo->GetOutputInfo(x)->Equal(fOutputInfo))
+ {
+ RTSPOutputInfo* rtspOutputInfo = NULL;
+ if (inInfo->IsRTSPSourceInfo())
+ {
+ rtspOutputInfo = ((RTSPSourceInfo*)inInfo)->GetRTSPOutputInfo(x);
+ if (!rtspOutputInfo->fIsAnnounced)
+ rtspOutputInfo = NULL;
+ }
+
+ if (fRTSPOutputInfo != NULL) // announced output
+ {
+ if (!fRTSPOutputInfo->Equal(rtspOutputInfo)) // doesn't match the output
+ continue;
+ }
+ else if (rtspOutputInfo != NULL)
+ continue;
+
+ // This is a rather special purpose function... here we set this
+ // flag marking this particular output as a duplicate, because
+ // we know it is equal to this object.
+ // (This is used in QTSSReflectorModule.cpp:RereadRelayPrefs)
+ inInfo->GetOutputInfo(x)->fAlreadySetup = true;
+ return true;
+ }
+ }
+ return false;
+}
+
+QTSS_Error RelayOutput::WritePacket(StrPtrLen* inPacket, void* inStreamCookie, UInt32 inFlags, SInt64 /*packetLatenessInMSec*/, SInt64* /*timeToSendThisPacketAgain*/, UInt64* packetIDPtr, SInt64* /*arrivalTimeMSec*/, Bool16 /*firstPacket */ )
+{
+
+ if (!fValid || fDoingAnnounce)
+ return OS_NoErr; // Not done setting up or we had an error setting up
+
+ // we don't use packetLateness becuase relays don't need to worry about TCP flow control induced transmit delay
+
+ // Look for the matching streamID
+ for (UInt32 x = 0; x < fNumStreams; x++)
+ {
+ if (inStreamCookie == fStreamCookieArray[x])
+ {
+ UInt16 theDestPort = fOutputInfo.fPortArray[x];
+ Assert((theDestPort & 1) == 0); //this should always be an RTP port (even)
+ if (inFlags & qtssWriteFlagsIsRTCP)
+ theDestPort++;
+
+ (void)fOutputSocket.SendTo(fOutputInfo.fDestAddr, theDestPort,
+ inPacket->Ptr, inPacket->Len);
+
+ // Update our totals
+ fTotalPacketsSent++;
+ fTotalBytesSent += inPacket->Len;
+ break;
+ }
+ }
+
+ // If it is time to recalculate statistics, do so
+ SInt64 curTime = OS::Milliseconds();
+ if ((fLastUpdateTime + kStatsIntervalInMilSecs) < curTime)
+ {
+ // Update packets per second
+ Float64 packetsPerSec = (Float64)((SInt64)fTotalPacketsSent - (SInt64)fLastPackets);
+ packetsPerSec *= 1000;
+ packetsPerSec /= (Float64)(curTime - fLastUpdateTime);
+ fPacketsPerSecond = (UInt32)packetsPerSec;
+
+ // Update bits per second. Win32 doesn't implement UInt64 -> Float64.
+ Float64 bitsPerSec = (Float64)((SInt64)fTotalBytesSent - (SInt64)fLastBytes);
+ bitsPerSec *= 1000 * 8;//convert from seconds to milsecs, bytes to bits
+ bitsPerSec /= (Float64)(curTime - fLastUpdateTime);
+ fBitsPerSecond = (UInt32)bitsPerSec;
+
+ fLastUpdateTime = curTime;
+ fLastPackets = fTotalPacketsSent;
+ fLastBytes = fTotalBytesSent;
+ }
+
+ return QTSS_NoErr;
+}
+
+SInt64 RelayOutput::RelayAnnouncer::Run()
+{
+ OSMutexLocker locker(RelayOutput::GetQueueMutex());
+ SInt64 result = -1;
+ if (fOutput != NULL)
+ result = fOutput->RunAnnounce();
+
+ return result;
+}
+
+SInt64 RelayOutput::RunAnnounce()
+{
+ OS_Error err = OS_NoErr;
+ SInt64 result = 1000;
+
+ if (fAnnounceState == kSendingAnnounce)
+ {
+ if (fOutgoingSDP == NULL || ::strlen(fOutgoingSDP) == 0)
+ err = ENOTCONN;
+ else
+ {
+ err = fClient->SendAnnounce(fOutgoingSDP);
+ if (err == OS_NoErr)
+ {
+ delete fOutgoingSDP;
+ fOutgoingSDP = NULL;
+ if (fClient->GetStatus() == 200)
+ fAnnounceState = kSendingSetup;
+ else
+ err = ENOTCONN;
+ }
+ }
+ }
+
+ while ((fAnnounceState == kSendingSetup) && (err == OS_NoErr))
+ {
+ err = fClient->SendUDPSetup(fTrackIDArray[fCurrentSetup], 10000);
+ if (err == OS_NoErr)
+ {
+ if (fClient->GetStatus() == 200)
+ {
+ fOutputInfo.fPortArray[fCurrentSetup] = fClient->GetServerPort(); // this got set from the Setup reply
+ fCurrentSetup++;
+ if (fCurrentSetup == fNumStreams)
+ fAnnounceState = kSendingPlay;
+ }
+ else
+ err = ENOTCONN;
+ }
+ }
+
+ if (fAnnounceState == kSendingPlay)
+ {
+ err = fClient->SendPlay(0);
+ if (err == OS_NoErr)
+ {
+ if (fClient->GetStatus() == 200)
+ fAnnounceState = kDone;
+ else
+ err = ENOTCONN;
+ }
+ }
+
+ if (fAnnounceState == kDone)
+ {
+ for (UInt32 index = 0; index < fNumStreams; index++) // source udp ports
+ {
+ UInt16 udpPort = fOutputInfo.fPortArray[index];
+ err = QTSS_SetValue (fRelayOutputObject, sOutputUDPPorts, index, &udpPort, sizeof(udpPort));
+ Assert(err == QTSS_NoErr);
+ }
+
+ fDoingAnnounce = false;
+ result = -1; // let the task die
+ fAnnounceTask = NULL;
+ }
+
+ if ((err == EINPROGRESS) || (err == EAGAIN))
+ {
+ // Request an async event
+ fClientSocket->GetSocket()->SetTask(fAnnounceTask);
+ fClientSocket->GetSocket()->RequestEvent(fClientSocket->GetEventMask() );
+ }
+ else if (err != OS_NoErr)
+ {
+ // We encountered some fatal error with the socket. Record this as a connection failure
+ fValid = false;
+ result = -1; // let the task die
+ fAnnounceTask = NULL;
+ }
+
+ if ( (-1 == result) && (NULL != fClientSocket) && (NULL != fClientSocket->GetSocket() ) )
+ fClientSocket->GetSocket()->SetTask(NULL); //remove fAnnounceTask from event handling code. The task can be safely deleted after detaching from the socket.
+
+ return result;
+}
+
+void RelayOutput::SetupRelayOutputObject(RTSPOutputInfo* inRTSPInfo)
+{
+ fRelaySessionObject = fRelaySession->GetRelaySessionObject();
+ QTSS_Error theErr = QTSS_NoErr;
+ UInt32 outIndex = 0;
+
+ theErr = QTSS_LockObject (fRelaySessionObject);
+ Assert(theErr == QTSS_NoErr);
+
+ theErr = QTSS_CreateObjectValue (fRelaySessionObject , RelaySession::sRelayOutputObject, qtssRelayOutputObjectType, &outIndex, &fRelayOutputObject);
+ Assert(theErr == QTSS_NoErr);
+
+ if ((inRTSPInfo == NULL) || !inRTSPInfo->fIsAnnounced) // output type
+ {
+ theErr = QTSS_SetValue (fRelayOutputObject, sOutputType, 0, (void*)sUDPDestStr.Ptr, sUDPDestStr.Len);
+ Assert(theErr == QTSS_NoErr);
+ }
+ else
+ {
+ theErr = QTSS_SetValue (fRelayOutputObject, sOutputType, 0, (void*)sAnnouncedDestStr.Ptr, sAnnouncedDestStr.Len); // output type
+ Assert(theErr == QTSS_NoErr);
+
+ theErr = QTSS_SetValue (fRelayOutputObject, sOutputRTSPPort, 0, &inRTSPInfo->fAnnouncePort, sizeof(inRTSPInfo->fAnnouncePort)); // rtsp port
+ Assert(theErr == QTSS_NoErr);
+
+ theErr = QTSS_SetValue (fRelayOutputObject, sOutputURL, 0, (fClient->GetURL())->Ptr, (fClient->GetURL())->Len); // output url
+ Assert(theErr == QTSS_NoErr);
+ }
+
+ char theIPAddrBuf[20];
+ StrPtrLen theIPAddr(theIPAddrBuf, 20);
+
+ struct in_addr theDestAddr; // output destination address
+ theDestAddr.s_addr = htonl(fOutputInfo.fDestAddr);
+ SocketUtils::ConvertAddrToString(theDestAddr, &theIPAddr);
+
+ theErr = QTSS_SetValue (fRelayOutputObject, sOutputDestAddr, 0, (void*)theIPAddr.Ptr, theIPAddr.Len);
+ Assert(theErr == QTSS_NoErr);
+
+ struct in_addr theLocalAddr; // output local address
+ theLocalAddr.s_addr = htonl(fOutputInfo.fLocalAddr);
+ SocketUtils::ConvertAddrToString(theLocalAddr, &theIPAddr);
+
+ theErr = QTSS_SetValue (fRelayOutputObject, sOutputLocalAddr, 0, (void*)theIPAddr.Ptr, theIPAddr.Len);
+ Assert(theErr == QTSS_NoErr);
+
+
+ for (UInt32 index = 0; index < fNumStreams; index++) // output udp ports
+ {
+ UInt16 udpPort = fOutputInfo.fPortArray[index];
+ theErr = QTSS_SetValue (fRelayOutputObject, sOutputUDPPorts, index, &udpPort, sizeof(udpPort));
+ Assert(theErr == QTSS_NoErr);
+ }
+
+ theErr = QTSS_SetValue (fRelayOutputObject, sOutputTTL, 0, &(fOutputInfo.fTimeToLive), sizeof(fOutputInfo.fTimeToLive));
+ Assert(theErr == QTSS_NoErr);
+
+ theErr = QTSS_SetValuePtr (fRelayOutputObject, sOutputCurPacketsPerSec, &fPacketsPerSecond, sizeof(fPacketsPerSecond));
+ Assert(theErr == QTSS_NoErr);
+
+ theErr = QTSS_SetValuePtr (fRelayOutputObject, sOutputCurBitsPerSec, &fBitsPerSecond, sizeof(fBitsPerSecond));
+ Assert(theErr == QTSS_NoErr);
+
+ theErr = QTSS_SetValuePtr (fRelayOutputObject, sOutputTotalPacketsSent, &fTotalPacketsSent, sizeof(fTotalPacketsSent));
+ Assert(theErr == QTSS_NoErr);
+
+ theErr = QTSS_SetValuePtr (fRelayOutputObject, sOutputTotalBytesSent, &fTotalBytesSent, sizeof(fTotalBytesSent));
+ Assert(theErr == QTSS_NoErr);
+
+ theErr = QTSS_UnlockObject (fRelaySessionObject);
+ Assert(theErr == QTSS_NoErr);
+
+}
diff --git a/APIModules/QTSSReflectorModule/RelayOutput.h b/APIModules/QTSSReflectorModule/RelayOutput.h
new file mode 100644
index 0000000..f7c5046
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/RelayOutput.h
@@ -0,0 +1,182 @@
+/*
+ *
+ * @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: RelayOutput.h
+
+ Contains: An implementation of the ReflectorOutput abstract base class,
+ that just writes data out to UDP sockets.
+
+*/
+
+#ifndef __RELAY_OUTPUT_H__
+#define __RELAY_OUTPUT_H__
+
+#include "ReflectorOutput.h"
+#include "RelaySession.h"
+#include "SourceInfo.h"
+#include "RTSPClient.h"
+#include "ClientSocket.h"
+
+#include "OSQueue.h"
+#include "OSMutex.h"
+
+class RelayAnnouncer;
+
+class RelayOutput : public ReflectorOutput
+{
+ public:
+
+ // Call Register in the Relay Module's Register Role
+ static void Register();
+
+ RelayOutput(SourceInfo* inInfo, UInt32 inWhichOutput, RelaySession* inSession, Bool16 isRTSPSourceInfo);
+ virtual ~RelayOutput();
+
+ // Returns true if this output matches one of the Outputs in the SourceInfo.
+ // Also marks the proper SourceInfo::OutputInfo "fAlreadySetup" flag as true
+ Bool16 Equal(SourceInfo* inInfo);
+
+ // Call this to setup this object's output socket
+ OS_Error BindSocket();
+
+ // Writes the packet directly to a UDP socket
+ virtual QTSS_Error WritePacket(StrPtrLen* inPacket, void* inStreamCookie, UInt32 inFlags, SInt64 packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTime, Bool16 firstPacket);
+
+ virtual Bool16 IsUDP() { return true; }
+
+ virtual Bool16 IsPlaying() {if (!fValid || fDoingAnnounce) return false; return true; }
+
+ // ACCESSORS
+
+ RelaySession* GetRelaySession() { return fRelaySession; }
+ StrPtrLen* GetOutputInfoHTML() { return &fOutputInfoHTML; }
+
+ UInt32 GetCurPacketsPerSecond() { return fPacketsPerSecond; }
+ UInt32 GetCurBitsPerSecond() { return fBitsPerSecond; }
+ UInt64& GetTotalPacketsSent() { return fTotalPacketsSent; }
+ UInt64& GetTotalBytesSent() { return fTotalBytesSent; }
+ Bool16 IsValid() { return fValid; }
+
+ // Use these functions to iterate over all RelayOutputs
+ static OSMutex* GetQueueMutex() { return &sQueueMutex; }
+ static OSQueue* GetOutputQueue(){ return &sRelayOutputQueue; }
+ void TearDown() {};
+
+ SInt64 RunAnnounce();
+
+ private:
+
+ void SetupRelayOutputObject(RTSPOutputInfo* inRTSPInfo);
+
+ class RelayAnnouncer : public Task
+ {
+ public:
+ RelayAnnouncer(RelayOutput* output) : fOutput(output) {this->SetTaskName("RelayAnnouncer");}
+
+ virtual SInt64 Run();
+ RelayOutput* fOutput;
+ };
+
+ enum
+ {
+ kMaxHTMLSize = 255, // Note, this may be too short and we don't protect!
+ kStatsIntervalInMilSecs = 10000 // Update "current" statistics every 10 seconds
+ };
+
+ RelaySession* fRelaySession;
+
+ // Relay streams all share this one socket for writing.
+ UDPSocket fOutputSocket;
+ UInt32 fNumStreams;
+ SourceInfo::OutputInfo fOutputInfo;
+ void** fStreamCookieArray;//Each stream has a cookie
+ UInt32* fTrackIDArray;
+
+ OSQueueElem fQueueElem;
+
+ char fHTMLBuf[kMaxHTMLSize];
+ StrPtrLen fOutputInfoHTML;
+ ResizeableStringFormatter fFormatter;
+
+ // Statistics
+ UInt32 fPacketsPerSecond;
+ UInt32 fBitsPerSecond;
+
+ SInt64 fLastUpdateTime;
+ UInt64 fTotalPacketsSent;
+ UInt64 fTotalBytesSent;
+ UInt64 fLastPackets;
+ UInt64 fLastBytes;
+
+ TCPClientSocket* fClientSocket;
+ RTSPClient* fClient;
+ Bool16 fDoingAnnounce;
+ Bool16 fValid;
+ char* fOutgoingSDP;
+ RelayAnnouncer* fAnnounceTask;
+
+ RTSPOutputInfo* fRTSPOutputInfo;
+
+ enum // anounce states
+ {
+ kSendingAnnounce = 0,
+ kSendingSetup = 1,
+ kSendingPlay = 2,
+ kDone = 3
+ };
+ UInt32 fAnnounceState;
+ UInt32 fCurrentSetup;
+
+ // Queue of all current RelayReflectorOutput objects, for use in the
+ static OSQueue sRelayOutputQueue;
+ static OSMutex sQueueMutex;
+
+ QTSS_Object fRelaySessionObject;
+ QTSS_Object fRelayOutputObject;
+
+ // attributes of the qtssRelayOutputObjectType
+ static QTSS_ObjectType qtssRelayOutputObjectType;
+
+ static QTSS_AttributeID sOutputType;
+ static QTSS_AttributeID sOutputDestAddr;
+ static QTSS_AttributeID sOutputLocalAddr;
+ static QTSS_AttributeID sOutputUDPPorts;
+ static QTSS_AttributeID sOutputRTSPPort;
+ static QTSS_AttributeID sOutputURL;
+ static QTSS_AttributeID sOutputTTL;
+ static QTSS_AttributeID sOutputCurPacketsPerSec;
+ static QTSS_AttributeID sOutputCurBitsPerSec;
+ static QTSS_AttributeID sOutputTotalPacketsSent;
+ static QTSS_AttributeID sOutputTotalBytesSent;
+
+
+
+
+
+
+};
+
+
+#endif //__RELAY_OUTPUT_H__
diff --git a/APIModules/QTSSReflectorModule/RelaySDPSourceInfo.cpp b/APIModules/QTSSReflectorModule/RelaySDPSourceInfo.cpp
new file mode 100644
index 0000000..9ce32d3
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/RelaySDPSourceInfo.cpp
@@ -0,0 +1,202 @@
+/*
+ *
+ * @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: RelaySDPSourceInfo.cpp
+
+ Contains: Implementation of object defined in .h file
+
+
+
+*/
+
+#include "RelaySDPSourceInfo.h"
+#include "SDPSourceInfo.h"
+
+#include "MyAssert.h"
+#include "StringParser.h"
+#include "OSMemory.h"
+
+#ifndef __Win32__
+#include
+#endif
+
+RelaySDPSourceInfo::~RelaySDPSourceInfo()
+{
+ // Not necessary anymore as the destructor of the base class will take care
+ // of deleting all allocated memory for fOutputArray and fStreamArray
+ /*
+ if (fOutputArray != NULL)
+ {
+ for (UInt32 x = 0; x < fNumOutputs; x++)
+ {
+ delete [] fOutputArray[x].fPortArray;
+ fOutputArray[x].fNumPorts = 0;
+ }
+
+ char* theOutputArray = (char*)fOutputArray;
+ delete [] theOutputArray;
+ }
+ if (fStreamArray != NULL)
+ {
+ char* theStreamArray = (char*)fStreamArray;
+ delete [] theStreamArray;
+ }
+ */
+}
+
+
+void RelaySDPSourceInfo::Parse(StrPtrLen* inSDPData)
+{
+ // These are the lines of the SDP file that we are interested in
+ static StrPtrLen sRelayAddr("a=x-qt-relay-addr");
+ static StrPtrLen sRelayPort("a=x-qt-relay-port");
+
+ Assert(fOutputArray == NULL);
+ Assert(fStreamArray == NULL);
+
+ StrPtrLen sdpLine;
+ StrPtrLen outputAddrs;
+
+ StringParser trackCounter(inSDPData);
+
+ UInt32 theDestIPAddr = 0;
+ UInt16 theDestTtl = 0;
+
+ //
+ // FIRST WALK THROUGH SDP
+ // The first walk is to count up the number of StreamInfo & OutputInfo
+ // objects that we will need.
+ while (true)
+ {
+ // grab a line
+ trackCounter.ConsumeUntil(&sdpLine, StringParser::sEOLMask);
+
+ if (sdpLine.NumEqualIgnoreCase(sRelayAddr.Ptr, sRelayAddr.Len))
+ {
+ // there is a x-qt-relay-addr line, look for all IP addrs
+ StringParser relayAddrParser(&sdpLine);
+ relayAddrParser.ConsumeUntil(NULL, StringParser::sDigitMask);
+
+ // The first IP addr on this line is the destination IP addr of the
+ // source broadcast.
+ theDestIPAddr = SDPSourceInfo::GetIPAddr(&relayAddrParser, ' ');
+ relayAddrParser.ConsumeWhitespace();
+
+ // Store this position so we can later go back to it
+ outputAddrs.Ptr = relayAddrParser.GetCurrentPosition();
+ outputAddrs.Len = relayAddrParser.GetDataRemaining();
+
+ StrPtrLen theTtl;
+ while (relayAddrParser.GetDataRemaining() > 0)
+ {
+ relayAddrParser.ConsumeUntil(&theTtl, ' ');
+ relayAddrParser.ConsumeWhitespace();
+ fNumOutputs++;
+ }
+ fNumOutputs--;// Don't count the ttl as an output!
+
+ StringParser ttlParser(&theTtl);
+ theDestTtl = (UInt16) ttlParser.ConsumeInteger(NULL);
+ }
+ // Each x=qt-relay-port line corresponds to one source stream.
+ else if (sdpLine.NumEqualIgnoreCase(sRelayPort.Ptr, sRelayPort.Len))
+ fNumStreams++;
+
+ //stop when we reach an empty line.
+ if (!trackCounter.ExpectEOL())
+ break;
+ }
+
+ // No relay info in this file!
+ if ((fNumStreams == 0) || (fNumOutputs == 0))
+ return;
+
+ // x-qt-relay-port lines should always be in pairs (RTP & RTCP)
+ if ((fNumStreams & 1) != 0)
+ return;
+ fNumStreams /= 2;
+
+ //
+ // CONSTRUCT fStreamInfo AND fOutputInfo ARRAYS
+ fStreamArray = NEW StreamInfo[fNumStreams];
+ fOutputArray = NEW OutputInfo[fNumOutputs];
+
+ //
+ // FILL IN ARRAYS
+
+ // Filling in the output addresses is easy because the outputAddrs
+ // StrPtrLen points right at the data we want
+ StringParser theOutputAddrParser(&outputAddrs);
+ for (UInt32 x = 0; x < fNumOutputs; x++)
+ {
+ fOutputArray[x].fDestAddr = SDPSourceInfo::GetIPAddr(&theOutputAddrParser, ' ');
+ fOutputArray[x].fLocalAddr = INADDR_ANY;
+ fOutputArray[x].fTimeToLive = theDestTtl;
+ fOutputArray[x].fPortArray = NEW UInt16[fNumStreams];//Each output has one port per stream
+ fOutputArray[x].fNumPorts = fNumStreams;
+ ::memset(fOutputArray[x].fPortArray, 0, fNumStreams * sizeof(UInt16));
+ fOutputArray[x].fAlreadySetup = false;
+ theOutputAddrParser.ConsumeWhitespace();
+ Assert(fOutputArray[x].fDestAddr > 0);
+ }
+
+ StringParser sdpParser(inSDPData);
+
+ // Now go through and find all the port information on all the x-qt-relay-port lines
+ for (UInt32 theStreamIndex = 0; theStreamIndex < fNumStreams; )
+ {
+ sdpParser.ConsumeUntil(&sdpLine, StringParser::sEOLMask);
+
+ // parse through all the x-qt-relay-port lines
+ if (sdpLine.NumEqualIgnoreCase(sRelayPort.Ptr, sRelayPort.Len))
+ {
+ // Begin parsing... find the first port on the line
+ StringParser relayAddrParser(&sdpLine);
+ relayAddrParser.ConsumeUntil(NULL, StringParser::sDigitMask);
+
+ // The first port is the source port for this stream
+ fStreamArray[theStreamIndex].fPort = (UInt16) relayAddrParser.ConsumeInteger(NULL);
+ if (fStreamArray[theStreamIndex].fPort & 1)
+ continue; //we only care about RTP ports
+
+ // Fill in all the fields we can for this stream
+ fStreamArray[theStreamIndex].fDestIPAddr = theDestIPAddr;
+ fStreamArray[theStreamIndex].fTimeToLive = theDestTtl;
+ fStreamArray[theStreamIndex].fTrackID = theStreamIndex + 1;
+
+ // Now fill in all the output ports for this stream
+ for (UInt32 x = 0; x < fNumOutputs; x++)
+ {
+ relayAddrParser.ConsumeWhitespace();
+ fOutputArray[x].fPortArray[theStreamIndex] = (UInt16) relayAddrParser.ConsumeInteger(NULL);
+ }
+
+ theStreamIndex++;
+ }
+ //stop when we reach an empty line.
+ if (!sdpParser.ExpectEOL())
+ break;
+ }
+}
diff --git a/APIModules/QTSSReflectorModule/RelaySDPSourceInfo.h b/APIModules/QTSSReflectorModule/RelaySDPSourceInfo.h
new file mode 100644
index 0000000..12d9481
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/RelaySDPSourceInfo.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * @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: RelaySDPSourceInfo.h
+
+ Contains: This object takes input SDP data, and uses it to support the SourceInfo
+ API. It looks for the x-qt-relay lines put in by some broadcasters,
+ and uses that information to construct OutputInfo objects.
+
+
+*/
+
+#ifndef __RELAY_SDP_SOURCE_INFO_H__
+#define __RELAY_SDP_SOURCE_INFO_H__
+
+#include "StrPtrLen.h"
+#include "SourceInfo.h"
+
+class RelaySDPSourceInfo : public SourceInfo
+{
+ public:
+
+ // Reads in the SDP data from this file, and builds up the SourceInfo structures
+ RelaySDPSourceInfo(StrPtrLen* inSDPData) { Parse(inSDPData); }
+ virtual ~RelaySDPSourceInfo();
+
+ private:
+
+ void Parse(StrPtrLen* inSDPData);
+};
+#endif // __SDP_SOURCE_INFO_H__
+
+
diff --git a/APIModules/QTSSReflectorModule/RelaySession.cpp b/APIModules/QTSSReflectorModule/RelaySession.cpp
new file mode 100644
index 0000000..d76851f
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/RelaySession.cpp
@@ -0,0 +1,269 @@
+/*
+ *
+ * @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: RelaySession.cpp
+
+ Contains: Implementation of object defined in RelaySession.h.
+
+
+
+*/
+
+
+#include "RelaySession.h"
+#include "QTSSModuleUtils.h"
+#include "SocketUtils.h"
+#include "../../revision.h"
+
+static StrPtrLen sUDPSourceStr("udp_source");
+static StrPtrLen sRTSPSourceStr("rtsp_source");
+static StrPtrLen sAnnouncedSourceStr("announced_source");
+static StrPtrLen sEmptyStr("");
+
+static char* sRelaySessionObjectName = "relay_session";
+static char* sRelayNameName = "relay_name";
+static char* sSourceTypeName = "source_type";
+static char* sSourceIPAddrName = "source_ip_addr";
+static char* sSourceInIPAddrName = "source_in_ip_addr";
+static char* sSourceUDPPortsName = "source_udp_ports";
+static char* sSourceRTSPPortName = "source_rtsp_port";
+static char* sSourceURLName = "source_url";
+static char* sSourceUsernameName = "source_username";
+static char* sSourcePasswordName = "source_password";
+static char* sSourceTTLName = "source_ttl";
+static char* sRelayOutputObjectName = "relay_output";
+
+QTSS_Object RelaySession::relayModuleAttributesObject;
+QTSS_ObjectType RelaySession::qtssRelaySessionObjectType;
+
+QTSS_AttributeID RelaySession::sRelaySessionObjectID = qtssIllegalAttrID;
+QTSS_AttributeID RelaySession::sRelayName = qtssIllegalAttrID;
+QTSS_AttributeID RelaySession::sSourceType = qtssIllegalAttrID;
+QTSS_AttributeID RelaySession::sSourceIPAddr = qtssIllegalAttrID;
+QTSS_AttributeID RelaySession::sSourceInIPAddr = qtssIllegalAttrID;
+QTSS_AttributeID RelaySession::sSourceUDPPorts = qtssIllegalAttrID;
+QTSS_AttributeID RelaySession::sSourceRTSPPort = qtssIllegalAttrID;
+QTSS_AttributeID RelaySession::sSourceURL = qtssIllegalAttrID;
+QTSS_AttributeID RelaySession::sSourceUsername = qtssIllegalAttrID;
+QTSS_AttributeID RelaySession::sSourcePassword = qtssIllegalAttrID;
+QTSS_AttributeID RelaySession::sSourceTTL = qtssIllegalAttrID;
+QTSS_AttributeID RelaySession::sRelayOutputObject = qtssIllegalAttrID;
+
+char RelaySession::sRelayUserAgent[20] = "";
+
+void RelaySession::Register()
+{
+ qtssRelaySessionObjectType = 0;
+
+ // create relay session object type
+ (void)QTSS_CreateObjectType(&qtssRelaySessionObjectType);
+
+ // Add the static attributes to the qtssRelaySessionObjectType object
+ (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sRelayNameName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sRelayNameName, &sRelayName); // relay name
+
+ (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceTypeName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceTypeName, &sSourceType); // source type
+
+ (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceIPAddrName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceIPAddrName, &sSourceIPAddr); // source addr
+
+ (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceInIPAddrName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceInIPAddrName, &sSourceInIPAddr); // interface addr
+
+ (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceUDPPortsName, NULL, qtssAttrDataTypeUInt16);
+ (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceUDPPortsName, &sSourceUDPPorts); // udp ports
+
+ (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceRTSPPortName, NULL, qtssAttrDataTypeUInt16);
+ (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceRTSPPortName, &sSourceRTSPPort); // rtsp port
+
+ (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceURLName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceURLName, &sSourceURL); // url
+
+ (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceUsernameName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceUsernameName, &sSourceUsername); // username
+
+ (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourcePasswordName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourcePasswordName, &sSourcePassword); // password
+
+ (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sSourceTTLName, NULL, qtssAttrDataTypeUInt16);
+ (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sSourceTTLName, &sSourceTTL); // ttl
+
+ (void)QTSS_AddStaticAttribute(qtssRelaySessionObjectType, sRelayOutputObjectName, NULL, qtssAttrDataTypeQTSS_Object);
+ (void)QTSS_IDForAttr(qtssRelaySessionObjectType, sRelayOutputObjectName, &sRelayOutputObject); // relay output
+
+ //char* strEnd = NULL;
+ char* relayStr = "QTSS_Relay/";
+ //kVersionString is changed now -- it doesn't contain any spaces or the build number
+ //strEnd = strchr(kVersionString, ' ');
+ //Assert(strEnd != NULL);
+#ifndef __Win32__
+ //qtss_snprintf(sRelayUserAgent, ::strlen(relayStr) + (strEnd - kVersionString) + 1, "%s/%s", relayStr, kVersionString);
+ qtss_snprintf(sRelayUserAgent, ::strlen(relayStr) + ::strlen(kVersionString) + 1, "%s%s", relayStr, kVersionString);
+#else
+ //_snprintf(sRelayUserAgent, ::strlen(relayStr) + (strEnd - kVersionString) + 1, "%s/%s", relayStr, kVersionString);
+ _snprintf(sRelayUserAgent, ::strlen(relayStr) + ::strlen(kVersionString) + 1, "%s%s", relayStr, kVersionString);
+#endif
+}
+
+void RelaySession::Initialize(QTSS_Object inRelayModuleAttributesObject)
+{
+ ReflectorSession::Initialize();
+
+ if (inRelayModuleAttributesObject != NULL)
+ {
+ relayModuleAttributesObject = inRelayModuleAttributesObject;
+ sRelaySessionObjectID = QTSSModuleUtils::CreateAttribute(inRelayModuleAttributesObject, sRelaySessionObjectName, qtssAttrDataTypeQTSS_Object, NULL, 0);
+ }
+}
+
+QTSS_Error RelaySession::SetupRelaySession(SourceInfo* inInfo)
+{
+ QTSS_Error theErr = QTSS_NoErr;
+ theErr = this->SetupReflectorSession(inInfo, NULL);
+
+ if (theErr != QTSS_NoErr)
+ return theErr;
+
+ // create the reflector session object for this session
+ UInt32 outIndex = 0;
+ fRelaySessionObject = NULL;
+
+ theErr = QTSS_LockObject(relayModuleAttributesObject);
+ Assert(theErr == QTSS_NoErr);
+
+ theErr = QTSS_CreateObjectValue (relayModuleAttributesObject , sRelaySessionObjectID, qtssRelaySessionObjectType, &outIndex, &fRelaySessionObject);
+ Assert(theErr == QTSS_NoErr);
+
+ if (fRelaySessionObject == NULL)
+ return theErr;
+
+ // set the values for all the static attributes in this session
+
+ char* relayName = inInfo->Name(); // name of the relay
+ if (relayName != NULL)
+ theErr = QTSS_SetValue (fRelaySessionObject, sRelayName, 0, (void*)relayName, ::strlen(relayName));
+ else
+ theErr = QTSS_SetValue (fRelaySessionObject, sRelayName, 0, (void*)sEmptyStr.Ptr, sEmptyStr.Len);
+ Assert(theErr == QTSS_NoErr);
+
+
+ StrPtrLen sourceStr; // type of source
+
+ if (inInfo->IsRTSPSourceInfo())
+ {
+ if (((RTSPSourceInfo*)inInfo)->IsAnnounce())
+ sourceStr.Set(sAnnouncedSourceStr.Ptr, sAnnouncedSourceStr.Len);
+ else
+ sourceStr.Set(sRTSPSourceStr.Ptr, sRTSPSourceStr.Len);
+ }
+ else
+ sourceStr.Set(sUDPSourceStr.Ptr, sUDPSourceStr.Len);
+
+ theErr = QTSS_SetValue (fRelaySessionObject, sSourceType, 0, (void*)sourceStr.Ptr, sourceStr.Len);
+ Assert(theErr == QTSS_NoErr);
+
+ char theIPAddrBuf[20];
+ StrPtrLen theIPAddr(theIPAddrBuf, 20);
+
+ struct in_addr theSrcAddr; // source ip address
+ theSrcAddr.s_addr = htonl(inInfo->GetStreamInfo(0)->fSrcIPAddr);
+ SocketUtils::ConvertAddrToString(theSrcAddr, &theIPAddr);
+
+ theErr = QTSS_SetValue (fRelaySessionObject, sSourceIPAddr, 0, (void*)theIPAddr.Ptr, theIPAddr.Len);
+ Assert(theErr == QTSS_NoErr);
+
+ struct in_addr theDestAddr; // dest (of source) ip address
+ theDestAddr.s_addr = htonl(inInfo->GetStreamInfo(0)->fDestIPAddr);
+ SocketUtils::ConvertAddrToString(theDestAddr, &theIPAddr);
+
+ theErr = QTSS_SetValue (fRelaySessionObject, sSourceInIPAddr, 0, (void*)theIPAddr.Ptr, theIPAddr.Len);
+ Assert(theErr == QTSS_NoErr);
+
+
+ for (UInt32 index = 0; index < (inInfo->GetNumStreams()); index++) // source udp ports
+ {
+ UInt16 udpPort = inInfo->GetStreamInfo(index)->fPort;
+ theErr = QTSS_SetValue (fRelaySessionObject, sSourceUDPPorts, index, &udpPort, sizeof(udpPort));
+ Assert(theErr == QTSS_NoErr);
+ }
+
+ if (inInfo->IsRTSPSourceInfo())
+ {
+ RTSPSourceInfo* rtspInfo = (RTSPSourceInfo*)inInfo;
+ if (!rtspInfo->IsAnnounce())
+ {
+ UInt16 rtspPort = (UInt16) rtspInfo->GetHostPort();
+ char* username = rtspInfo->GetUsername();
+ char* password = rtspInfo->GetPassword();
+ theErr = QTSS_SetValue (fRelaySessionObject, sSourceRTSPPort, 0, &rtspPort, sizeof(rtspPort)); // source rtsp port
+ Assert(theErr == QTSS_NoErr);
+
+ theErr = QTSS_SetValue (fRelaySessionObject, sSourceUsername, 0, username, sizeof(username)); // source username
+ Assert(theErr == QTSS_NoErr);
+
+ theErr = QTSS_SetValue (fRelaySessionObject, sSourcePassword, 0, password, sizeof(password)); // source password
+ Assert(theErr == QTSS_NoErr);
+ }
+
+ char* url = rtspInfo->GetSourceURL();
+ theErr = QTSS_SetValue (fRelaySessionObject, sSourceURL, 0, url, ::strlen(url));
+ Assert(theErr == QTSS_NoErr); // source url
+ }
+
+ UInt16 ttl = inInfo->GetStreamInfo(0)->fTimeToLive;
+ theErr = QTSS_SetValue (fRelaySessionObject, sSourceTTL, 0, &ttl, sizeof(ttl)); // source ttl
+ Assert(theErr == QTSS_NoErr);
+
+ theErr = QTSS_UnlockObject(relayModuleAttributesObject);
+ Assert(theErr == QTSS_NoErr);
+
+ return QTSS_NoErr;
+}
+
+RelaySession::~RelaySession()
+{
+ QTSS_Object sessionObject;
+ UInt32 len = sizeof(QTSS_Object);
+
+ for (int x = 0; QTSS_GetValue(relayModuleAttributesObject, sRelaySessionObjectID, x, &sessionObject, &len) == QTSS_NoErr; x++)
+ {
+ Assert(sessionObject != NULL);
+ Assert(len == sizeof(QTSS_Object));
+
+ if (sessionObject == fRelaySessionObject)
+ {
+ (void)QTSS_RemoveValue(relayModuleAttributesObject, sRelaySessionObjectID, x);
+ break;
+ }
+ }
+}
+
+
+
+
+
+
+
diff --git a/APIModules/QTSSReflectorModule/RelaySession.h b/APIModules/QTSSReflectorModule/RelaySession.h
new file mode 100644
index 0000000..3b7e092
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/RelaySession.h
@@ -0,0 +1,89 @@
+/*
+ *
+ * @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: RelaySession.h
+
+ Contains: Subclass of ReflectorSession. It has two static
+ attributes (QTSSRelayModule Attributes object
+ and the ReflectorSession attribute ID)
+
+*/
+
+#include "QTSS.h"
+#include "ReflectorSession.h"
+#include "StrPtrLen.h"
+#include "SourceInfo.h"
+#include "RTSPSourceInfo.h"
+
+#ifndef _RELAY_SESSION_
+#define _RELAY_SESSION_
+
+class RelaySession : public ReflectorSession
+{
+ public:
+
+ // Call Register in the Relay Module's Register Role
+ static void Register();
+
+ //
+ // Initialize
+ // Call Initialize in the Relay Module's Initialize Role
+ static void Initialize(QTSS_Object inAttrObject);
+
+ RelaySession(StrPtrLen* inSourceID, SourceInfo* inInfo = NULL):ReflectorSession(inSourceID, inInfo){};
+ ~RelaySession();
+
+ QTSS_Error SetupRelaySession(SourceInfo* inInfo);
+
+ QTSS_Object GetRelaySessionObject() { return fRelaySessionObject; }
+ static QTSS_AttributeID sRelayOutputObject;
+
+ static char sRelayUserAgent[20];
+
+ private:
+
+ QTSS_Object fRelaySessionObject;
+
+ // gets set in the initialize method
+ static QTSS_Object relayModuleAttributesObject;
+
+ static QTSS_ObjectType qtssRelaySessionObjectType;
+
+ static QTSS_AttributeID sRelaySessionObjectID;
+ static QTSS_AttributeID sRelayName;
+ static QTSS_AttributeID sSourceType;
+ static QTSS_AttributeID sSourceIPAddr;
+ static QTSS_AttributeID sSourceInIPAddr;
+ static QTSS_AttributeID sSourceUDPPorts;
+ static QTSS_AttributeID sSourceRTSPPort;
+ static QTSS_AttributeID sSourceURL;
+ static QTSS_AttributeID sSourceUsername;
+ static QTSS_AttributeID sSourcePassword;
+ static QTSS_AttributeID sSourceTTL;
+
+
+};
+
+#endif
diff --git a/APIModules/QTSSReflectorModule/SequenceNumberMap.cpp b/APIModules/QTSSReflectorModule/SequenceNumberMap.cpp
new file mode 100644
index 0000000..04275f9
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/SequenceNumberMap.cpp
@@ -0,0 +1,153 @@
+/*
+ *
+ * @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: SequenceNumberMap.cpp
+
+ Contains: Implements object defined in SequenceNumberMap.h.
+
+
+
+
+*/
+
+#include
+#include "MyAssert.h"
+#include "OSMemory.h"
+
+#include "SequenceNumberMap.h"
+
+SequenceNumberMap::SequenceNumberMap(UInt32 inSlidingWindowSize)
+: fSlidingWindow(NULL),
+ fWindowSize( (SInt32) inSlidingWindowSize),
+ fNegativeWindowSize( (SInt32) inSlidingWindowSize - (SInt32) (2 * inSlidingWindowSize)),
+ fHighestSeqIndex(0),
+ fHighestSeqNumber(0)
+{
+ Assert(fNegativeWindowSize < 0);
+ Assert(fWindowSize < 32768);//AddSequenceNumber makes this assumption
+
+}
+
+Bool16 SequenceNumberMap::AddSequenceNumber(UInt16 inSeqNumber)
+{
+ // Returns whether sequence number has already been added.
+
+ //Check to see if object has been initialized
+ if (fSlidingWindow == NULL)
+ {
+ fSlidingWindow = NEW Bool16[fWindowSize + 1];
+ ::memset(fSlidingWindow, 0, fWindowSize * sizeof(Bool16));
+ fHighestSeqIndex = 0;
+ fHighestSeqNumber = inSeqNumber;
+ }
+
+ // First check to see if this sequence number is so far below the highest sequence number
+ // we can't even put it in the sliding window.
+
+ SInt16 theWindowOffset = inSeqNumber - fHighestSeqNumber;
+
+ if (theWindowOffset < fNegativeWindowSize)
+ return false;//We don't know, but for safety, assume we haven't seen it.
+
+ // If this seq # is higher thn the highest previous, set the highest to be this
+ // new sequence number, and zero out our sliding window as we go.
+
+ while (theWindowOffset > 0)
+ {
+ fHighestSeqNumber++;
+
+ fHighestSeqIndex++;
+ if (fHighestSeqIndex == fWindowSize)
+ fHighestSeqIndex = 0;
+ fSlidingWindow[fHighestSeqIndex] = false;
+
+ theWindowOffset--;
+ }
+
+ // Find the right entry in the sliding window for this sequence number, taking
+ // into account that we may need to wrap.
+
+ SInt32 theWindowIndex = fHighestSeqIndex + theWindowOffset;
+ if (theWindowIndex < 0)
+ theWindowIndex += fWindowSize;
+
+ Assert(theWindowIndex >= 0);
+ Assert(theWindowIndex < fWindowSize);
+
+ // Turn this index on, return whether it was already turned on.
+ Bool16 alreadyAdded = fSlidingWindow[theWindowIndex];
+ fSlidingWindow[theWindowIndex] = true;
+#if SEQUENCENUMBERMAPTESTING
+ //if (alreadyAdded)
+ // qtss_printf("Found a duplicate seq num. Num = %d\n", inSeqNumber);
+#endif
+ return alreadyAdded;
+}
+
+#if SEQUENCENUMBERMAPTESTING
+void SequenceNumberMap::Test()
+{
+ SequenceNumberMap theMap1;
+ Bool16 retval = theMap1.AddSequenceNumber(64674);
+ Assert(retval == false);
+
+ retval = theMap1.AddSequenceNumber(64582);
+ Assert(retval == false);
+
+ retval = theMap1.AddSequenceNumber(64777);
+ Assert(retval == false);
+
+ retval = theMap1.AddSequenceNumber(64582);
+ Assert(retval == TRUE);
+
+ retval = theMap1.AddSequenceNumber(64674);
+ Assert(retval == TRUE);
+
+ retval = theMap1.AddSequenceNumber(1);
+ Assert(retval == FALSE);
+
+ retval = theMap1.AddSequenceNumber(65500);
+ Assert(retval == FALSE);
+
+ retval = theMap1.AddSequenceNumber(65500);
+ Assert(retval == FALSE);
+
+ retval = theMap1.AddSequenceNumber(32768);
+ Assert(retval == FALSE);
+
+ retval = theMap1.AddSequenceNumber(1024);
+ Assert(retval == FALSE);
+
+ retval = theMap1.AddSequenceNumber(32757);
+ Assert(retval == FALSE);
+
+ retval = theMap1.AddSequenceNumber(32799);
+ Assert(retval == FALSE);
+
+ retval = theMap1.AddSequenceNumber(32768);
+ Assert(retval == FALSE);
+
+}
+#endif
diff --git a/APIModules/QTSSReflectorModule/SequenceNumberMap.h b/APIModules/QTSSReflectorModule/SequenceNumberMap.h
new file mode 100644
index 0000000..0f206f0
--- /dev/null
+++ b/APIModules/QTSSReflectorModule/SequenceNumberMap.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * @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: SequenceNumberMap.h
+
+ Contains: Data structure for keeping track of duplicate sequence numbers.
+ Useful for removing duplicate packets from an RTP stream.
+
+
+
+
+*/
+
+#ifndef _SEQUENCE_NUMBER_MAP_H_
+#define _SEQUENCE_NUMBER_MAP_H_
+
+#include "OSHeaders.h"
+
+#define SEQUENCENUMBERMAPTESTING 1
+
+class SequenceNumberMap
+{
+ public:
+
+ enum
+ {
+ kDefaultSlidingWindowSize = 256
+ };
+
+ SequenceNumberMap(UInt32 inSlidingWindowSize = kDefaultSlidingWindowSize);
+ ~SequenceNumberMap() { delete [] fSlidingWindow; }
+
+ // Returns whether this sequence number was already added or not.
+ Bool16 AddSequenceNumber(UInt16 inSeqNumber);
+
+#if SEQUENCENUMBERMAPTESTING
+ static void Test();
+#endif
+
+ private:
+
+ Bool16* fSlidingWindow;
+
+ const SInt32 fWindowSize;
+ const SInt32 fNegativeWindowSize;
+
+ UInt16 fHighestSeqIndex;
+ UInt16 fHighestSeqNumber;
+};
+
+
+#endif
diff --git a/APIModules/QTSSSpamDefenseModule.bproj/QTSSSpamDefenseModule.cpp b/APIModules/QTSSSpamDefenseModule.bproj/QTSSSpamDefenseModule.cpp
new file mode 100644
index 0000000..47a1683
--- /dev/null
+++ b/APIModules/QTSSSpamDefenseModule.bproj/QTSSSpamDefenseModule.cpp
@@ -0,0 +1,284 @@
+/*
+ *
+ * @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: QTSSSpamDefanseModule.cpp
+
+ Contains: Implementation of module described in .h file
+
+
+
+*/
+
+#include "QTSSSpamDefenseModule.h"
+#include "OSHashTable.h"
+#include "OSMutex.h"
+#include "QTSSModuleUtils.h"
+#include "OSMemory.h"
+
+static QTSS_ModulePrefsObject sPrefs = NULL;
+static QTSS_StreamRef sErrorLogStream = NULL;
+
+class IPAddrTableKey;
+
+class IPAddrTableElem
+{
+ public:
+
+ IPAddrTableElem(UInt32 inIPAddr) : fIPAddr(inIPAddr), fRefCount(0), fNextHashEntry(NULL) {}
+ ~IPAddrTableElem() {}
+
+ UInt32 GetRefCount() { return fRefCount; }
+ void IncrementRefCount() { fRefCount++; }
+ void DecrementRefCount() { fRefCount--; }
+ private:
+
+ UInt32 fIPAddr;// this also serves as the hash value
+ UInt32 fRefCount;
+
+ IPAddrTableElem* fNextHashEntry;
+
+ friend class IPAddrTableKey;
+ friend class OSHashTable;
+};
+
+
+class IPAddrTableKey
+{
+public:
+
+ //CONSTRUCTOR / DESTRUCTOR:
+ IPAddrTableKey(UInt32 inIPAddr) : fIPAddr(inIPAddr) {}
+ ~IPAddrTableKey() {}
+
+
+private:
+
+ //PRIVATE ACCESSORS:
+ SInt32 GetHashKey() { return fIPAddr; }
+
+ //these functions are only used by the hash table itself. This constructor
+ //will break the "Set" functions.
+ IPAddrTableKey(IPAddrTableElem *elem) : fIPAddr(elem->fIPAddr) {}
+
+ friend int operator ==(const IPAddrTableKey &key1, const IPAddrTableKey &key2)
+ {
+ return (key1.fIPAddr == key2.fIPAddr);
+ }
+
+ //data:
+ UInt32 fIPAddr;
+
+ friend class OSHashTable;
+};
+
+typedef OSHashTable IPAddrHashTable;
+
+// STATIC DATA
+static IPAddrHashTable* sHashTable = NULL;
+static OSMutex* sMutex;
+static UInt32 sNumConnsPerIP = 0;
+static UInt32 sDefaultNumConnsPerIP = 100;
+
+// ATTRIBUTES
+static QTSS_AttributeID sIsFirstRequestAttr = qtssIllegalAttrID;
+static QTSS_AttributeID sTooManyConnectionsErr = qtssIllegalAttrID;
+
+// FUNCTION PROTOTYPES
+static QTSS_Error QTSSSpamDefenseModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
+static QTSS_Error Register(QTSS_Register_Params* inParams);
+static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
+static QTSS_Error RereadPrefs();
+static QTSS_Error Authorize(QTSS_StandardRTSP_Params* inParams);
+static QTSS_Error SessionClosing(QTSS_RTSPSession_Params* inParams);
+
+// FUNCTION IMPLEMENTATIONS
+
+
+QTSS_Error QTSSSpamDefenseModule_Main(void* inPrivateArgs)
+{
+ return _stublibrary_main(inPrivateArgs, QTSSSpamDefenseModuleDispatch);
+}
+
+
+QTSS_Error QTSSSpamDefenseModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
+{
+ switch (inRole)
+ {
+ case QTSS_Register_Role:
+ return Register(&inParams->regParams);
+ case QTSS_Initialize_Role:
+ return Initialize(&inParams->initParams);
+ case QTSS_RereadPrefs_Role:
+ return RereadPrefs();
+ case QTSS_RTSPAuthorize_Role:
+ return Authorize(&inParams->rtspAuthParams);
+ case QTSS_RTSPSessionClosing_Role:
+ return SessionClosing(&inParams->rtspSessionClosingParams);
+ }
+ return QTSS_NoErr;
+}
+
+QTSS_Error Register(QTSS_Register_Params* inParams)
+{
+ // The spam defense module has one preference, the number of connections
+ // to allow per ip addr
+ static char* sIsFirstRequestName = "QTSSSpamDefenseModuleIsFirstRequest";
+
+ // Add text messages attributes
+ static char* sTooManyConnectionsName = "QTSSSpamDefenseModuleTooManyConnections";
+
+ // Do role & attribute setup
+ (void)QTSS_AddRole(QTSS_Initialize_Role);
+ (void)QTSS_AddRole(QTSS_RereadPrefs_Role);
+
+ (void)QTSS_AddRole(QTSS_RTSPAuthorize_Role);
+ (void)QTSS_AddRole(QTSS_RTSPSessionClosing_Role);
+
+ (void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sIsFirstRequestName, NULL, qtssAttrDataTypeBool16);
+ (void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sIsFirstRequestName, &sIsFirstRequestAttr);
+
+ (void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sTooManyConnectionsName, NULL, qtssAttrDataTypeCharArray);
+ (void)QTSS_IDForAttr(qtssTextMessagesObjectType, sTooManyConnectionsName, &sTooManyConnectionsErr);
+
+ // Tell the server our name!
+ static char* sModuleName = "QTSSSpamDefenseModule";
+ ::strcpy(inParams->outModuleName, sModuleName);
+
+ return QTSS_NoErr;
+}
+
+QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
+{
+ // Setup module utils
+ QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
+ sErrorLogStream = inParams->inErrorLogStream;
+ sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
+ sMutex = NEW OSMutex();
+ sHashTable = NEW IPAddrHashTable(277);//277 is prime, I think...
+ RereadPrefs();
+ return QTSS_NoErr;
+}
+
+QTSS_Error RereadPrefs()
+{
+ QTSSModuleUtils::GetAttribute(sPrefs, "num_conns_per_ip_addr", qtssAttrDataTypeUInt32,
+ &sNumConnsPerIP, &sDefaultNumConnsPerIP, sizeof(sNumConnsPerIP));
+ return QTSS_NoErr;
+}
+
+QTSS_Error Authorize(QTSS_StandardRTSP_Params* inParams)
+{
+ static Bool16 sTrue = true;
+
+ Bool16* isFirstRequest = NULL;
+ UInt32* theIPAddr = NULL;
+ UInt32 theLen = 0;
+
+ // Only do anything if this is the first request
+ (void)QTSS_GetValuePtr(inParams->inRTSPSession, sIsFirstRequestAttr, 0, (void**)&isFirstRequest, &theLen);
+ if (isFirstRequest != NULL)
+ return QTSS_NoErr;
+
+ // Get the IP address of this client.
+ (void)QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesRemoteAddr, 0, (void**)&theIPAddr, &theLen);
+ if ((theIPAddr == NULL) || (theLen != sizeof(UInt32)))
+ {
+ return QTSS_NoErr;
+ }
+
+ IPAddrTableKey theKey(*theIPAddr);
+
+ // This must be atomic
+ OSMutexLocker locker(sMutex);
+
+ // Check to see if this client currently has a connection open.
+ IPAddrTableElem* theElem = sHashTable->Map(&theKey);
+ if (theElem == NULL)
+ {
+ // Client doesn't have a connetion open currently. Create a map element,
+ // and add it into the map.
+ theElem = NEW IPAddrTableElem(*theIPAddr);
+ sHashTable->Add(theElem);
+ }
+
+ // Check to see if this client has too many connections open. If it does,
+ // return an error, otherwise, allow the connection and increment the
+ // refcount.
+ if (theElem->GetRefCount() >= sNumConnsPerIP) {
+ QTSSModuleUtils::LogErrorStr(qtssMessageVerbosity, "Blocking connection from IP address");
+ return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientForbidden,
+ sTooManyConnectionsErr);
+ } else
+ theElem->IncrementRefCount();
+
+ // Mark the request so we'll know subsequent ones aren't the first.
+ // Note that we only do this if we've successfully added this client to our map.
+ // That way, we only remove it in SessionClosing if we've added it.
+ (void)QTSS_SetValue(inParams->inRTSPSession, sIsFirstRequestAttr, 0, &sTrue, sizeof(sTrue));
+
+ return QTSS_NoErr;
+}
+
+QTSS_Error SessionClosing(QTSS_RTSPSession_Params* inParams)
+{
+ UInt32* theIPAddr = NULL;
+ Bool16* isFirstRequest = NULL;
+ UInt32 theLen = 0;
+
+ // Only remove this session from the map if it has been added in the first place
+ (void)QTSS_GetValuePtr(inParams->inRTSPSession, sIsFirstRequestAttr, 0, (void**)&isFirstRequest, &theLen);
+ if (isFirstRequest == NULL)
+ return QTSS_NoErr;
+
+ // Get the IP address of this client.
+ (void)QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesRemoteAddr, 0, (void**)&theIPAddr, &theLen);
+ if ((theIPAddr == NULL) || (theLen != sizeof(UInt32)))
+ {
+ return QTSS_NoErr;
+ }
+
+ IPAddrTableKey theKey(*theIPAddr);
+
+ // This must be atomic
+ OSMutexLocker locker(sMutex);
+
+ // Check to see if this client currently has a connection open.
+ IPAddrTableElem* theElem = sHashTable->Map(&theKey);
+ if (theElem == NULL)
+ return QTSS_NoErr; //this may happen if there is another module denying connections
+
+ // Decrement the refcount
+ if (theElem->GetRefCount() > 0)
+ theElem->DecrementRefCount();
+
+ // If the refcount is 0, remove this from the map, and delete it.
+ if (theElem->GetRefCount() == 0)
+ {
+ sHashTable->Remove(theElem);
+ delete theElem;
+ }
+
+ return QTSS_NoErr;
+}
diff --git a/APIModules/QTSSSpamDefenseModule.bproj/QTSSSpamDefenseModule.h b/APIModules/QTSSSpamDefenseModule.bproj/QTSSSpamDefenseModule.h
new file mode 100644
index 0000000..d6d09d3
--- /dev/null
+++ b/APIModules/QTSSSpamDefenseModule.bproj/QTSSSpamDefenseModule.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * @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: QTSSSpamDefanseModule.h
+
+ Contains: Protects the server against denial-of-service attacks by
+ only allowing X number of RTSP connections from a certain
+ IP address
+
+
+
+
+*/
+
+
+#ifndef __QTSSSPAMDEFENSEMODULE_H__
+#define __QTSSSPAMDEFENSEMODULE_H__
+
+#include "QTSS.h"
+
+extern "C"
+{
+ EXPORT QTSS_Error QTSSSpamDefenseModule_Main(void* inPrivateArgs);
+}
+
+#endif // __QTSSSPAMDEFENSEMODULE_H__
+
diff --git a/APIModules/QTSSWebDebugModule/QTSSWebDebugModule.cpp b/APIModules/QTSSWebDebugModule/QTSSWebDebugModule.cpp
new file mode 100644
index 0000000..37157fc
--- /dev/null
+++ b/APIModules/QTSSWebDebugModule/QTSSWebDebugModule.cpp
@@ -0,0 +1,148 @@
+/*
+ *
+ * @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: QTSSWebDebugModule.cpp
+
+ Contains: Implements web debug module
+
+
+*/
+
+#include "QTSSWebDebugModule.h"
+#include "OS.h"
+#include "OSMemory.h"
+#include "StrPtrLen.h"
+#include "ev.h"
+
+// STATIC DATA
+
+static QTSS_AttributeID sStateAttr = qtssIllegalAttrID;
+
+static StrPtrLen sRequestHeader("GET /debug HTTP");
+
+#if MEMORY_DEBUGGING
+static char* sResponseHeader = "HTTP/1.0 200 OK\r\nServer: TimeShare/1.0\r\nConnection: Close\r\nContent-Type: text/html\r\n\r\n";
+static char* sResponseEnd = "";
+#endif
+
+// FUNCTION PROTOTYPES
+
+QTSS_Error QTSSWebDebugModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
+static QTSS_Error Register(QTSS_Register_Params* inParams);
+static QTSS_Error Filter(QTSS_Filter_Params* inParams);
+
+QTSS_Error QTSSWebDebugModule_Main(void* inPrivateArgs)
+{
+ return _stublibrary_main(inPrivateArgs, QTSSWebDebugModuleDispatch);
+}
+
+QTSS_Error QTSSWebDebugModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
+{
+ switch (inRole)
+ {
+ case QTSS_Register_Role:
+ return Register(&inParams->regParams);
+ case QTSS_RTSPFilter_Role:
+ return Filter(&inParams->rtspFilterParams);
+ }
+ return QTSS_NoErr;
+}
+
+QTSS_Error Register(QTSS_Register_Params* inParams)
+{
+ // Do role & attribute setup
+ (void)QTSS_AddRole(QTSS_RTSPFilter_Role);
+
+ // Register an attribute
+ static char* sStateName = "QTSSWebDebugModuleState";
+ (void)QTSS_AddStaticAttribute(qtssRTSPRequestObjectType, sStateName, NULL, qtssAttrDataTypeUInt32);
+ (void)QTSS_IDForAttr(qtssRTSPRequestObjectType, sStateName, &sStateAttr);
+
+ // Tell the server our name!
+ static char* sModuleName = "QTSSWebDebugModule";
+ ::strcpy(inParams->outModuleName, sModuleName);
+
+ return QTSS_NoErr;
+}
+
+QTSS_Error Filter(QTSS_Filter_Params* inParams)
+{
+ UInt32 theLen = 0;
+ char* theFullRequest = NULL;
+ (void)QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest, &theLen);
+
+ if ((theFullRequest == NULL) || (theLen < sRequestHeader.Len))
+ return QTSS_NoErr;
+ if (::memcmp(theFullRequest, sRequestHeader.Ptr, sRequestHeader.Len) != 0)
+ return QTSS_NoErr;
+
+#if MEMORY_DEBUGGING
+ UInt32* theStateVal = NULL;
+ (void)QTSS_GetValuePtr(inParams->inRTSPRequest, sStateAttr, 0, (void**)&theStateVal, &theLen);
+ //if ((theStateVal == NULL) || (theLen != sizeof(UInt32)))
+ //{
+ Bool16 theFalse = false;
+ (void)QTSS_SetValue(inParams->inRTSPRequest, qtssRTSPReqRespKeepAlive, 0, &theFalse, sizeof(theFalse));
+
+ // Begin writing the HTTP response. We don't need to worry about flow control
+ // because we're using the QTSS_RTSPRequestObject for the response, which does buffering
+ (void)QTSS_Write(inParams->inRTSPRequest, sResponseHeader, ::strlen(sResponseHeader), &theLen, 0);
+
+ //QTSS_EventContextRef* theContext = NULL;
+ //(void)QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesEventCntxt, 0, (void**)&theContext, &theLen);
+ //Assert(theContext != NULL);
+ //Assert(theLen == sizeof(QTSS_EventContextRef));
+
+ //(void)QTSS_RequestEvent(*theContext, EV_WR);
+
+ // UInt32 theValue = 4;
+ // (void)QTSS_SetValue(inParams->inRTSPRequest, sStateAttr, 0, &theValue, sizeof(theValue));
+ // return QTSS_NoErr;
+ //}
+
+ //we must hold the tagQueue mutex for the duration of this exercise because
+ //we don't want any of the values we are reporting to change
+ OSMutexLocker locker(OSMemory::GetTagQueueMutex());
+
+ //write out header and total allocated memory
+ char buffer[1024];
+ qtss_sprintf(buffer, "TimeShare Debug PageTotal dynamic memory allocated: %"_S32BITARG_"List of objects:
", OSMemory::GetAllocatedMemory());
+ (void)QTSS_Write(inParams->inRTSPRequest, buffer, ::strlen(buffer), &theLen, 0);
+
+ //now report the list of tags:
+ for (OSQueueIter iter(OSMemory::GetTagQueue()); !iter.IsDone(); iter.Next())
+ {
+ OSMemory::TagElem* elem = (OSMemory::TagElem*)iter.GetCurrent()->GetEnclosingObject();
+ Assert(elem != NULL);
+ if (elem->numObjects > 0)
+ {
+ qtss_sprintf(buffer, "Object allocated at: %s, %d. Number of currently allocated objects: %"_S32BITARG_", Total size: %"_S32BITARG_"
", elem->fileName, elem->line, elem->numObjects, elem->totMemory);
+ (void)QTSS_Write(inParams->inRTSPRequest, buffer, ::strlen(buffer), &theLen, 0);
+ }
+ }
+ (void)QTSS_Write(inParams->inRTSPRequest, sResponseEnd, ::strlen(sResponseEnd), &theLen, 0);
+#endif
+ return QTSS_NoErr;
+}
diff --git a/APIModules/QTSSWebDebugModule/QTSSWebDebugModule.h b/APIModules/QTSSWebDebugModule/QTSSWebDebugModule.h
new file mode 100644
index 0000000..efd3530
--- /dev/null
+++ b/APIModules/QTSSWebDebugModule/QTSSWebDebugModule.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * @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: QTSSWebDebugModule.h
+
+ Contains: A module that uses the debugging information available in the server
+ to present a web page containing that information. Uses the Filter
+ module feature of the QTSS API.
+
+
+
+
+*/
+
+#ifndef __QTSSWEBDEBUGMODULE_H__
+#define __QTSSWEBDEBUGMODULE_H__
+
+#include "QTSS.h"
+
+extern "C"
+{
+ EXPORT QTSS_Error QTSSWebDebugModule_Main(void* inPrivateArgs);
+}
+
+#endif //__QTSSWEBDEBUGMODULE_H__
diff --git a/APIModules/QTSSWebStatsModule/QTSSWebStatsModule.cpp b/APIModules/QTSSWebStatsModule/QTSSWebStatsModule.cpp
new file mode 100644
index 0000000..4da7287
--- /dev/null
+++ b/APIModules/QTSSWebStatsModule/QTSSWebStatsModule.cpp
@@ -0,0 +1,991 @@
+/*
+ *
+ * @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: QTSSWebStatsModule.cpp
+
+ Contains: Implements web stats module
+
+
+
+
+
+*/
+
+#include
+
+#include /* for qtss_printf */
+#include /* for getloadavg & other useful stuff */
+
+#include "QTSSWebStatsModule.h"
+#include "OSArrayObjectDeleter.h"
+#include "StringParser.h"
+#include "StrPtrLen.h"
+#include "QTSSModuleUtils.h"
+
+// STATIC DATA
+
+static char* sResponseHeader = "HTTP/1.0 200 OK\r\nServer: QTSS/3.0\r\nConnection: Close\r\nContent-Type: text/html\r\n\r\n";
+
+static QTSS_ServerObject sServer = NULL;
+static QTSS_ModulePrefsObject sAccessLogPrefs = NULL;
+static QTSS_ModulePrefsObject sReflectorPrefs = NULL;
+static QTSS_ModulePrefsObject sSvrControlPrefs = NULL;
+static QTSS_ModulePrefsObject sPrefs = NULL;
+static QTSS_PrefsObject sServerPrefs = NULL;
+
+static Bool16 sFalse = false;
+static time_t sStartupTime = 0;
+
+static char* sDefaultURL = "";
+static StrPtrLen sDefaultURLStr;
+static char* sDefaultURLPrefName = "web_stats_url";
+
+
+static QTSS_Error QTSSWebStatsModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
+static QTSS_Error Register(QTSS_Register_Params* inParams);
+static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
+static QTSS_Error FilterRequest(QTSS_Filter_Params* inParams);
+static void SendStats(QTSS_StreamRef inStream, UInt32 refreshInterval, Bool16 displayHelp, StrPtrLen* fieldList);
+static char* GetPrefAsString(QTSS_ModulePrefsObject inPrefsObject, char* inPrefName);
+
+
+// FUNCTION IMPLEMENTATIONS
+
+QTSS_Error QTSSWebStatsModule_Main(void* inPrivateArgs)
+{
+ return _stublibrary_main(inPrivateArgs, QTSSWebStatsModuleDispatch);
+}
+
+
+QTSS_Error QTSSWebStatsModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
+{
+ switch (inRole)
+ {
+ case QTSS_Register_Role:
+ return Register(&inParams->regParams);
+ case QTSS_Initialize_Role:
+ return Initialize(&inParams->initParams);
+ case QTSS_RTSPFilter_Role:
+ return FilterRequest(&inParams->rtspFilterParams);
+ }
+ return QTSS_NoErr;
+}
+
+
+QTSS_Error Register(QTSS_Register_Params* inParams)
+{
+ // Do role & attribute setup
+ (void)QTSS_AddRole(QTSS_Initialize_Role);
+ (void)QTSS_AddRole(QTSS_RTSPFilter_Role);
+
+ // Tell the server our name!
+ static char* sModuleName = "QTSSWebStatsModule";
+ ::strcpy(inParams->outModuleName, sModuleName);
+
+ return QTSS_NoErr;
+}
+
+
+QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
+{
+ // Setup module utils
+ QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
+
+ sAccessLogPrefs = QTSSModuleUtils::GetModulePrefsObject(QTSSModuleUtils::GetModuleObjectByName("QTSSAccessLogModule"));
+ sReflectorPrefs = QTSSModuleUtils::GetModulePrefsObject(QTSSModuleUtils::GetModuleObjectByName("QTSSReflectorModule"));
+ sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
+
+ // This module may not be present, so be careful...
+ QTSS_ModuleObject theSvrControlModule = QTSSModuleUtils::GetModuleObjectByName("QTSSSvrControlModule");
+ if (theSvrControlModule != NULL)
+ sSvrControlPrefs = QTSSModuleUtils::GetModulePrefsObject(theSvrControlModule);
+ sServer = inParams->inServer;
+ sServerPrefs = inParams->inPrefs;
+
+ sStartupTime = ::time(NULL);
+
+ sDefaultURLStr.Delete();
+ sDefaultURLStr.Set(QTSSModuleUtils::GetStringAttribute(sPrefs, sDefaultURLPrefName, sDefaultURL));
+
+ return QTSS_NoErr;
+}
+
+QTSS_Error FilterRequest(QTSS_Filter_Params* inParams)
+{
+ UInt8 sParamStopMask[] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-9 //stop unless a '\t', ' ', or '&'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10-19
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20-29
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, //30-39
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //40-49
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //50-59
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //60-69
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //70-79
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //80-89
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //90-99
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //100-109
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //110-119
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //120-129
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //130-139
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //140-149
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //150-159
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //160-169
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //170-179
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //180-189
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //190-199
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //200-209
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //210-219
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //220-229
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //230-239
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //240-249
+ 0, 0, 0, 0, 0, 1 //250-255
+ };
+
+
+ //check to see if we should handle this request. Invokation is triggered
+ //by a "GET /" request
+
+ QTSS_RTSPRequestObject theRequest = inParams->inRTSPRequest;
+
+ StrPtrLen theFullRequest;
+ (void)QTSS_GetValuePtr(theRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest.Ptr, &theFullRequest.Len);
+
+ StringParser fullRequest(&theFullRequest);
+
+ StrPtrLen strPtr;
+ StrPtrLen paramName;
+ StrPtrLen fieldsList;
+
+ fullRequest.ConsumeWord(&strPtr);
+ if ( strPtr.Equal(StrPtrLen("GET")) ) //it's a "Get" request
+ {
+ fullRequest.ConsumeWhitespace();
+ if ( fullRequest.Expect('/') )
+ {
+ UInt32 refreshInterval = 0;
+ Bool16 displayHelp = false;
+
+
+ OSCharArrayDeleter theWebStatsURL(GetPrefAsString(sPrefs, sDefaultURLPrefName));
+ StrPtrLen theWebStatsURLPtr(theWebStatsURL.GetObject());
+
+ // If there isn't any web stats URL, we can just return at this point
+ if (theWebStatsURLPtr.Len == 0)
+ return QTSS_NoErr;
+
+ fullRequest.ConsumeUntil(&strPtr, StringParser::sEOLWhitespaceQueryMask);
+ if ( strPtr.Len != 0 && strPtr.Equal(theWebStatsURLPtr) ) //it's a "stats" request
+ {
+ if ( fullRequest.Expect('?') )
+ {
+ do {
+ fullRequest.ConsumeWord(¶mName);
+
+ if( paramName.Len != 0)
+ {
+
+ if ( paramName.Equal(StrPtrLen("refresh",strlen("refresh"))) )
+ {
+ if (fullRequest.Expect('='))
+ refreshInterval = fullRequest.ConsumeInteger(NULL);
+ }
+ else if ( paramName.Equal(StrPtrLen("help",strlen("help"))) )
+ {
+ displayHelp = true;
+ }
+ else if ( paramName.Equal(StrPtrLen("fields",strlen("fields"))) )
+ {
+ if (fullRequest.Expect('='))
+ fullRequest.ConsumeUntil(&fieldsList, (UInt8*)sParamStopMask);
+
+ }
+ }
+ } while ( paramName.Len != 0 && fullRequest.Expect('&') );
+ }
+
+ // Before sending a response, set keep alive to off for this connection
+ (void)QTSS_SetValue(theRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse));
+ SendStats(inParams->inRTSPRequest, refreshInterval, displayHelp, (fieldsList.Len != 0) ? &fieldsList : NULL);
+ }
+ }
+ }
+ return QTSS_NoErr;
+}
+
+
+void SendStats(QTSS_StreamRef inStream, UInt32 refreshInterval, Bool16 displayHelp, StrPtrLen* fieldList)
+{
+ struct FieldIndex {
+ char* fieldName;
+ int fieldIndex;
+ };
+
+ const FieldIndex kFieldIndexes[] = {
+ {"title", 1},
+ {"dnsname", 2},
+ {"curtime", 3},
+ {"", 4},
+ {"serververs", 5},
+ {"serverbornon", 6},
+ {"serverstatus", 7},
+ {"", 8},
+ {"", 9},
+ {"", 10},
+ {"", 11},
+ {"", 12},
+ {"", 13},
+ {"currtp", 14},
+ {"currtsp", 15},
+ {"currtsphttp", 16},
+ {"curthru", 17},
+ {"curpkts", 18},
+ {"totbytes", 19},
+ {"totconns", 20},
+ {"", 21},
+ {"connlimit", 22},
+ {"thrulimit", 23},
+ {"moviedir", 24},
+ {"rtspip", 25},
+ {"rtspport", 26},
+ {"rtsptimeout", 27},
+ {"rtptimeout", 28},
+ {"secstobuffer", 29},
+ {"", 30},
+ {"accesslog", 31},
+ {"accesslogdir",32},
+ {"accesslogname", 33},
+ {"accessrollsize", 34},
+ {"accessrollinterval", 35},
+ {"", 36},
+ {"errorlog", 37},
+ {"errorlogdir", 38},
+ {"errorlogname", 39},
+ {"errorrollsize", 40},
+ {"errorrollinterval", 41},
+ {"errorloglevel", 42},
+ {"", 43},
+ {"assertbreak", 44},
+ {"autostart", 45},
+ {"totbytesupdateinterval", 46},
+ {"reflectordelay", 47},
+ {"reflectorbucketsize", 48},
+ {"historyinterval", 49},
+ {"outoffiledesc", 50},
+ {"numudpsockets", 51},
+ {"apiversion", 52},
+ {"numreliableudpbuffers", 53},
+ {"reliableudpwastedbytes", 54},
+ {"numtaskthreads", 55}
+ };
+ const int kMaxFieldNum = 55;
+ static char* kEmptyStr = "?";
+ char* thePrefStr = kEmptyStr;
+
+ char buffer[1024];
+
+ (void)QTSS_Write(inStream, sResponseHeader, ::strlen(sResponseHeader), NULL, 0);
+
+ if (refreshInterval > 0)
+ {
+ qtss_sprintf(buffer, "\n", refreshInterval);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+
+ //qtss_sprintf(buffer, "\n");
+ //(void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+
+ char *theHTML = "\n";
+
+ (void)QTSS_Write(inStream, theHTML, ::strlen(theHTML), NULL, 0);
+
+ if (displayHelp)
+ {
+
+ #ifndef __MacOSX__
+ static StrPtrLen sHelpLine1("Streaming Server Statistics Help
\n");
+ #else
+ static StrPtrLen sHelpLine1("QuickTime Streaming Server Statistics Help
\n");
+ #endif
+
+ static StrPtrLen sHelpLine2("Example:
\n");
+ static StrPtrLen sHelpLine3("http://server/statsURL?help&refresh=15&fields=curtime,cpuload
\n");
+
+ static StrPtrLen sHelpLine4("\"?\" means that there are options being attached to the stats request.
\n");
+ static StrPtrLen sHelpLine5("\"&\" separates multiple stats options
\n
\n");
+
+ static StrPtrLen sHelpLine6("The three possible parameters to stats are:
\n");
+ static StrPtrLen sHelpLine7("\"help\" -- shows the help information you're reading right now.
\n");
+ static StrPtrLen sHelpLine8("\"refresh=[n]\" -- tells the browser to automatically update the page every [n] seconds.
\n");
+ static StrPtrLen sHelpLine9("\"fields=[fieldList]\" -- show only the fields specified in comma delimited [fieldList]
\n");
+ static StrPtrLen sHelpLine10("The following fields are available for use with the \"fields\" option:
\n");
+
+ (void)QTSS_Write(inStream, sHelpLine1.Ptr, sHelpLine1.Len, NULL, 0);
+ (void)QTSS_Write(inStream, sHelpLine2.Ptr, sHelpLine2.Len, NULL, 0);
+ (void)QTSS_Write(inStream, sHelpLine3.Ptr, sHelpLine3.Len, NULL, 0);
+
+ (void)QTSS_Write(inStream, sHelpLine4.Ptr, sHelpLine4.Len, NULL, 0);
+ (void)QTSS_Write(inStream, sHelpLine5.Ptr, sHelpLine5.Len, NULL, 0);
+
+ (void)QTSS_Write(inStream, sHelpLine6.Ptr, sHelpLine6.Len, NULL, 0);
+ (void)QTSS_Write(inStream, sHelpLine7.Ptr, sHelpLine7.Len, NULL, 0);
+ (void)QTSS_Write(inStream, sHelpLine8.Ptr, sHelpLine8.Len, NULL, 0);
+ (void)QTSS_Write(inStream, sHelpLine9.Ptr, sHelpLine9.Len, NULL, 0);
+ (void)QTSS_Write(inStream, sHelpLine10.Ptr, sHelpLine10.Len, NULL, 0);
+
+ for (short i = 0; i < kMaxFieldNum; i++)
+ {
+ qtss_sprintf(buffer, "- %s
\n", kFieldIndexes[i].fieldName);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+
+ static StrPtrLen sHelpLine11("
");
+ (void)QTSS_Write(inStream, sHelpLine11.Ptr, sHelpLine11.Len, NULL, 0);
+ }
+
+ StringParser fieldNamesParser(fieldList);
+ StrPtrLen fieldName;
+ int fieldNum = 0;
+ do
+ {
+
+
+ if (fieldList != NULL)
+ {
+ fieldNum = 0;
+
+ fieldNamesParser.ConsumeWord(&fieldName);
+
+ for (short i = 0; i < kMaxFieldNum; i++)
+ {
+ if ( fieldName.Equal(StrPtrLen(kFieldIndexes[i].fieldName, ::strlen(kFieldIndexes[i].fieldName))) )
+ {
+ fieldNum = kFieldIndexes[i].fieldIndex;
+ break;
+ }
+ }
+ }
+ else
+ {
+ fieldNum++;
+ if ( fieldNum > kMaxFieldNum )
+ fieldNum = 0;
+ }
+
+ UInt32 theLen = 0;
+
+ switch (fieldNum)
+ {
+ case 1:
+ {
+#if __MacOSX__
+ static StrPtrLen sStatsLine1("QuickTime Streaming Server Stats
\n");
+ (void)QTSS_Write(inStream, sStatsLine1.Ptr, sStatsLine1.Len, NULL, 0);
+#else
+ static StrPtrLen sStatsLine1("Streaming Server Stats
\n");
+ (void)QTSS_Write(inStream, sStatsLine1.Ptr, sStatsLine1.Len, NULL, 0);
+#endif
+
+#if __MacOSX__
+ static StrPtrLen sStatsLine2("QuickTime Streaming Server Statistics
\n");
+ (void)QTSS_Write(inStream, sStatsLine2.Ptr, sStatsLine2.Len, NULL, 0);
+#else
+ static StrPtrLen sStatsLine2("Streaming Server Statistics
\n");
+ (void)QTSS_Write(inStream, sStatsLine2.Ptr, sStatsLine2.Len, NULL, 0);
+#endif
+ }
+ break;
+
+ case 2:
+ {
+ StrPtrLen theDNS;
+ (void)QTSS_GetValuePtr(sServer, qtssSvrDefaultDNSName, 0, (void**)&theDNS.Ptr, &theDNS.Len);
+
+ if ( theDNS.Ptr == NULL )
+ { // no DNS, try for the IP address only.
+ (void)QTSS_GetValuePtr(sServer, qtssSvrDefaultIPAddr, 0, (void**)&theDNS.Ptr, &theDNS.Len);
+
+ }
+
+ if ( theDNS.Ptr != NULL )
+ {
+ qtss_sprintf(buffer, "DNS Name (default): %s
\n", theDNS.Ptr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ }
+ break;
+
+ case 3:
+ {
+ char uptimebuffer[1024];
+ time_t curTime = ::time(NULL);
+ qtss_sprintf(uptimebuffer, "Current Time: %s
\n", qtss_ctime(&curTime, buffer, sizeof(buffer)));
+ (void)QTSS_Write(inStream, uptimebuffer, ::strlen(uptimebuffer), NULL, 0);
+
+ time_t upTime = curTime - sStartupTime;
+ #define kDaySeconds (24 * 60 * 60)
+ #define kHourSeconds (60 * 60)
+ #define kMinuteSeconds 60
+
+ UInt32 upTimeDays = upTime / kDaySeconds;
+ UInt32 upTimeHours = (upTime % kDaySeconds) / kHourSeconds;
+ UInt32 upTimeMinutes = (upTime % kHourSeconds) / kMinuteSeconds;
+ UInt32 upTimeSeconds = (upTime % kMinuteSeconds);
+ qtss_snprintf(uptimebuffer,sizeof(uptimebuffer), "Up Time Total Seconds: %"_U32BITARG_"
\n", upTime);
+ uptimebuffer[sizeof(uptimebuffer) -1] = 0;
+ (void)QTSS_Write(inStream, uptimebuffer, ::strlen(uptimebuffer), NULL, 0);
+
+ qtss_snprintf(uptimebuffer,sizeof(uptimebuffer), "Up Time: %"_U32BITARG_" days %"_U32BITARG_" hours %"_U32BITARG_" minutes %"_U32BITARG_" seconds
\n", upTimeDays, upTimeHours,upTimeMinutes, upTimeSeconds);
+ uptimebuffer[sizeof(uptimebuffer) -1] = 0;
+ (void)QTSS_Write(inStream, uptimebuffer, ::strlen(uptimebuffer), NULL, 0);
+ }
+ break;
+
+ case 4:
+ {
+ (void)QTSS_Write(inStream, "
", ::strlen("
"), NULL, 0);
+ }
+ break;
+
+ case 5:
+ {
+ StrPtrLen theVersion;
+ (void)QTSS_GetValuePtr(sServer, qtssSvrRTSPServerHeader, 0, (void**)&theVersion.Ptr, &theVersion.Len);
+ Assert(theVersion.Ptr != NULL);
+ if (theVersion.Len > 7) //Skip the "Server:" text
+ theVersion.Ptr += 7;
+ qtss_sprintf(buffer, "Server Version: %s
\n", theVersion.Ptr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 6:
+ {
+ StrPtrLen theBuildDate;
+ (void)QTSS_GetValuePtr(sServer, qtssSvrServerBuildDate, 0, (void**)&theBuildDate.Ptr, &theBuildDate.Len);
+ Assert(theBuildDate.Ptr != NULL);
+ qtss_sprintf(buffer, "Server Build Date: %s
\n", theBuildDate.Ptr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 7:
+ {
+ char statusBuffer[1024];
+ const char* states[] = { "Starting Up",
+ "Running",
+ "Refusing Connections",
+ "Fatal Error",
+ "Shutting Down"
+ };
+ QTSS_ServerState theState = qtssRunningState;
+ theLen = sizeof(theState);
+ (void)QTSS_GetValue(sServer, qtssSvrState, 0, &theState, &theLen);
+
+ if (theState == qtssRunningState)
+ { qtss_snprintf(statusBuffer, sizeof(statusBuffer), "Status: %s since %s
", states[theState], qtss_ctime(&sStartupTime,buffer,sizeof(buffer)));
+ }
+ else
+ qtss_snprintf(statusBuffer,sizeof(statusBuffer), "Status: %s
", states[theState]);
+ (void)QTSS_Write(inStream, statusBuffer, ::strlen(statusBuffer), NULL, 0);
+ }
+ break;
+
+ case 8:
+ {
+ (void)QTSS_Write(inStream, "
", ::strlen("
"), NULL, 0);
+ }
+ break;
+
+ case 9:
+ {
+
+ //NOOP
+ }
+ break;
+
+ case 10:
+ {
+
+ //NOOP
+ }
+ break;
+
+ case 11:
+ {
+
+ //NOOP
+ }
+ break;
+
+ case 12:
+ {
+ /*
+ struct vm_statistics vmStats = {};
+ if (vm_statistics (current_task (), &vmStats) != KERN_SUCCESS)
+ memset (&stats, '\0', sizeof (vmStats)) ;
+ */
+ (void)QTSS_Write(inStream, "
", ::strlen("
"), NULL, 0);
+ }
+ break;
+
+ case 13:
+ {
+ (void)QTSS_Write(inStream, "
", ::strlen("
"), NULL, 0);
+ }
+ break;
+
+ //**********************************
+
+
+
+
+ case 14:
+ {
+ (void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurConn, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Current RTP Connections: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 15:
+ {
+ (void)QTSS_GetValueAsString(sServer, qtssRTSPCurrentSessionCount, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Current RTSP Connections: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 16:
+ {
+ (void)QTSS_GetValueAsString(sServer, qtssRTSPHTTPCurrentSessionCount, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Current RTSP over HTTP Connections: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 17:
+ {
+ UInt32 curBandwidth = 0;
+ theLen = sizeof(curBandwidth);
+ (void)QTSS_GetValue(sServer, qtssRTPSvrCurBandwidth, 0, &curBandwidth, &theLen);
+
+ qtss_sprintf(buffer, "Current Throughput: %"_U32BITARG_" kbits
\n", curBandwidth/1024);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 18:
+ {
+ (void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurPackets, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Current Packets Per Second: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 19:
+ {
+ (void)QTSS_GetValueAsString(sServer, qtssRTPSvrTotalBytes, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Total Bytes Served: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 20:
+ {
+ (void)QTSS_GetValueAsString(sServer, qtssRTPSvrTotalConn, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Total Connections: %s
", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 21:
+ {
+ (void)QTSS_Write(inStream, "
", ::strlen("
"), NULL, 0);
+ }
+ break;
+
+ //**************************************
+ case 22:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMaximumConnections, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Maximum Connections: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 23:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMaximumBandwidth, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Maximum Throughput: %s Kbits
\n",thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 24:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMovieFolder, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Movie Folder Path: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 25:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsRTSPIPAddr, 0, &thePrefStr);
+ qtss_sprintf(buffer, "RTSP IP Address: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 26:
+ {
+ static StrPtrLen sRTSPPortsStart("RTSP Ports: ");
+ (void)QTSS_Write(inStream, sRTSPPortsStart.Ptr, sRTSPPortsStart.Len, NULL, 0);
+
+ StrPtrLen thePort;
+ for (UInt32 theIndex = 0; true; theIndex++)
+ {
+ QTSS_Error theErr = QTSS_GetValuePtr(sServer, qtssSvrRTSPPorts, theIndex, (void**)&thePort.Ptr, &thePort.Len);
+ if (theErr != QTSS_NoErr)
+ break;
+
+ Assert(thePort.Ptr != NULL);
+ char temp[20];
+ qtss_sprintf(temp, "%u ", *(UInt16*)thePort.Ptr);
+ (void)QTSS_Write(inStream, temp, ::strlen(temp), NULL, 0);
+ }
+
+ static StrPtrLen sRTSPPortsEnd("
\n");
+ (void)QTSS_Write(inStream, sRTSPPortsEnd.Ptr, sRTSPPortsEnd.Len, NULL, 0);
+ }
+ break;
+
+ case 27:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsRTSPTimeout, 0, &thePrefStr);
+ qtss_sprintf(buffer, "RTP Timeout: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 28:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsRTPTimeout, 0, &thePrefStr);
+ qtss_sprintf(buffer, "RTP Timeout: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 29:
+ {
+ (void)QTSS_Write(inStream, "
", ::strlen("
"), NULL, 0);
+ }
+ break;
+
+ case 30:
+ {
+ (void)QTSS_Write(inStream, "
", ::strlen("
"), NULL, 0);
+ }
+ break;
+
+ case 31:
+ {
+ if ( sAccessLogPrefs != NULL )
+ {
+ thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logging");
+ qtss_sprintf(buffer, "Access Logging: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ }
+ break;
+
+ case 32:
+ {
+ if ( sAccessLogPrefs != NULL )
+ {
+ thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_dir");
+ qtss_sprintf(buffer, "Access Log Directory: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ }
+ break;
+
+ case 33:
+ {
+ if ( sAccessLogPrefs != NULL )
+ {
+ thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_name");
+ qtss_sprintf(buffer, "Access Log Name: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ }
+ break;
+
+ case 34:
+ {
+ if ( sAccessLogPrefs != NULL )
+ {
+ thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_size");
+ qtss_sprintf(buffer, "Access Log Roll Size: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ }
+ break;
+
+ case 35:
+ {
+ if ( sAccessLogPrefs != NULL )
+ {
+ thePrefStr = GetPrefAsString(sAccessLogPrefs, "request_logfile_interval");
+ qtss_sprintf(buffer, "Access Log Roll Interval (days): %s
", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ }
+ break;
+
+ case 36:
+ {
+ (void)QTSS_Write(inStream, "
", ::strlen("
"), NULL, 0);
+ }
+ break;
+
+ case 37:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogEnabled, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Error Logging: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 38:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogDir, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Error Log Directory: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 39:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogName, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Error Log Name: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 40:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMaxErrorLogSize, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Error Log Roll Size: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 41:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorRollInterval, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Error Log Roll Interval (days): %s
\n",thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 42:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsErrorLogVerbosity, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Error Log Verbosity: %s
", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 43:
+ {
+ (void)QTSS_Write(inStream, "
", ::strlen("
"), NULL, 0);
+ }
+ break;
+
+ case 44:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsBreakOnAssert, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Break On Assert: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 45:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsAutoRestart, 0, &thePrefStr);
+ qtss_sprintf(buffer, "AutoStart: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 46:
+ {
+ (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsTotalBytesUpdate, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Total Bytes Update Interval: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 47:
+ {
+ if (sReflectorPrefs != NULL)
+ {
+ thePrefStr = GetPrefAsString(sReflectorPrefs, "reflector_delay");
+ qtss_sprintf(buffer, "Reflector Delay Time: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ }
+ break;
+
+ case 48:
+ {
+ if (sReflectorPrefs != NULL)
+ {
+ thePrefStr = GetPrefAsString(sReflectorPrefs, "reflector_bucket_size");
+ qtss_sprintf(buffer, "Reflector Bucket Size: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ }
+ break;
+
+ case 49:
+ {
+ if ( sSvrControlPrefs != NULL)
+ {
+ thePrefStr = GetPrefAsString(sSvrControlPrefs, "history_update_interval");
+ qtss_sprintf(buffer, "History Update Interval: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ }
+ break;
+
+ case 50:
+ {
+ Bool16 isOutOfDescriptors = false;
+ theLen = sizeof(isOutOfDescriptors);
+ (void)QTSS_GetValue(sServer, qtssSvrIsOutOfDescriptors, 0, &isOutOfDescriptors, &theLen);
+
+ qtss_sprintf(buffer, "Out of file descriptors: %d
\n", isOutOfDescriptors);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 51:
+ {
+ (void)QTSS_GetValueAsString(sServer, qtssRTPSvrNumUDPSockets, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Number of UDP sockets: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 52:
+ {
+ UInt32 apiVersion = 0;
+ UInt32 size = sizeof(UInt32);
+ (void)QTSS_GetValue(sServer, qtssServerAPIVersion, 0, &apiVersion, &size);
+ qtss_sprintf(buffer, "API version: %d.%d
\n", (int)( (UInt32) (apiVersion & (UInt32) 0xFFFF0000L) >> 16), (int)(apiVersion &(UInt32) 0x0000FFFFL));
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 53:
+ {
+ UInt32 reliableUDPBuffers = 0;
+ UInt32 blahSize = sizeof(reliableUDPBuffers);
+ (void)QTSS_GetValue(sServer, qtssSvrNumReliableUDPBuffers, 0, &reliableUDPBuffers, &blahSize);
+ qtss_sprintf(buffer, "Num Reliable UDP Retransmit Buffers: %"_U32BITARG_"
\n", reliableUDPBuffers);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 54:
+ {
+ UInt32 wastedBufSpace = 0;
+ UInt32 blahSize2 = sizeof(wastedBufSpace);
+ (void)QTSS_GetValue(sServer, qtssSvrReliableUDPWastageInBytes, 0, &wastedBufSpace, &blahSize2);
+ qtss_sprintf(buffer, "Amount of buffer space being wasted in UDP Retrans buffers: %"_U32BITARG_"
\n", wastedBufSpace);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+ case 55:
+ {
+ (void)QTSS_GetValueAsString(sServer, qtssSvrNumThreads, 0, &thePrefStr);
+ qtss_sprintf(buffer, "Number of Task Threads: %s
\n", thePrefStr);
+ (void)QTSS_Write(inStream, buffer, ::strlen(buffer), NULL, 0);
+ }
+ break;
+
+
+
+ default:
+ break;
+
+
+ } //switch fieldNum
+
+
+ if (fieldList != NULL && !fieldNamesParser.Expect(','))
+ fieldNum = 0;
+
+ if (thePrefStr != kEmptyStr)
+ delete [] thePrefStr;
+
+ thePrefStr = kEmptyStr;
+
+ } while (fieldNum != 0);
+
+ theHTML = "\n";
+ (void)QTSS_Write(inStream, theHTML, ::strlen(theHTML), NULL, 0);
+}
+
+
+
+
+
+
+char* GetPrefAsString(QTSS_ModulePrefsObject inPrefsObject, char* inPrefName)
+{
+ static StrPtrLen sEmpty("");
+
+ //
+ // Get the attribute ID of this pref.
+ QTSS_AttributeID theID = qtssIllegalAttrID;
+
+ if(inPrefsObject != NULL)
+ theID = QTSSModuleUtils::GetAttrID(inPrefsObject, inPrefName);
+
+ char* theString = NULL;
+
+ if(inPrefsObject != NULL)
+ (void)QTSS_GetValueAsString(inPrefsObject, theID, 0, &theString);
+
+ if (theString == NULL)
+ theString = sEmpty.GetAsCString();
+
+ return theString;
+}
diff --git a/APIModules/QTSSWebStatsModule/QTSSWebStatsModule.h b/APIModules/QTSSWebStatsModule/QTSSWebStatsModule.h
new file mode 100644
index 0000000..6d72179
--- /dev/null
+++ b/APIModules/QTSSWebStatsModule/QTSSWebStatsModule.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * @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: QTSSWebStatsModule.h
+
+ Contains: A module that uses the stats information available in the server
+ to present a web page containing that information. Uses the Filter
+ module feature of QTSS API.
+
+
+*/
+
+#ifndef __QTSSWEBSTATSMODULE_H__
+#define __QTSSWEBSTATSMODULE_H__
+
+#include "QTSS.h"
+
+extern "C"
+{
+ EXPORT QTSS_Error QTSSWebStatsModule_Main(void* inPrivateArgs);
+}
+
+#endif // __QTSSWEBSTATSMODULE_H__
+
diff --git a/APIStubLib/QTSS.h b/APIStubLib/QTSS.h
new file mode 100644
index 0000000..963fc0b
--- /dev/null
+++ b/APIStubLib/QTSS.h
@@ -0,0 +1,2024 @@
+/*
+ *
+ * @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@
+ *
+ */
+
+#ifndef QTSS_H
+#define QTSS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "OSHeaders.h"
+#include "QTSSRTSPProtocol.h"
+
+#ifndef __Win32__
+#include
+#endif
+
+#define QTSS_API_VERSION 0x00050000
+#define QTSS_MAX_MODULE_NAME_LENGTH 64
+#define QTSS_MAX_SESSION_ID_LENGTH 32
+#define QTSS_MAX_ATTRIBUTE_NAME_SIZE 64
+
+
+//*******************************
+// ENUMERATED TYPES
+
+/**********************************/
+// Error Codes
+
+enum
+{
+ QTSS_NoErr = 0,
+ QTSS_RequestFailed = -1,
+ QTSS_Unimplemented = -2,
+ QTSS_RequestArrived = -3,
+ QTSS_OutOfState = -4,
+ QTSS_NotAModule = -5,
+ QTSS_WrongVersion = -6,
+ QTSS_IllegalService = -7,
+ QTSS_BadIndex = -8,
+ QTSS_ValueNotFound = -9,
+ QTSS_BadArgument = -10,
+ QTSS_ReadOnly = -11,
+ QTSS_NotPreemptiveSafe = -12,
+ QTSS_NotEnoughSpace = -13,
+ QTSS_WouldBlock = -14,
+ QTSS_NotConnected = -15,
+ QTSS_FileNotFound = -16,
+ QTSS_NoMoreData = -17,
+ QTSS_AttrDoesntExist = -18,
+ QTSS_AttrNameExists = -19,
+ QTSS_InstanceAttrsNotAllowed= -20
+};
+typedef SInt32 QTSS_Error;
+
+// QTSS_AddStreamFlags used in the QTSS_AddStream Callback function
+enum
+{
+ qtssASFlagsNoFlags = 0x00000000,
+ qtssASFlagsAllowDestination = 0x00000001,
+ qtssASFlagsForceInterleave = 0x00000002,
+ qtssASFlagsDontUseSlowStart = 0x00000004,
+ qtssASFlagsForceUDPTransport = 0x00000008
+};
+typedef UInt32 QTSS_AddStreamFlags;
+
+// QTSS_PlayFlags used in the QTSS_Play Callback function.
+enum
+{
+ qtssPlayFlagsSendRTCP = 0x00000010, // have the server generate RTCP Sender Reports
+ qtssPlayFlagsAppendServerInfo = 0x00000020 // have the server append the server info APP packet to your RTCP Sender Reports
+};
+typedef UInt32 QTSS_PlayFlags;
+
+// Flags for QTSS_Write when writing to a QTSS_ClientSessionObject.
+enum
+{
+ qtssWriteFlagsNoFlags = 0x00000000,
+ qtssWriteFlagsIsRTP = 0x00000001,
+ qtssWriteFlagsIsRTCP = 0x00000002,
+ qtssWriteFlagsWriteBurstBegin = 0x00000004,
+ qtssWriteFlagsBufferData = 0x00000008
+};
+typedef UInt32 QTSS_WriteFlags;
+
+// Flags for QTSS_SendStandardRTSPResponse
+enum
+{
+ qtssPlayRespWriteTrackInfo = 0x00000001,
+ qtssSetupRespDontWriteSSRC = 0x00000002
+};
+
+
+// Flags for the qtssRTSPReqAction attribute in a QTSS_RTSPRequestObject.
+enum
+{
+ qtssActionFlagsNoFlags = 0x00000000,
+ qtssActionFlagsRead = 0x00000001,
+ qtssActionFlagsWrite = 0x00000002,
+ qtssActionFlagsAdmin = 0x00000004,
+ qtssActionFlagsExtended = 0x40000000,
+ qtssActionQTSSExtended = 0x80000000,
+};
+typedef UInt32 QTSS_ActionFlags;
+
+/**********************************/
+// RTP SESSION STATES
+//
+// Is this session playing, paused, or what?
+enum
+{
+ qtssPausedState = 0,
+ qtssPlayingState = 1
+};
+typedef UInt32 QTSS_RTPSessionState;
+
+//*********************************/
+// CLIENT SESSION CLOSING REASON
+//
+// Why is this Client going away?
+enum
+{
+ qtssCliSesCloseClientTeardown = 0, // QTSS_Teardown was called on this session
+ qtssCliSesCloseTimeout = 1, // Server is timing this session out
+ qtssCliSesCloseClientDisconnect = 2 // Client disconnected.
+};
+typedef UInt32 QTSS_CliSesClosingReason;
+
+// CLIENT SESSION TEARDOWN REASON
+//
+// An attribute in the QTSS_ClientSessionObject
+//
+// When calling QTSS_Teardown, a module should specify the QTSS_CliSesTeardownReason in the QTSS_ClientSessionObject
+// if the tear down was not a client request.
+//
+enum
+{
+ qtssCliSesTearDownClientRequest = 0,
+ qtssCliSesTearDownUnsupportedMedia = 1,
+ qtssCliSesTearDownServerShutdown = 2,
+ qtssCliSesTearDownServerInternalErr = 3,
+ qtssCliSesTearDownBroadcastEnded = 4 // A broadcast the client was watching ended
+
+};
+typedef UInt32 QTSS_CliSesTeardownReason;
+
+// Events
+enum
+{
+ QTSS_ReadableEvent = 1,
+ QTSS_WriteableEvent = 2
+};
+typedef UInt32 QTSS_EventType;
+
+// Authentication schemes
+enum
+{
+ qtssAuthNone = 0,
+ qtssAuthBasic = 1,
+ qtssAuthDigest = 2
+};
+typedef UInt32 QTSS_AuthScheme;
+
+
+/**********************************/
+// RTSP SESSION TYPES
+//
+// Is this a normal RTSP session or an RTSP / HTTP session?
+enum
+{
+ qtssRTSPSession = 0,
+ qtssRTSPHTTPSession = 1,
+ qtssRTSPHTTPInputSession= 2 //The input half of an RTSPHTTP session. These session types are usually very short lived.
+};
+typedef UInt32 QTSS_RTSPSessionType;
+
+/**********************************/
+//
+// What type of RTP transport is being used for the RTP stream?
+enum
+{
+ qtssRTPTransportTypeUDP = 0,
+ qtssRTPTransportTypeReliableUDP = 1,
+ qtssRTPTransportTypeTCP = 2,
+ qtssRTPTransportType3GPPUDP = 3
+};
+typedef UInt32 QTSS_RTPTransportType;
+
+/**********************************/
+//
+// What type of RTP network mode is being used for the RTP stream?
+// unicast | multicast (mutually exclusive)
+enum
+{
+ qtssRTPNetworkModeDefault = 0, // not declared
+ qtssRTPNetworkModeMulticast = 1,
+ qtssRTPNetworkModeUnicast = 2
+};
+typedef UInt32 QTSS_RTPNetworkMode;
+
+
+
+/**********************************/
+//
+// The transport mode in a SETUP request
+enum
+{
+ qtssRTPTransportModePlay = 0,
+ qtssRTPTransportModeRecord = 1
+};
+typedef UInt32 QTSS_RTPTransportMode;
+
+/**********************************/
+// PAYLOAD TYPES
+//
+// When a module adds an RTP stream to a client session, it must specify
+// the stream's payload type. This is so that other modules can find out
+// this information in a generalized fashion. Here are the currently
+// defined payload types
+enum
+{
+ qtssUnknownPayloadType = 0,
+ qtssVideoPayloadType = 1,
+ qtssAudioPayloadType = 2
+};
+typedef UInt32 QTSS_RTPPayloadType;
+
+/**********************************/
+// QTSS API OBJECT TYPES
+enum
+{
+ qtssDynamicObjectType = FOUR_CHARS_TO_INT('d', 'y', 'm', 'c'), //dymc
+ qtssRTPStreamObjectType = FOUR_CHARS_TO_INT('r', 's', 't', 'o'), //rsto
+ qtssClientSessionObjectType = FOUR_CHARS_TO_INT('c', 's', 'e', 'o'), //cseo
+ qtssRTSPSessionObjectType = FOUR_CHARS_TO_INT('s', 's', 'e', 'o'), //sseo
+ qtssRTSPRequestObjectType = FOUR_CHARS_TO_INT('s', 'r', 'q', 'o'), //srqo
+ qtssRTSPHeaderObjectType = FOUR_CHARS_TO_INT('s', 'h', 'd', 'o'), //shdo
+ qtssServerObjectType = FOUR_CHARS_TO_INT('s', 'e', 'r', 'o'), //sero
+ qtssPrefsObjectType = FOUR_CHARS_TO_INT('p', 'r', 'f', 'o'), //prfo
+ qtssTextMessagesObjectType = FOUR_CHARS_TO_INT('t', 'x', 't', 'o'), //txto
+ qtssFileObjectType = FOUR_CHARS_TO_INT('f', 'i', 'l', 'e'), //file
+ qtssModuleObjectType = FOUR_CHARS_TO_INT('m', 'o', 'd', 'o'), //modo
+ qtssModulePrefsObjectType = FOUR_CHARS_TO_INT('m', 'o', 'd', 'p'), //modp
+ qtssAttrInfoObjectType = FOUR_CHARS_TO_INT('a', 't', 't', 'r'), //attr
+ qtssUserProfileObjectType = FOUR_CHARS_TO_INT('u', 's', 'p', 'o'), //uspo
+ qtssConnectedUserObjectType = FOUR_CHARS_TO_INT('c', 'u', 's', 'r'), //cusr
+ qtss3GPPStreamObjectType = FOUR_CHARS_TO_INT('3', 's', 't', 'r'), //3str
+ qtss3GPPClientSessionObjectType = FOUR_CHARS_TO_INT('3', 's', 'e', 's'), //3ses
+ qtss3GPPRTSPObjectType = FOUR_CHARS_TO_INT('3', 'r', 't', 's'), //3rts
+ qtss3GPPRequestObjectType = FOUR_CHARS_TO_INT('3', 'r', 'e', 'q') //3req
+
+};
+typedef UInt32 QTSS_ObjectType;
+
+/**********************************/
+// ERROR LOG VERBOSITIES
+//
+// This provides some information to the module on the priority or
+// type of this error message.
+//
+// When modules write to the error log stream (see below),
+// the verbosity is qtssMessageVerbosity.
+enum
+{
+ qtssFatalVerbosity = 0,
+ qtssWarningVerbosity = 1,
+ qtssMessageVerbosity = 2,
+ qtssAssertVerbosity = 3,
+ qtssDebugVerbosity = 4,
+
+ qtssIllegalVerbosity = 5
+};
+typedef UInt32 QTSS_ErrorVerbosity;
+
+enum
+{
+ qtssOpenFileNoFlags = 0,
+ qtssOpenFileAsync = 1, // File stream will be asynchronous (read may return QTSS_WouldBlock)
+ qtssOpenFileReadAhead = 2 // File stream will be used for a linear read through the file.
+};
+typedef UInt32 QTSS_OpenFileFlags;
+
+
+/**********************************/
+// SERVER STATES
+//
+// An attribute in the QTSS_ServerObject returns the server state
+// as a QTSS_ServerState. Modules may also set the server state.
+//
+// Setting the server state to qtssFatalErrorState, or qtssShuttingDownState
+// will cause the server to quit.
+//
+// Setting the state to qtssRefusingConnectionsState will cause the server
+// to start refusing new connections.
+enum
+{
+ qtssStartingUpState = 0,
+ qtssRunningState = 1,
+ qtssRefusingConnectionsState = 2,
+ qtssFatalErrorState = 3,//a fatal error has occurred, not shutting down yet
+ qtssShuttingDownState = 4,
+ qtssIdleState = 5 // Like refusing connections state, but will also kill any currently connected clients
+};
+typedef UInt32 QTSS_ServerState;
+
+/**********************************/
+// ILLEGAL ATTRIBUTE ID
+enum
+{
+ qtssIllegalAttrID = -1,
+ qtssIllegalServiceID = -1
+};
+
+//*********************************/
+// QTSS DON'T CALL SENDPACKETS AGAIN
+// If this time is specified as the next packet time when returning
+// from QTSS_SendPackets_Role, the module won't get called again in
+// that role until another QTSS_Play is issued
+enum
+{
+ qtssDontCallSendPacketsAgain = -1
+};
+
+// DATA TYPES
+enum
+{
+ qtssAttrDataTypeUnknown = 0,
+ qtssAttrDataTypeCharArray = 1,
+ qtssAttrDataTypeBool16 = 2,
+ qtssAttrDataTypeSInt16 = 3,
+ qtssAttrDataTypeUInt16 = 4,
+ qtssAttrDataTypeSInt32 = 5,
+ qtssAttrDataTypeUInt32 = 6,
+ qtssAttrDataTypeSInt64 = 7,
+ qtssAttrDataTypeUInt64 = 8,
+ qtssAttrDataTypeQTSS_Object = 9,
+ qtssAttrDataTypeQTSS_StreamRef = 10,
+ qtssAttrDataTypeFloat32 = 11,
+ qtssAttrDataTypeFloat64 = 12,
+ qtssAttrDataTypeVoidPointer = 13,
+ qtssAttrDataTypeTimeVal = 14,
+
+ qtssAttrDataTypeNumTypes = 15
+};
+typedef UInt32 QTSS_AttrDataType;
+
+enum
+{
+ qtssAttrModeRead = 1,
+ qtssAttrModeWrite = 2,
+ qtssAttrModePreempSafe = 4,
+ qtssAttrModeInstanceAttrAllowed = 8,
+ qtssAttrModeCacheable = 16,
+ qtssAttrModeDelete = 32
+};
+typedef UInt32 QTSS_AttrPermission;
+
+
+enum
+{
+ qtssAttrRightNone = 0,
+ qtssAttrRightRead = 1 << 0,
+ qtssAttrRightWrite = 1 << 1,
+ qtssAttrRightAdmin = 1 << 2,
+ qtssAttrRightExtended = 1 << 30, // Set this flag in the qtssUserRights when defining a new right. The right is a string i.e. "myauthmodule.myright" store the string in the QTSS_UserProfileObject attribute qtssUserExtendedRights
+ qtssAttrRightQTSSExtended = 1 << 31 // This flag is reserved for future use by the server. Additional rights are stored in qtssUserQTSSExtendedRights.
+};
+typedef UInt32 QTSS_AttrRights; // see QTSS_UserProfileObject
+
+
+/**********************************/
+//BUILT IN SERVER ATTRIBUTES
+
+//The server maintains many attributes internally, and makes these available to plug-ins.
+//Each value is a standard attribute, with a name and everything. Plug-ins may resolve the id's of
+//these values by name if they'd like, but in the initialize role they will receive a struct of
+//all the ids of all the internally maintained server parameters. This enumerated type block defines the indexes
+//in that array for the id's.
+
+enum
+{
+ //QTSS_RTPStreamObject parameters. All of these are preemptive safe.
+
+ qtssRTPStrTrackID = 0, //r/w //UInt32 //Unique ID identifying each stream. This will default to 0 unless set explicitly by a module.
+ qtssRTPStrSSRC = 1, //read //UInt32 //SSRC (Synchronization Source) generated by the server. Guarenteed to be unique amongst all streams in the session.
+ //This SSRC will be included in all RTCP Sender Reports generated by the server. See The RTP / RTCP RFC for more info on SSRCs.
+ qtssRTPStrPayloadName = 2, //r/w //char array //Payload name of the media on this stream. This will be empty unless set explicitly by a module.
+ qtssRTPStrPayloadType = 3, //r/w //QTSS_RTPPayloadType //Payload type of the media on this stream. This will default to qtssUnknownPayloadType unless set explicitly by a module.
+ qtssRTPStrFirstSeqNumber = 4, //r/w //SInt16 //Sequence number of the first RTP packet generated for this stream after the last PLAY request was issued. If known, this must be set by a module before calling QTSS_Play. It is used by the server to generate a proper RTSP PLAY response.
+ qtssRTPStrFirstTimestamp = 5, //r/w //SInt32 //RTP timestamp of the first RTP packet generated for this stream after the last PLAY request was issued. If known, this must be set by a module before calling QTSS_Play. It is used by the server to generate a proper RTSP PLAY response.
+ qtssRTPStrTimescale = 6, //r/w //SInt32 //Timescale for the track. If known, this must be set before calling QTSS_Play.
+ qtssRTPStrQualityLevel = 7, //r/w //UInt32 //Private
+ qtssRTPStrNumQualityLevels = 8, //r/w //UInt32 //Private
+ qtssRTPStrBufferDelayInSecs = 9, //r/w //Float32 //Size of the client's buffer. Server always sets this to 3 seconds, it is up to a module to determine what the buffer size is and set this attribute accordingly.
+
+ // All of these parameters come out of the last RTCP packet received on this stream.
+ // If the corresponding field in the last RTCP packet was blank, the attribute value will be 0.
+
+ qtssRTPStrFractionLostPackets = 10, //read //UInt32 // Fraction lost packets so far on this stream.
+ qtssRTPStrTotalLostPackets = 11, //read //UInt32 // Total lost packets so far on this stream.
+ qtssRTPStrJitter = 12, //read //UInt32 // Cumulative jitter on this stream.
+ qtssRTPStrRecvBitRate = 13, //read //UInt32 // Average bit rate received by the client in bits / sec.
+ qtssRTPStrAvgLateMilliseconds = 14, //read //UInt16 // Average msec packets received late.
+ qtssRTPStrPercentPacketsLost = 15, //read //UInt16 // Percent packets lost on this stream, as a fixed %.
+ qtssRTPStrAvgBufDelayInMsec = 16, //read //UInt16 // Average buffer delay in milliseconds
+ qtssRTPStrGettingBetter = 17, //read //UInt16 // Non-zero if the client is reporting that the stream is getting better.
+ qtssRTPStrGettingWorse = 18, //read //UInt16 // Non-zero if the client is reporting that the stream is getting worse.
+ qtssRTPStrNumEyes = 19, //read //UInt32 // Number of clients connected to this stream.
+ qtssRTPStrNumEyesActive = 20, //read //UInt32 // Number of clients playing this stream.
+ qtssRTPStrNumEyesPaused = 21, //read //UInt32 // Number of clients connected but currently paused.
+ qtssRTPStrTotPacketsRecv = 22, //read //UInt32 // Total packets received by the client
+ qtssRTPStrTotPacketsDropped = 23, //read //UInt16 // Total packets dropped by the client.
+ qtssRTPStrTotPacketsLost = 24, //read //UInt16 // Total packets lost.
+ qtssRTPStrClientBufFill = 25, //read //UInt16 // How much the client buffer is filled in 10ths of a second.
+ qtssRTPStrFrameRate = 26, //read //UInt16 // Current frame rate, in frames per second.
+ qtssRTPStrExpFrameRate = 27, //read //UInt16 // Expected frame rate, in frames per second.
+ qtssRTPStrAudioDryCount = 28, //read //UInt16 // Number of times the audio has run dry.
+ // Address & network related parameters
+ qtssRTPStrIsTCP = 29, //read //Bool16 //Is this RTP stream being sent over TCP? If false, it is being sent over UDP.
+ qtssRTPStrStreamRef = 30, //read //QTSS_StreamRef //A QTSS_StreamRef used for sending RTP or RTCP packets to the client. Use the QTSS_WriteFlags to specify whether each packet is an RTP or RTCP packet.
+ qtssRTPStrTransportType = 31, //read //QTSS_RTPTransportType // What kind of transport is being used?
+ qtssRTPStrStalePacketsDropped = 32, //read //UInt32 // Number of packets dropped by QTSS_Write because they were too old.
+ qtssRTPStrCurrentAckTimeout = 33, //read //UInt32 // Current ack timeout being advertised to the client in msec (part of reliable udp).
+
+ qtssRTPStrCurPacketsLostInRTCPInterval = 34, // read //UInt32 // An RTCP delta count of lost packets equal to qtssRTPStrPercentPacketsLost
+ qtssRTPStrPacketCountInRTCPInterval = 35, // read //UInt32 // An RTCP delta count of packets
+ qtssRTPStrSvrRTPPort = 36, //read //UInt16 // Port the server is sending RTP packets from for this stream
+ qtssRTPStrClientRTPPort = 37, //read //UInt16 // Port the server is sending RTP packets to for this stream
+ qtssRTPStrNetworkMode = 38, //read //QTSS_RTPNetworkMode // unicast or multicast
+ qtssRTPStr3gppObject = 39, //read //QTSS_3GPPStreamObject // QTSS_ObjectType qtss3GPPStreamObjectType 3gpp data for the stream object
+ qtssRTPStrThinningDisabled = 40, //read //Bool16 //Stream thinning is disabled on this stream.
+ qtssRTPStrNumParams = 41
+
+};
+typedef UInt32 QTSS_RTPStreamAttributes;
+
+enum
+{
+ //All text names are identical to the enumerated type names
+ qtss3GPPStreamEnabled = 0,
+ qtss3GPPStreamRateAdaptBufferBytes = 1,
+ qtss3GPPStreamRateAdaptTimeMilli = 2,
+ qtss3GPPStreamNumParams = 3
+};
+typedef UInt32 QTSS_RTPStream3GPPAttributes; //QTSS_3GPPStreamObject
+
+
+enum
+{
+ //QTSS_ClientSessionObject parameters. All of these are preemptive safe
+
+ qtssCliSesStreamObjects = 0, //read //QTSS_RTPStreamObject//Iterated attribute. All the QTSS_RTPStreamRefs belonging to this session.
+ qtssCliSesCreateTimeInMsec = 1, //read //QTSS_TimeVal //Time in milliseconds the session was created.
+ qtssCliSesFirstPlayTimeInMsec = 2, //read //QTSS_TimeVal //Time in milliseconds the first QTSS_Play call was issued.
+ qtssCliSesPlayTimeInMsec = 3, //read //QTSS_TimeVal //Time in milliseconds the most recent play was issued.
+ qtssCliSesAdjustedPlayTimeInMsec= 4, //read //QTSS_TimeVal //Private - do not use
+ qtssCliSesRTPBytesSent = 5, //read //UInt32 //Number of RTP bytes sent so far on this session.
+ qtssCliSesRTPPacketsSent = 6, //read //UInt32 //Number of RTP packets sent so far on this session.
+ qtssCliSesState = 7, //read //QTSS_RTPSessionState // State of this session: is it paused or playing currently?
+ qtssCliSesPresentationURL = 8, //read //char array //Presentation URL for this session. This URL is the "base" URL for the session. RTSP requests to this URL are assumed to affect all streams on the session.
+ qtssCliSesFirstUserAgent = 9, //read //char array //Private
+ qtssCliSesMovieDurationInSecs = 10, //r/w //Float64 //Duration of the movie for this session in seconds. This will default to 0 unless set by a module.
+ qtssCliSesMovieSizeInBytes = 11, //r/w //UInt64 //Movie size in bytes. This will default to 0 unless explictly set by a module
+ qtssCliSesMovieAverageBitRate = 12, //r/w //UInt32 //average bits per second based on total RTP bits/movie duration. This will default to 0 unless explictly set by a module.
+ qtssCliSesLastRTSPSession = 13, //read //QTSS_RTSPSessionObject //Private
+ qtssCliSesFullURL = 14, //read //char array //full Presentation URL for this session. Same as qtssCliSesPresentationURL, but includes rtsp://domain.com prefix
+ qtssCliSesHostName = 15, //read //char array //requestes host name for s session. Just the "domain.com" portion from qtssCliSesFullURL above
+
+ qtssCliRTSPSessRemoteAddrStr = 16, //read //char array //IP address addr of client, in dotted-decimal format.
+ qtssCliRTSPSessLocalDNS = 17, //read //char array //DNS name of local IP address for this RTSP connection.
+ qtssCliRTSPSessLocalAddrStr = 18, //read //char array //Ditto, in dotted-decimal format.
+
+ qtssCliRTSPSesUserName = 19, //read //char array // from the most recent (last) request.
+ qtssCliRTSPSesUserPassword = 20, //read //char array // from the most recent (last) request.
+ qtssCliRTSPSesURLRealm = 21, //read //char array // from the most recent (last) request.
+
+ qtssCliRTSPReqRealStatusCode = 22, //read //UInt32 //Same as qtssRTSPReqRTSPReqRealStatusCode, the status from the most recent (last) request.
+ qtssCliTeardownReason = 23, //r/w //QTSS_CliSesTeardownReason // Must be set by a module that calls QTSS_Teardown if it is not a client requested disconnect.
+
+ qtssCliSesReqQueryString = 24, //read //char array //Query string from the request that creates this client session
+
+ qtssCliRTSPReqRespMsg = 25, //read //char array // from the most recent (last) request. Error message sent back to client if response was an error.
+
+ qtssCliSesCurrentBitRate = 26, //read //UInt32 //Current bit rate of all the streams on this session. This is not an average. In bits per second.
+ qtssCliSesPacketLossPercent = 27, //read //Float32 //Current percent loss as a fraction. .5 = 50%. This is not an average.
+ qtssCliSesTimeConnectedInMsec = 28, //read //SInt64 //Time in milliseconds that this client has been connected.
+ qtssCliSesCounterID = 29, //read //UInt32 //A unique, non-repeating ID for this session.
+ qtssCliSesRTSPSessionID = 30, //read //char array//The RTSP session ID that refers to this client session
+ qtssCliSesFramesSkipped = 31, //r/w //UInt32 //Modules can set this to be the number of frames skipped for this client
+ qtssCliSesTimeoutMsec = 32, //r/w //UInt32 // client session timeout in milliseconds refreshed by RefreshTimeout API call or any rtcp or rtp packet on the session.
+ qtssCliSesOverBufferEnabled = 33, //read //Bool16 // client overbuffers using dynamic rate streams
+ qtssCliSesRTCPPacketsRecv = 34, //read //UInt32 //Number of RTCP packets received so far on this session.
+ qtssCliSesRTCPBytesRecv = 35, //read //UInt32 //Number of RTCP bytes received so far on this session.
+ qtssCliSesStartedThinning = 36, //read //Bool16 // At least one of the streams in the session is thinned
+ qtssCliSes3GPPObject = 37, //read //QTSS_3GPPClientSessionObject //QTSS_ObjectType qtss3GPPClientSessionObjectType
+ qtssCliSessLastRTSPBandwidth = 38, //read //UInt32 // The last RTSP Bandwidth header value received from the client.
+ qtssCliSessIs3GPPSession = 39, //read //Bool16 // Client is using 3gpp RTSP headers
+ qtssCliSesNumParams = 40
+
+};
+typedef UInt32 QTSS_ClientSessionAttributes;
+
+//QTSS_3GPPClientSessionObject //class RTPSession3GPP
+enum
+{
+ //All text names are identical to the enumerated type names
+ qtss3GPPCliSesEnabled = 0, //read //Bool16 //initialized to preference setting
+ qtss3GPPCliSesLinkCharGuaranteedBitRate = 1, //read //UInt32 //The Received Link Characteristic rate. default = 0
+ qtss3GPPCliSesLinkCharMaxBitRate = 2, //read //UInt32 //The Received Link Characteristic max. default = 0
+ qtss3GPPCliSesLinkCharMaxTransferDelayMilliSec = 3, //read //UInt32 //The Received Link Characteristic transfer delay. default = 0
+ qtss3GPPCliSesLinkCharURL = 4, //read //char array //The Received Link Characteristic URL.
+
+ qtss3GPPCliSesNumParams = 5
+};
+typedef UInt32 QTSS_ClientSession3GPPAttributes;
+
+enum
+{
+ //QTSS_RTSPSessionObject parameters
+
+ //Valid in any role that receives a QTSS_RTSPSessionObject
+ qtssRTSPSesID = 0, //read //UInt32 //This is a unique ID for each session since the server started up.
+ qtssRTSPSesLocalAddr = 1, //read //UInt32 //Local IP address for this RTSP connection
+ qtssRTSPSesLocalAddrStr = 2, //read //char array //Ditto, in dotted-decimal format.
+ qtssRTSPSesLocalDNS = 3, //read //char array //DNS name of local IP address for this RTSP connection.
+ qtssRTSPSesRemoteAddr = 4, //read //UInt32 //IP address of client.
+ qtssRTSPSesRemoteAddrStr= 5, //read //char array //IP address addr of client, in dotted-decimal format.
+ qtssRTSPSesEventCntxt = 6, //read //QTSS_EventContextRef //An event context for the RTSP connection to the client. This should primarily be used to wait for EV_WR events if flow-controlled when responding to a client.
+ qtssRTSPSesType = 7, //read //QTSS_RTSPSession //Is this a normal RTSP session, or is it a HTTP tunnelled RTSP session?
+ qtssRTSPSesStreamRef = 8, //read //QTSS_RTSPSessionStream // A QTSS_StreamRef used for sending data to the RTSP client.
+
+ qtssRTSPSesLastUserName = 9,//read //char array // Private
+ qtssRTSPSesLastUserPassword = 10,//read //char array // Private
+ qtssRTSPSesLastURLRealm = 11,//read //char array // Private
+
+ qtssRTSPSesLocalPort = 12, //read //UInt16 // This is the local port for the connection
+ qtssRTSPSesRemotePort = 13, //read //UInt16 // This is the client port for the connection
+
+ qtssRTSPSes3GPPObject = 14, //read //QTSS_3GPPRTSPSessionObject //QTSS_ObjectType qtss3GPPRTSPObjectType 3gpp data and state info
+
+ qtssRTSPSesLastDigestChallenge = 15,//read //char array // Private
+ qtssRTSPSesNumParams = 16
+};
+typedef UInt32 QTSS_RTSPSessionAttributes;
+
+//QTSS_3GPPRTSPSessionObject //class RTSPSession3GPP
+enum
+{
+ //All text names are identical to the enumerated type names
+ qtss3GPPRTSPSesEnabled = 0,
+ qtss3GPPRTSPSessNumParams = 1
+};
+typedef UInt32 QTSS_3GPPRTSPSessionAttributes;
+
+
+enum
+{
+ //All text names are identical to the enumerated type names
+
+ //QTSS_RTSPRequestObject parameters. All of these are pre-emptive safe parameters
+
+ //Available in every role that receives the QTSS_RTSPRequestObject
+
+ qtssRTSPReqFullRequest = 0, //read //char array //The full request sent by the client
+
+ //Available in every method that receives the QTSS_RTSPRequestObject except for the QTSS_FilterMethod
+
+ qtssRTSPReqMethodStr = 1, //read //char array //RTSP Method of this request.
+ qtssRTSPReqFilePath = 2, //r/w //char array //Not pre-emptive safe!! //URI for this request, converted to a local file system path.
+ qtssRTSPReqURI = 3, //read //char array //URI for this request
+ qtssRTSPReqFilePathTrunc = 4, //read //char array //Not pre-emptive safe!! //Same as qtssRTSPReqFilePath, without the last element of the path
+ qtssRTSPReqFileName = 5, //read //char array //Not pre-emptive safe!! //Everything after the last path separator in the file system path
+ qtssRTSPReqFileDigit = 6, //read //char array //Not pre-emptive safe!! //If the URI ends with one or more digits, this points to those.
+ qtssRTSPReqAbsoluteURL = 7, //read //char array //The full URL, starting from "rtsp://"
+ qtssRTSPReqTruncAbsoluteURL = 8, //read //char array //Absolute URL without last element of path
+ qtssRTSPReqMethod = 9, //read //QTSS_RTSPMethod //Method as QTSS_RTSPMethod
+ qtssRTSPReqStatusCode = 10, //r/w //QTSS_RTSPStatusCode //The current status code for the request as QTSS_RTSPStatusCode. By default, it is always qtssSuccessOK. If a module sets this attribute, and calls QTSS_SendRTSPHeaders, the status code of the header generated by the server will reflect this value.
+ qtssRTSPReqStartTime = 11, //read //Float64 //Start time specified in Range: header of PLAY request.
+ qtssRTSPReqStopTime = 12, //read //Float64 //Stop time specified in Range: header of PLAY request.
+ qtssRTSPReqRespKeepAlive = 13, //r/w //Bool16 //Will (should) the server keep the connection alive. Set this to false if the connection should be terminated after completion of this request.
+ qtssRTSPReqRootDir = 14, //r/w //char array //Not pre-emptive safe!! //Root directory to use for this request. The default value for this parameter is the server's media folder path. Modules may set this attribute from the QTSS_RTSPRoute_Role.
+ qtssRTSPReqRealStatusCode = 15, //read //UInt32 //Same as qtssRTSPReqStatusCode, but translated from QTSS_RTSPStatusCode into an actual RTSP status code.
+ qtssRTSPReqStreamRef = 16, //read //QTSS_RTSPRequestStream //A QTSS_StreamRef for sending data to the RTSP client. This stream ref, unlike the one provided as an attribute in the QTSS_RTSPSessionObject, will never return EWOULDBLOCK in response to a QTSS_Write or a QTSS_WriteV call.
+
+ qtssRTSPReqUserName = 17, //read //char array//decoded Authentication information when provided by the RTSP request. See RTSPSessLastUserName.
+ qtssRTSPReqUserPassword = 18, //read //char array //decoded Authentication information when provided by the RTSP request. See RTSPSessLastUserPassword.
+ qtssRTSPReqUserAllowed = 19, //r/w //Bool16 //Default is server pref based, set to false if request is denied. Missing or bad movie files should allow the server to handle the situation and return true.
+ qtssRTSPReqURLRealm = 20, //r/w //char array //The authorization entity for the client to display "Please enter password for -realm- at server name. The default realm is "Streaming Server".
+ qtssRTSPReqLocalPath = 21, //read //char array //Not pre-emptive safe!! //The full local path to the file. This Attribute is first set after the Routing Role has run and before any other role is called.
+
+ qtssRTSPReqIfModSinceDate = 22, //read //QTSS_TimeVal // If the RTSP request contains an If-Modified-Since header, this is the if-modified date, converted to a QTSS_TimeVal
+
+
+ qtssRTSPReqQueryString = 23, //read //char array // query stting (CGI parameters) passed to the server in the request URL, does not include the '?' separator
+
+ qtssRTSPReqRespMsg = 24, //r/w //char array // A module sending an RTSP error to the client should set this to be a text message describing why the error occurred. This description is useful to add to log files. Once the RTSP response has been sent, this attribute contains the response message.
+ qtssRTSPReqContentLen = 25, //read //UInt32 // Content length of incoming RTSP request body
+ qtssRTSPReqSpeed = 26, //read //Float32 // Value of Speed header, converted to a Float32.
+ qtssRTSPReqLateTolerance = 27, //read //Float32 // Value of the late-tolerance field of the x-RTP-Options header, or -1 if not present.
+
+ qtssRTSPReqTransportType = 28, //read //QTSS_RTPTransportType // What kind of transport?
+ qtssRTSPReqTransportMode = 29, //read //QTSS_RTPTransportMode // A setup request from the client. * maybe should just be an enum or the text of the mode value?
+ qtssRTSPReqSetUpServerPort = 30, //r/w //UInt16 // the ServerPort to respond to a client SETUP request with.
+
+ qtssRTSPReqAction = 31, //r/w //QTSS_ActionFlags //Set by a module in the QTSS_RTSPSetAction_Role - for now, the server will set it as the role hasn't been added yet
+ qtssRTSPReqUserProfile = 32, //r/w //QTSS_UserProfileObject //Object's username is filled in by the server and its password and group memberships filled in by the authentication module.
+ qtssRTSPReqPrebufferMaxTime = 33, //read //Float32 //The maxtime field of the x-Prebuffer RTSP header
+ qtssRTSPReqAuthScheme = 34, //read //QTSS_AuthScheme
+
+ qtssRTSPReqSkipAuthorization = 35, //r/w //Bool16 // Set by a module that wants the particular request to be
+ // allowed by all authorization modules
+ qtssRTSPReqNetworkMode = 36, //read //QTSS_RTPNetworkMode // unicast or multicast
+ qtssRTSPReqDynamicRateState = 37, //read //SInt32 // -1 not in request, 0 off, 1 on
+ qtssRTSPReq3GPPRequestObject = 38, //read //QTSS_3GPPRequestObject //QTSS_ObjectType qtss3GPPRequestObject
+ qtssRTSPReqBandwidthBits = 39, //read //UInt32 // Value of the Bandwdith header. Default is 0.
+ qtssRTSPReqUserFound = 40, //r/w //Bool16 //Default is false, set to true if the user is found in the authenticate role and the module wants to take ownership of authenticating the user.
+ qtssRTSPReqAuthHandled = 41, //r/w //Bool16 //Default is false, set to true in the authorize role to take ownerhsip of authorizing the request.
+ qtssRTSPReqDigestChallenge = 42, //read //char array //Challenge used by the server for Digest authentication
+ qtssRTSPReqDigestResponse = 43, //read //char array //Digest response used by the server for Digest authentication
+ qtssRTSPReqNumParams = 44
+
+};
+typedef UInt32 QTSS_RTSPRequestAttributes;
+
+enum
+{
+ //All text names are identical to the enumerated type names
+ qtss3GPPRequestEnabled = 0, //r/w //Bool16
+ qtss3GPPRequestRateAdaptationStreamData = 1, //read //char array //the rate adaptation url and parameters per stream
+ qtss3GPPRequestNumParams = 2
+};
+typedef UInt32 QTSS_RTSPRequest3GPPAttributes;
+
+
+enum
+{
+ //QTSS_ServerObject parameters
+
+ // These parameters ARE pre-emptive safe.
+
+ qtssServerAPIVersion = 0, //read //UInt32 //The API version supported by this server (format 0xMMMMmmmm, where M=major version, m=minor version)
+ qtssSvrDefaultDNSName = 1, //read //char array //The "default" DNS name of the server
+ qtssSvrDefaultIPAddr = 2, //read //UInt32 //The "default" IP address of the server
+ qtssSvrServerName = 3, //read //char array //Name of the server
+ qtssSvrServerVersion = 4, //read //char array //Version of the server
+ qtssSvrServerBuildDate = 5, //read //char array //When was the server built?
+ qtssSvrRTSPPorts = 6, //read // NOT PREEMPTIVE SAFE!//UInt16 //Indexed parameter: all the ports the server is listening on
+ qtssSvrRTSPServerHeader = 7, //read //char array //Server: header that the server uses to respond to RTSP clients
+
+ // These parameters are NOT pre-emptive safe, they cannot be accessed
+ // via. QTSS_GetValuePtr. Some exceptions noted below
+
+ qtssSvrState = 8, //r/w //QTSS_ServerState //The current state of the server. If a module sets the server state, the server will respond in the appropriate fashion. Setting to qtssRefusingConnectionsState causes the server to refuse connections, setting to qtssFatalErrorState or qtssShuttingDownState causes the server to quit.
+ qtssSvrIsOutOfDescriptors = 9, //read //Bool16 //true if the server has run out of file descriptors, false otherwise
+ qtssRTSPCurrentSessionCount = 10, //read //UInt32 //Current number of connected clients over standard RTSP
+ qtssRTSPHTTPCurrentSessionCount = 11, //read //UInt32 //Current number of connected clients over RTSP / HTTP
+
+ qtssRTPSvrNumUDPSockets = 12, //read //UInt32 //Number of UDP sockets currently being used by the server
+ qtssRTPSvrCurConn = 13, //read //UInt32 //Number of clients currently connected to the server
+ qtssRTPSvrTotalConn = 14, //read //UInt32 //Total number of clients since startup
+ qtssRTPSvrCurBandwidth = 15, //read //UInt32 //Current bandwidth being output by the server in bits per second
+ qtssRTPSvrTotalBytes = 16, //read //UInt64 //Total number of bytes served since startup
+ qtssRTPSvrAvgBandwidth = 17, //read //UInt32 //Average bandwidth being output by the server in bits per second
+ qtssRTPSvrCurPackets = 18, //read //UInt32 //Current packets per second being output by the server
+ qtssRTPSvrTotalPackets = 19, //read //UInt64 //Total number of bytes served since startup
+
+ qtssSvrHandledMethods = 20, //r/w //QTSS_RTSPMethod //The methods that the server supports. Modules should append the methods they support to this attribute in their QTSS_Initialize_Role.
+ qtssSvrModuleObjects = 21, //read // this IS PREMPTIVE SAFE! //QTSS_ModuleObject // A module object representing each module
+ qtssSvrStartupTime = 22, //read //QTSS_TimeVal //Time the server started up
+ qtssSvrGMTOffsetInHrs = 23, //read //SInt32 //Server time zone (offset from GMT in hours)
+ qtssSvrDefaultIPAddrStr = 24, //read //char array //The "default" IP address of the server as a string
+
+ qtssSvrPreferences = 25, //read //QTSS_PrefsObject // An object representing each the server's preferences
+ qtssSvrMessages = 26, //read //QTSS_Object // An object containing the server's error messages.
+ qtssSvrClientSessions = 27, //read //QTSS_Object // An object containing all client sessions stored as indexed QTSS_ClientSessionObject(s).
+ qtssSvrCurrentTimeMilliseconds = 28, //read //QTSS_TimeVal //Server's current time in milliseconds. Retrieving this attribute is equivalent to calling QTSS_Milliseconds
+ qtssSvrCPULoadPercent = 29, //read //Float32 //Current % CPU being used by the server
+
+ qtssSvrNumReliableUDPBuffers = 30, //read //UInt32 //Number of buffers currently allocated for UDP retransmits
+ qtssSvrReliableUDPWastageInBytes= 31, //read //UInt32 //Amount of data in the reliable UDP buffers being wasted
+ qtssSvrConnectedUsers = 32, //r/w //QTSS_Object //List of connected user sessions (updated by modules for their sessions)
+
+ qtssMP3SvrCurConn = 33, //r/w //UInt32 //Number of MP3 client sessions connected
+ qtssMP3SvrTotalConn = 34, //r/w //UInt32 //Total number of MP3 clients since startup
+ qtssMP3SvrCurBandwidth = 35, //r/w //UInt32 //Current MP3 bandwidth being output by the server in bits per second
+ qtssMP3SvrTotalBytes = 36, //r/w //UInt64 //Total number of MP3 bytes served since startup
+ qtssMP3SvrAvgBandwidth = 37, //r/w //UInt32 //Average MP3 bandwidth being output by the server in bits per second
+
+ qtssSvrServerBuild = 38, //read //char array //build of the server
+ qtssSvrServerPlatform = 39, //read //char array //Platform (OS) of the server
+ qtssSvrRTSPServerComment = 40, //read //char array //RTSP comment for the server header
+ qtssSvrNumThinned = 41, //read //SInt32 //Number of thinned sessions
+ qtssSvrNumThreads = 42, //read //UInt32 //Number of task threads // see also qtssPrefsRunNumThreads
+ qtssSvrNumParams = 43
+};
+typedef UInt32 QTSS_ServerAttributes;
+
+enum
+{
+ //QTSS_PrefsObject parameters
+
+ // Valid in all methods. None of these are pre-emptive safe, so the version
+ // of QTSS_GetAttribute that copies data must be used.
+
+ // All of these parameters are read-write.
+
+ qtssPrefsRTSPTimeout = 0, //"rtsp_timeout" //UInt32 //RTSP timeout in seconds sent to the client.
+ qtssPrefsRealRTSPTimeout = 1, //"real_rtsp_timeout" //UInt32 //Amount of time in seconds the server will wait before disconnecting idle RTSP clients. 0 means no timeout
+ qtssPrefsRTPTimeout = 2, //"rtp_timeout" //UInt32 //Amount of time in seconds the server will wait before disconnecting idle RTP clients. 0 means no timeout
+ qtssPrefsMaximumConnections = 3, //"maximum_connections" //SInt32 //Maximum # of concurrent RTP connections allowed by the server. -1 means unlimited.
+ qtssPrefsMaximumBandwidth = 4, //"maximum_bandwidth" //SInt32 //Maximum amt of bandwidth the server is allowed to serve in K bits. -1 means unlimited.
+ qtssPrefsMovieFolder = 5, //"movie_folder" //char array //Path to the root movie folder
+ qtssPrefsRTSPIPAddr = 6, //"bind_ip_addr" //char array //IP address the server should accept RTSP connections on. 0.0.0.0 means all addresses on the machine.
+ qtssPrefsBreakOnAssert = 7, //"break_on_assert" //Bool16 //If true, the server will break in the debugger when an assert fails.
+ qtssPrefsAutoRestart = 8, //"auto_restart" //Bool16 //If true, the server will automatically restart itself if it crashes.
+ qtssPrefsTotalBytesUpdate = 9, //"total_bytes_update" //UInt32 //Interval in seconds between updates of the server's total bytes and current bandwidth statistics
+ qtssPrefsAvgBandwidthUpdate = 10, //"average_bandwidth_update" //UInt32 //Interval in seconds between computations of the server's average bandwidth
+ qtssPrefsSafePlayDuration = 11, //"safe_play_duration" //UInt32 //Hard to explain... see streamingserver.conf
+ qtssPrefsModuleFolder = 12, //"module_folder" //char array //Path to the module folder
+
+ // There is a compiled-in error log module that loads before all the other modules
+ // (so it can log errors from the get-go). It uses these prefs.
+
+ qtssPrefsErrorLogName = 13, //"error_logfile_name" //char array //Name of error log file
+ qtssPrefsErrorLogDir = 14, //"error_logfile_dir" //char array //Path to error log file directory
+ qtssPrefsErrorRollInterval = 15, //"error_logfile_interval" //UInt32 //Interval in days between error logfile rolls
+ qtssPrefsMaxErrorLogSize = 16, //"error_logfile_size" //UInt32 //Max size in bytes of the error log
+ qtssPrefsErrorLogVerbosity = 17, //"error_logfile_verbosity" //UInt32 //Max verbosity level of messages the error logger will log
+ qtssPrefsScreenLogging = 18, //"screen_logging" //Bool16 //Should the error logger echo messages to the screen?
+ qtssPrefsErrorLogEnabled = 19, //"error_logging" //Bool16 //Is error logging enabled?
+
+ qtssPrefsDropVideoAllPacketsDelayInMsec = 20, //"drop_all_video_delay" //SInt32 // Don't send video packets later than this
+ qtssPrefsStartThinningDelayInMsec = 21, //"start_thinning_delay" //SInt32 // lateness at which we might start thinning
+ qtssPrefsLargeWindowSizeInK = 22, //"large_window_size" // UInt32 //default size that will be used for high bitrate movies
+ qtssPrefsWindowSizeThreshold = 23, //"window_size_threshold" // UInt32 //bitrate at which we switch to larger window size
+
+ qtssPrefsMinTCPBufferSizeInBytes = 24, //"min_tcp_buffer_size" //UInt32 // When streaming over TCP, this is the minimum size the TCP socket send buffer can be set to
+ qtssPrefsMaxTCPBufferSizeInBytes = 25, //"max_tcp_buffer_size" //UInt32 // When streaming over TCP, this is the maximum size the TCP socket send buffer can be set to
+ qtssPrefsTCPSecondsToBuffer = 26, //"tcp_seconds_to_buffer" //Float32 // When streaming over TCP, the size of the TCP send buffer is scaled based on the bitrate of the movie. It will fit all the data that gets sent in this amount of time.
+
+ qtssPrefsDoReportHTTPConnectionAddress = 27, //"do_report_http_connection_ip_address" //Bool16 // when behind a round robin DNS, the client needs to be told the specific ip address of the maching handling its request. this pref tells the server to repot its IP address in the reply to the HTTP GET request when tunneling RTSP through HTTP
+
+ qtssPrefsDefaultAuthorizationRealm = 28, // "default_authorization_realm" //char array //
+
+ qtssPrefsRunUserName = 29, //"run_user_name" //char array //Run under this user's account
+ qtssPrefsRunGroupName = 30, //"run_group_name" //char array //Run under this group's account
+
+ qtssPrefsSrcAddrInTransport = 31, //"append_source_addr_in_transport" // Bool16 //If true, the server will append the src address to the Transport header responses
+ qtssPrefsRTSPPorts = 32, //"rtsp_ports" // UInt16
+
+ qtssPrefsMaxRetransDelayInMsec = 33, //"max_retransmit_delay" // UInt32 //maximum interval between when a retransmit is supposed to be sent and when it actually gets sent. Lower values means smoother flow but slower server performance
+ qtssPrefsSmallWindowSizeInK = 34, //"small_window_size" // UInt32 //default size that will be used for low bitrate movies
+ qtssPrefsAckLoggingEnabled = 35, //"ack_logging_enabled" // Bool16 //Debugging only: turns on detailed logging of UDP acks / retransmits
+ qtssPrefsRTCPPollIntervalInMsec = 36, //"rtcp_poll_interval" // UInt32 //interval (in Msec) between poll for RTCP packets
+ qtssPrefsRTCPSockRcvBufSizeInK = 37, //"rtcp_rcv_buf_size" // UInt32 //Size of the receive socket buffer for udp sockets used to receive rtcp packets
+ qtssPrefsSendInterval = 38, //"send_interval" // UInt32 //
+ qtssPrefsThickAllTheWayDelayInMsec = 39, //"thick_all_the_way_delay" // UInt32 //
+ qtssPrefsAltTransportIPAddr = 40, //"alt_transport_src_ipaddr"// char //If empty, the server uses its own IP addr in the source= param of the transport header. Otherwise, it uses this addr.
+ qtssPrefsMaxAdvanceSendTimeInSec = 41, //"max_send_ahead_time" // UInt32 //This is the farthest in advance the server will send a packet to a client that supports overbuffering.
+ qtssPrefsReliableUDPSlowStart = 42, //"reliable_udp_slow_start" // Bool16 //Is reliable UDP slow start enabled?
+ qtssPrefsAutoDeleteSDPFiles = 43, //"auto_delete_sdp_files" // Bool16 //SDP files in the Movies directory tree are deleted after a Broadcaster's RTSP controlled SDP session ends.
+ qtssPrefsAuthenticationScheme = 44, //"authentication_scheme" // char //Set this to be the authentication scheme you want the server to use. "basic", "digest", and "none" are the currently supported values
+ qtssPrefsDeleteSDPFilesInterval = 45, //"sdp_file_delete_interval_seconds" //UInt32 //Feature rem
+ qtssPrefsAutoStart = 46, //"auto_start" //Bool16 //If true, streaming server likes to be started at system startup
+ qtssPrefsReliableUDP = 47, //"reliable_udp" //Bool16 //If true, uses reliable udp transport if requested by the client
+ qtssPrefsReliableUDPDirs = 48, //"reliable_udp_dirs" //CharArray
+ qtssPrefsReliableUDPPrintfs = 49, //"reliable_udp_printfs" //Bool16 //If enabled, server prints out interesting statistics for the reliable UDP clients
+
+ qtssPrefsDropAllPacketsDelayInMsec = 50, //"drop_all_packets_delay" // SInt32 // don't send any packets later than this
+ qtssPrefsThinAllTheWayDelayInMsec = 51, //"thin_all_the_way_delay" // SInt32 // thin to key frames
+ qtssPrefsAlwaysThinDelayInMsec = 52, //"always_thin_delay" // SInt32 // we always start to thin at this point
+ qtssPrefsStartThickingDelayInMsec = 53, //"start_thicking_delay" // SInt32 // maybe start thicking at this point
+ qtssPrefsQualityCheckIntervalInMsec = 54, //"quality_check_interval" // UInt32 // adjust thinnning params this often
+ qtssPrefsEnableRTSPErrorMessage = 55, //"RTSP_error_message" //Bool16 // Appends a content body string error message for reported RTSP errors.
+ qtssPrefsEnableRTSPDebugPrintfs = 56, //"RTSP_debug_printfs" //Boo1l6 // printfs incoming RTSPRequests and Outgoing RTSP responses.
+
+ qtssPrefsEnableMonitorStatsFile = 57, //"enable_monitor_stats_file" //Bool16 //write server stats to the monitor file
+ qtssPrefsMonitorStatsFileIntervalSec = 58, //"monitor_stats_file_interval_seconds" // private
+ qtssPrefsMonitorStatsFileName = 59, //"monitor_stats_file_name" // private
+
+ qtssPrefsEnablePacketHeaderPrintfs = 60, // "enable_packet_header_printfs" //Bool16 // RTP and RTCP printfs of outgoing packets.
+ qtssPrefsPacketHeaderPrintfOptions = 61, // "packet_header_printf_options" //char //set of printfs to print. Form is [text option] [;] default is "rtp;rr;sr;". This means rtp packets, rtcp sender reports, and rtcp receiver reports.
+ qtssPrefsOverbufferRate = 62, // "overbuffer_rate" //Float32
+ qtssPrefsMediumWindowSizeInK = 63, // "medium_window_size" // UInt32 //default size that will be used for medium bitrate movies
+ qtssPrefsWindowSizeMaxThreshold = 64, //"window_size_threshold" // UInt32 //bitrate at which we switch from medium to large window size
+ qtssPrefsEnableRTSPServerInfo = 65, //"RTSP_server_info" //Boo1l6 // Adds server info to the RTSP responses.
+ qtssPrefsRunNumThreads = 66, //"run_num_threads" //UInt32 // if value is non-zero, will create that many task threads; otherwise a thread will be created for each processor
+ qtssPrefsPidFile = 67, //"pid_file" //Char Array //path to pid file
+ qtssPrefsCloseLogsOnWrite = 68, // "force_logs_close_on_write" //Bool16 // force log files to close after each write.
+ qtssPrefsDisableThinning = 69, // "disable_thinning" //Bool16 // Usually used for performance testing. Turn off stream thinning from packet loss or stream lateness.
+ qtssPrefsPlayersReqRTPHeader = 70, // "player_requires_rtp_header_info" //Char array //name of player to match against the player's user agent header
+ qtssPrefsPlayersReqBandAdjust = 71, // "player_requires_bandwidth_adjustment //Char array //name of player to match against the player's user agent header
+ qtssPrefsPlayersReqNoPauseTimeAdjust = 72, // "player_requires_no_pause_time_adjustment //Char array //name of player to match against the player's user agent header
+ qtssPrefsEnable3gppProtocol = 73, // "enable_3gpp_protocol //Bool16 //enable or disable 3gpp release 6 protocol support featues
+ qtssPrefsEnable3gppProtocolRateAdapt = 74, // "enable_3gpp_protocol_rate_adaptation //Bool16 //enable or disable 3gpp release 6 rate adaptation featues
+ qtssPrefs3gppRateAdaptReportFrequency = 75, // "3gpp_protocol_rate_adaptation_report_frequency //UInt16 //requested rate adaptation rtcp report frequency
+ qtssPrefsDefaultStreamQuality = 76, // "default_stream_quality //UInt16 //0 is all day and best quality. Higher values are worse maximum depends on the media and the media module
+ qtssPrefsPlayersReqRTPStartTimeAdjust = 77, // "player_requires_rtp_start_time_adjust" //Char Array //name of players to match against the player's user agent header
+ qtssPrefsEnable3gppDebugPrintfs = 78, // "enable_3gpp_debug_printfs" //Boo1l6 // 3gpp rate adaptation state and debugging printfs.
+ qtssPrefsEnableUDPMonitor = 79, // "enable_udp_monitor_stream" //Boo1l6 // reflect all udp streams to the monitor ports, use an sdp to view
+ qtssPrefsUDPMonitorAudioPort = 80, // "udp_monitor_video_port" //UInt16 // localhost destination port of reflected stream
+ qtssPrefsUDPMonitorVideoPort = 81, // "udp_monitor_audio_port" //UInt16 // localhost destination port of reflected stream
+ qtssPrefsUDPMonitorDestIPAddr = 82, // "udp_monitor_dest_ip" //char array //IP address the server should send RTP monitor reflected streams.
+ qtssPrefsUDPMonitorSourceIPAddr = 83, // "udp_monitor_src_ip" //char array //client IP address the server monitor should reflect. *.*.*.* means all client addresses.
+ qtssPrefsEnableAllowGuestDefault = 84, // "enable_allow_guest_authorize_default" //Boo1l6 // server hint to access modules to allow guest access as the default (can be overriden in a qtaccess file or other means)
+ qtssPrefsNumRTSPThreads = 85, // "run_num_rtsp_threads" //UInt32 // if value is non-zero, the server will create that many task threads; otherwise a single thread will be created.
+ qtssPrefsPlayersReqDisable3gppRateAdapt = 86, // "player_requires_disable_3gpp_rate_adapt" //Char array //name of players to match against the player's user agent header
+ qtssPrefsPlayersReq3GPPTargetTime = 87, // "player_requires_3gpp_target_time" //Char array //name of player to set the target time for
+ qtssPrefs3GPPTargetTime = 88, // "3gpp_target_time_milliseconds" //UInt32 // milliseconds set as the target time.
+ qtssPrefsPlayersReqDisableThinning = 89, // "player_requires_disable_thinning" //Char array //name of player to set the target time for
+
+ qtssPrefsNumParams = 90
+};
+
+typedef UInt32 QTSS_PrefsAttributes;
+
+enum
+{
+ //QTSS_TextMessagesObject parameters
+
+ // All of these parameters are read-only, char*'s, and preemptive-safe.
+
+ qtssMsgNoMessage = 0, //"NoMessage"
+ qtssMsgNoURLInRequest = 1,
+ qtssMsgBadRTSPMethod = 2,
+ qtssMsgNoRTSPVersion = 3,
+ qtssMsgNoRTSPInURL = 4,
+ qtssMsgURLTooLong = 5,
+ qtssMsgURLInBadFormat = 6,
+ qtssMsgNoColonAfterHeader = 7,
+ qtssMsgNoEOLAfterHeader = 8,
+ qtssMsgRequestTooLong = 9,
+ qtssMsgNoModuleFolder = 10,
+ qtssMsgCouldntListen = 11,
+ qtssMsgInitFailed = 12,
+ qtssMsgNotConfiguredForIP = 13,
+ qtssMsgDefaultRTSPAddrUnavail = 14,
+ qtssMsgBadModule = 15,
+ qtssMsgRegFailed = 16,
+ qtssMsgRefusingConnections = 17,
+ qtssMsgTooManyClients = 18,
+ qtssMsgTooMuchThruput = 19,
+ qtssMsgNoSessionID = 20,
+ qtssMsgFileNameTooLong = 21,
+ qtssMsgNoClientPortInTransport = 22,
+ qtssMsgRTPPortMustBeEven = 23,
+ qtssMsgRTCPPortMustBeOneBigger = 24,
+ qtssMsgOutOfPorts = 25,
+ qtssMsgNoModuleForRequest = 26,
+ qtssMsgAltDestNotAllowed = 27,
+ qtssMsgCantSetupMulticast = 28,
+ qtssListenPortInUse = 29,
+ qtssListenPortAccessDenied = 30,
+ qtssListenPortError = 31,
+ qtssMsgBadBase64 = 32,
+ qtssMsgSomePortsFailed = 33,
+ qtssMsgNoPortsSucceeded = 34,
+ qtssMsgCannotCreatePidFile = 35,
+ qtssMsgCannotSetRunUser = 36,
+ qtssMsgCannotSetRunGroup = 37,
+ qtssMsgNoSesIDOnDescribe = 38,
+ qtssServerPrefMissing = 39,
+ qtssServerPrefWrongType = 40,
+ qtssMsgCantWriteFile = 41,
+ qtssMsgSockBufSizesTooLarge = 42,
+ qtssMsgBadFormat = 43,
+ qtssMsgNumParams = 44
+
+};
+typedef UInt32 QTSS_TextMessagesAttributes;
+
+enum
+{
+ //QTSS_FileObject parameters
+
+ // All of these parameters are preemptive-safe.
+
+ qtssFlObjStream = 0, // read // QTSS_FileStream. Stream ref for this file object
+ qtssFlObjFileSysModuleName = 1, // read // char array. Name of the file system module handling this file object
+ qtssFlObjLength = 2, // r/w // UInt64. Length of the file
+ qtssFlObjPosition = 3, // read // UInt64. Current position of the file pointer in the file.
+ qtssFlObjModDate = 4, // r/w // QTSS_TimeVal. Date & time of last modification
+
+ qtssFlObjNumParams = 5
+};
+typedef UInt32 QTSS_FileObjectAttributes;
+
+enum
+{
+ //QTSS_ModuleObject parameters
+
+ qtssModName = 0, //read //preemptive-safe //char array //Module name.
+ qtssModDesc = 1, //r/w //not preemptive-safe //char array //Text description of what the module does
+ qtssModVersion = 2, //r/w //not preemptive-safe //UInt32 //Version of the module. UInt32 format should be 0xMM.m.v.bbbb M=major version m=minor version v=very minor version b=build #
+ qtssModRoles = 3, //read //preemptive-safe //QTSS_Role //List of all the roles this module has registered for.
+ qtssModPrefs = 4, //read //preemptive-safe //QTSS_ModulePrefsObject //An object containing as attributes the preferences for this module
+ qtssModAttributes = 5, //read //preemptive-safe //QTSS_Object
+
+ qtssModNumParams = 6
+};
+typedef UInt32 QTSS_ModuleObjectAttributes;
+
+enum
+{
+ //QTSS_AttrInfoObject parameters
+
+ // All of these parameters are preemptive-safe.
+
+ qtssAttrName = 0, //read //char array //Attribute name
+ qtssAttrID = 1, //read //QTSS_AttributeID //Attribute ID
+ qtssAttrDataType = 2, //read //QTSS_AttrDataType //Data type
+ qtssAttrPermissions = 3, //read //QTSS_AttrPermission //Permissions
+
+ qtssAttrInfoNumParams = 4
+};
+typedef UInt32 QTSS_AttrInfoObjectAttributes;
+
+enum
+{
+ //QTSS_UserProfileObject parameters
+
+ // All of these parameters are preemptive-safe.
+
+ qtssUserName = 0, //read //char array
+ qtssUserPassword = 1, //r/w //char array
+ qtssUserGroups = 2, //r/w //char array - multi-valued attribute, all values should be C strings padded with \0s to // make them all of the same length
+ qtssUserRealm = 3, //r/w //char array - the authentication realm for username
+ qtssUserRights = 4, //r/w //QTSS_AttrRights - rights granted this user
+ qtssUserExtendedRights = 5, //r/w //qtssAttrDataTypeCharArray - a list of strings with extended rights granted to the user.
+ qtssUserQTSSExtendedRights = 6, //r/w //qtssAttrDataTypeCharArray - a private list of strings with extended rights granted to the user and reserved by QTSS/Apple.
+ qtssUserNumParams = 7,
+};
+typedef UInt32 QTSS_UserProfileObjectAttributes;
+
+enum
+{
+ //QTSS_ConnectedUserObject parameters
+
+ //All of these are preemptive safe
+
+ qtssConnectionType = 0, //read //char array // type of user connection (e.g. "RTP reflected" or "MP3")
+ qtssConnectionCreateTimeInMsec = 1, //read //QTSS_TimeVal //Time in milliseconds the session was created.
+ qtssConnectionTimeConnectedInMsec = 2, //read //QTSS_TimeVal //Time in milliseconds the session was created.
+ qtssConnectionBytesSent = 3, //read //UInt32 //Number of RTP bytes sent so far on this session.
+ qtssConnectionMountPoint = 4, //read //char array //Presentation URL for this session. This URL is the "base" URL for the session. RTSP requests to this URL are assumed to affect all streams on the session.
+ qtssConnectionHostName = 5, //read //char array //host name for this request
+
+ qtssConnectionSessRemoteAddrStr = 6, //read //char array //IP address addr of client, in dotted-decimal format.
+ qtssConnectionSessLocalAddrStr = 7, //read //char array //Ditto, in dotted-decimal format.
+
+ qtssConnectionCurrentBitRate = 8, //read //UInt32 //Current bit rate of all the streams on this session. This is not an average. In bits per second.
+ qtssConnectionPacketLossPercent = 9, //read //Float32 //Current percent loss as a fraction. .5 = 50%. This is not an average.
+
+ qtssConnectionTimeStorage = 10, //read //QTSS_TimeVal //Internal, use qtssConnectionTimeConnectedInMsec above
+
+ qtssConnectionNumParams = 11
+};
+typedef UInt32 QTSS_ConnectedUserObjectAttributes;
+
+
+/********************************************************************/
+// QTSS API ROLES
+//
+// Each role represents a unique situation in which a module may be
+// invoked. Modules must specify which roles they want to be invoked for.
+
+enum
+{
+ //Global
+ QTSS_Register_Role = FOUR_CHARS_TO_INT('r', 'e', 'g', ' '), //reg //All modules get this once at startup
+ QTSS_Initialize_Role = FOUR_CHARS_TO_INT('i', 'n', 'i', 't'), //init //Gets called once, later on in the startup process
+ QTSS_Shutdown_Role = FOUR_CHARS_TO_INT('s', 'h', 'u', 't'), //shut //Gets called once at shutdown
+
+ QTSS_ErrorLog_Role = FOUR_CHARS_TO_INT('e', 'l', 'o', 'g'), //elog //This gets called when the server wants to log an error.
+ QTSS_RereadPrefs_Role = FOUR_CHARS_TO_INT('p', 'r', 'e', 'f'), //pref //This gets called when the server rereads preferences.
+ QTSS_StateChange_Role = FOUR_CHARS_TO_INT('s', 't', 'a', 't'), //stat //This gets called whenever the server changes state.
+
+ QTSS_Interval_Role = FOUR_CHARS_TO_INT('t', 'i', 'm', 'r'), //timr //This gets called whenever the module's interval timer times out calls.
+
+ //RTSP-specific
+ QTSS_RTSPFilter_Role = FOUR_CHARS_TO_INT('f', 'i', 'l', 't'), //filt //Filter all RTSP requests before the server parses them
+ QTSS_RTSPRoute_Role = FOUR_CHARS_TO_INT('r', 'o', 'u', 't'), //rout //Route all RTSP requests to the correct root folder.
+ QTSS_RTSPAuthenticate_Role = FOUR_CHARS_TO_INT('a', 't', 'h', 'n'), //athn //Authenticate the RTSP request username.
+ QTSS_RTSPAuthorize_Role = FOUR_CHARS_TO_INT('a', 'u', 't', 'h'), //auth //Authorize RTSP requests to proceed
+ QTSS_RTSPPreProcessor_Role = FOUR_CHARS_TO_INT('p', 'r', 'e', 'p'), //prep //Pre-process all RTSP requests before the server responds.
+ //Modules may opt to "steal" the request and return a client response.
+ QTSS_RTSPRequest_Role = FOUR_CHARS_TO_INT('r', 'e', 'q', 'u'), //requ //Process an RTSP request & send client response
+ QTSS_RTSPPostProcessor_Role = FOUR_CHARS_TO_INT('p', 'o', 's', 't'), //post //Post-process all RTSP requests
+ QTSS_RTSPSessionClosing_Role = FOUR_CHARS_TO_INT('s', 'e', 's', 'c'), //sesc //RTSP session is going away
+
+ QTSS_RTSPIncomingData_Role = FOUR_CHARS_TO_INT('i', 'c', 'm', 'd'), //icmd //Incoming interleaved RTP data on this RTSP connection
+
+ //RTP-specific
+ QTSS_RTPSendPackets_Role = FOUR_CHARS_TO_INT('s', 'e', 'n', 'd'), //send //Send RTP packets to the client
+ QTSS_ClientSessionClosing_Role = FOUR_CHARS_TO_INT('d', 'e', 's', 's'), //dess //Client session is going away
+
+ //RTCP-specific
+ QTSS_RTCPProcess_Role = FOUR_CHARS_TO_INT('r', 't', 'c', 'p'), //rtcp //Process all RTCP packets sent to the server
+
+ //File system roles
+ QTSS_OpenFilePreProcess_Role = FOUR_CHARS_TO_INT('o', 'p', 'p', 'r'), //oppr
+ QTSS_OpenFile_Role = FOUR_CHARS_TO_INT('o', 'p', 'f', 'l'), //opfl
+ QTSS_AdviseFile_Role = FOUR_CHARS_TO_INT('a', 'd', 'f', 'l'), //adfl
+ QTSS_ReadFile_Role = FOUR_CHARS_TO_INT('r', 'd', 'f', 'l'), //rdfl
+ QTSS_CloseFile_Role = FOUR_CHARS_TO_INT('c', 'l', 'f', 'l'), //clfl
+ QTSS_RequestEventFile_Role = FOUR_CHARS_TO_INT('r', 'e', 'f', 'l'), //refl
+
+};
+typedef UInt32 QTSS_Role;
+
+
+//***********************************************/
+// TYPEDEFS
+
+typedef void* QTSS_StreamRef;
+typedef void* QTSS_Object;
+typedef void* QTSS_ServiceFunctionArgsPtr;
+typedef SInt32 QTSS_AttributeID;
+typedef SInt32 QTSS_ServiceID;
+typedef SInt64 QTSS_TimeVal;
+
+typedef QTSS_Object QTSS_RTPStreamObject;
+typedef QTSS_Object QTSS_RTSPSessionObject;
+typedef QTSS_Object QTSS_RTSPRequestObject;
+typedef QTSS_Object QTSS_RTSPHeaderObject;
+typedef QTSS_Object QTSS_ClientSessionObject;
+typedef QTSS_Object QTSS_ServerObject;
+typedef QTSS_Object QTSS_PrefsObject;
+typedef QTSS_Object QTSS_TextMessagesObject;
+typedef QTSS_Object QTSS_FileObject;
+typedef QTSS_Object QTSS_ModuleObject;
+typedef QTSS_Object QTSS_ModulePrefsObject;
+typedef QTSS_Object QTSS_AttrInfoObject;
+typedef QTSS_Object QTSS_UserProfileObject;
+typedef QTSS_Object QTSS_ConnectedUserObject;
+
+typedef QTSS_Object QTSS_3GPPStreamObject;
+typedef QTSS_Object QTSS_3GPPClientSessionObject;
+typedef QTSS_Object QTSS_3GPPRTSPSessionObject;
+typedef QTSS_Object QTSS_3GPPRequestObject;
+
+typedef QTSS_StreamRef QTSS_ErrorLogStream;
+typedef QTSS_StreamRef QTSS_FileStream;
+typedef QTSS_StreamRef QTSS_RTSPSessionStream;
+typedef QTSS_StreamRef QTSS_RTSPRequestStream;
+typedef QTSS_StreamRef QTSS_RTPStreamStream;
+typedef QTSS_StreamRef QTSS_SocketStream;
+
+typedef QTSS_RTSPStatusCode QTSS_SessionStatusCode;
+
+//***********************************************/
+// ROLE PARAMETER BLOCKS
+//
+// Each role has a unique set of parameters that get passed
+// to the module.
+
+typedef struct
+{
+ char outModuleName[QTSS_MAX_MODULE_NAME_LENGTH];
+} QTSS_Register_Params;
+
+typedef struct
+{
+ QTSS_ServerObject inServer; // Global dictionaries
+ QTSS_PrefsObject inPrefs;
+ QTSS_TextMessagesObject inMessages;
+ QTSS_ErrorLogStream inErrorLogStream; // Writing to this stream causes modules to
+ // be invoked in the QTSS_ErrorLog_Role
+ QTSS_ModuleObject inModule;
+} QTSS_Initialize_Params;
+
+typedef struct
+{
+ QTSS_ErrorVerbosity inVerbosity;
+ char* inBuffer;
+
+} QTSS_ErrorLog_Params;
+
+typedef struct
+{
+ QTSS_ServerState inNewState;
+} QTSS_StateChange_Params;
+
+typedef struct
+{
+ QTSS_RTSPSessionObject inRTSPSession;
+ QTSS_RTSPRequestObject inRTSPRequest;
+ QTSS_RTSPHeaderObject inRTSPHeaders;
+ QTSS_ClientSessionObject inClientSession;
+
+} QTSS_StandardRTSP_Params;
+
+typedef struct
+{
+ QTSS_RTSPSessionObject inRTSPSession;
+ QTSS_RTSPRequestObject inRTSPRequest;
+ char** outNewRequest;
+
+} QTSS_Filter_Params;
+
+typedef struct
+{
+ QTSS_RTSPRequestObject inRTSPRequest;
+} QTSS_RTSPAuth_Params;
+
+typedef struct
+{
+ QTSS_RTSPSessionObject inRTSPSession;
+ QTSS_ClientSessionObject inClientSession;
+ char* inPacketData;
+ UInt32 inPacketLen;
+
+} QTSS_IncomingData_Params;
+
+typedef struct
+{
+ QTSS_RTSPSessionObject inRTSPSession;
+} QTSS_RTSPSession_Params;
+
+typedef struct
+{
+ QTSS_ClientSessionObject inClientSession;
+ QTSS_TimeVal inCurrentTime;
+ QTSS_TimeVal outNextPacketTime;
+} QTSS_RTPSendPackets_Params;
+
+typedef struct
+{
+ QTSS_ClientSessionObject inClientSession;
+ QTSS_CliSesClosingReason inReason;
+} QTSS_ClientSessionClosing_Params;
+
+typedef struct
+{
+ QTSS_ClientSessionObject inClientSession;
+ QTSS_RTPStreamObject inRTPStream;
+ void* inRTCPPacketData;
+ UInt32 inRTCPPacketDataLen;
+} QTSS_RTCPProcess_Params;
+
+typedef struct
+{
+ char* inPath;
+ QTSS_OpenFileFlags inFlags;
+ QTSS_Object inFileObject;
+} QTSS_OpenFile_Params;
+
+typedef struct
+{
+ QTSS_Object inFileObject;
+ UInt64 inPosition;
+ UInt32 inSize;
+} QTSS_AdviseFile_Params;
+
+typedef struct
+{
+ QTSS_Object inFileObject;
+ UInt64 inFilePosition;
+ void* ioBuffer;
+ UInt32 inBufLen;
+ UInt32* outLenRead;
+} QTSS_ReadFile_Params;
+
+typedef struct
+{
+ QTSS_Object inFileObject;
+} QTSS_CloseFile_Params;
+
+typedef struct
+{
+ QTSS_Object inFileObject;
+ QTSS_EventType inEventMask;
+} QTSS_RequestEventFile_Params;
+
+typedef union
+{
+ QTSS_Register_Params regParams;
+ QTSS_Initialize_Params initParams;
+ QTSS_ErrorLog_Params errorParams;
+ QTSS_StateChange_Params stateChangeParams;
+
+ QTSS_Filter_Params rtspFilterParams;
+ QTSS_IncomingData_Params rtspIncomingDataParams;
+ QTSS_StandardRTSP_Params rtspRouteParams;
+ QTSS_RTSPAuth_Params rtspAthnParams;
+ QTSS_StandardRTSP_Params rtspAuthParams;
+ QTSS_StandardRTSP_Params rtspPreProcessorParams;
+ QTSS_StandardRTSP_Params rtspRequestParams;
+ QTSS_StandardRTSP_Params rtspPostProcessorParams;
+ QTSS_RTSPSession_Params rtspSessionClosingParams;
+
+ QTSS_RTPSendPackets_Params rtpSendPacketsParams;
+ QTSS_ClientSessionClosing_Params clientSessionClosingParams;
+ QTSS_RTCPProcess_Params rtcpProcessParams;
+
+ QTSS_OpenFile_Params openFilePreProcessParams;
+ QTSS_OpenFile_Params openFileParams;
+ QTSS_AdviseFile_Params adviseFileParams;
+ QTSS_ReadFile_Params readFileParams;
+ QTSS_CloseFile_Params closeFileParams;
+ QTSS_RequestEventFile_Params reqEventFileParams;
+
+} QTSS_RoleParams, *QTSS_RoleParamPtr;
+
+typedef struct
+{
+ void* packetData;
+ QTSS_TimeVal packetTransmitTime;
+ QTSS_TimeVal suggestedWakeupTime;
+} QTSS_PacketStruct;
+
+
+/********************************************************************/
+// ENTRYPOINTS & FUNCTION TYPEDEFS
+
+// MAIN ENTRYPOINT FOR MODULES
+//
+// Every QTSS API must implement two functions: a main entrypoint, and a dispatch
+// function. The main entrypoint gets called by the server at startup to do some
+// initialization. Your main entrypoint must follow the convention established below
+//
+// QTSS_Error mymodule_main(void* inPrivateArgs)
+// {
+// return _stublibrary_main(inPrivateArgs, MyDispatchFunction);
+// }
+//
+//
+
+typedef QTSS_Error (*QTSS_MainEntryPointPtr)(void* inPrivateArgs);
+typedef QTSS_Error (*QTSS_DispatchFuncPtr)(QTSS_Role inRole, QTSS_RoleParamPtr inParamBlock);
+
+// STUB LIBRARY MAIN
+QTSS_Error _stublibrary_main(void* inPrivateArgs, QTSS_DispatchFuncPtr inDispatchFunc);
+
+/********************************************************************/
+// QTSS_New
+// QTSS_Delete
+//
+// These should be used for all dynamic memory allocation done from
+// within modules. The memoryIdentifier is used for debugging:
+// the server can track this memory to make memory leak debugging easier.
+void* QTSS_New(FourCharCode inMemoryIdentifier, UInt32 inSize);
+void QTSS_Delete(void* inMemory);
+
+/********************************************************************/
+// QTSS_Milliseconds
+//
+// The server maintains a millisecond timer internally. The current
+// value of that timer can be obtained from this function. This value
+// is not valid between server executions.
+//
+// All millisecond values used in QTSS API use this timer, unless otherwise noted
+QTSS_TimeVal QTSS_Milliseconds();
+
+
+/********************************************************************/
+// QTSS_MilliSecsTo1970Secs
+//
+// Convert milliseconds from the QTSS_Milliseconds call to
+// second's since 1970
+//
+time_t QTSS_MilliSecsTo1970Secs(QTSS_TimeVal inQTSS_MilliSeconds);
+
+/********************************************************************/
+// QTSS_AddRole
+//
+// Only available from QTSS_Initialize role. Call this for all the roles you
+// would like your module to operate on.
+//
+// Returns: QTSS_NoErr
+// QTSS_OutOfState: If this function isn't being called from the Register role
+// QTSS_RequestFailed: If module is registering for the QTSS_RTSPRequest_Role
+// and there already is such a module.
+// QTSS_BadArgument: Registering for a nonexistent role.
+QTSS_Error QTSS_AddRole(QTSS_Role inRole);
+
+
+/*****************************************/
+// ATTRIBUTE / OBJECT CALLBACKS
+//
+
+
+/********************************************************************/
+// QTSS_LockObject
+//
+// Grabs the mutex for this object so that accesses to the objects attributes
+// from other threads will block. Note that objects created through QTSS_CreateObjectValue
+// will share a mutex with the parent object.
+//
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: bad object
+QTSS_Error QTSS_LockObject(QTSS_Object inObject);
+
+/********************************************************************/
+// QTSS_UnlockObject
+//
+// Releases the mutex for this object.
+//
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: bad object
+QTSS_Error QTSS_UnlockObject(QTSS_Object inObject);
+
+/********************************************************************/
+// QTSS_CreateObjectType
+//
+// Creates a new object type. Attributes can be added to this object type and then it can
+// be passed into QTSS_CreateObjectValue.
+//
+// This may only be called from the QTSS_Register role.
+//
+// Returns: QTSS_NoErr
+// QTSS_RequestFailed: Too many object types already exist.
+QTSS_Error QTSS_CreateObjectType(QTSS_ObjectType* outType);
+
+/********************************************************************/
+// QTSS_AddStaticAttribute
+//
+// Adds a new static attribute to a predefined object type. All added attributes implicitly have
+// qtssAttrModeRead, qtssAttrModeWrite, and qtssAttrModePreempSafe permissions. "inUnused" should
+// always be NULL. Specify the data type and name of the attribute.
+//
+// This may only be called from the QTSS_Register role.
+//
+// Returns: QTSS_NoErr
+// QTSS_OutOfState: If this function isn't being called from the Register role
+// QTSS_BadArgument: Adding an attribute to a nonexistent object type, attribute
+// name too long, or NULL arguments.
+// QTSS_AttrNameExists: The name must be unique.
+QTSS_Error QTSS_AddStaticAttribute( QTSS_ObjectType inObjectType, char* inAttrName,
+ void* inUnused, QTSS_AttrDataType inAttrDataType);
+
+/********************************************************************/
+// QTSS_AddInstanceAttribute
+//
+// Adds a new instance attribute to a predefined object type. All added attributes implicitly have
+// qtssAttrModeRead, qtssAttrModeWrite, and qtssAttrModePreempSafe permissions. "inUnused" should
+// always be NULL. Specify the data type and name of the attribute.
+//
+// This may be called at any time.
+//
+// Returns: QTSS_NoErr
+// QTSS_OutOfState: If this function isn't being called from the Register role
+// QTSS_BadArgument: Adding an attribute to a nonexistent object type, attribute
+// name too long, or NULL arguments.
+// QTSS_AttrNameExists: The name must be unique.
+QTSS_Error QTSS_AddInstanceAttribute( QTSS_Object inObject, char* inAttrName,
+ void* inUnused, QTSS_AttrDataType inAttrDataType);
+
+/********************************************************************/
+// QTSS_RemoveInstanceAttribute
+//
+// Removes an existing instance attribute. This may be called at any time
+//
+// Returns: QTSS_NoErr
+// QTSS_OutOfState: If this function isn't being called from the Register role
+// QTSS_BadArgument: Bad object type.
+// QTSS_AttrDoesntExist: Bad attribute ID
+QTSS_Error QTSS_RemoveInstanceAttribute(QTSS_Object inObject, QTSS_AttributeID inID);
+
+/********************************************************************/
+// Getting attribute information
+//
+// The following callbacks allow modules to discover at runtime what
+// attributes exist in which objects and object types, and discover
+// all attribute meta-data
+
+/********************************************************************/
+// QTSS_IDForAttr
+//
+// Given an attribute name, this returns its accompanying attribute ID.
+// The ID can in turn be used to retrieve the attribute value from
+// a object. This callback applies only to static attributes
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+QTSS_Error QTSS_IDForAttr(QTSS_ObjectType inObjectType, const char* inAttributeName,
+ QTSS_AttributeID* outID);
+
+/********************************************************************/
+// QTSS_GetAttrInfoByID
+//
+// Searches for an attribute with the specified ID in the specified object.
+// If found, this function returns a QTSS_AttrInfoObject describing the attribute.
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument
+// QTSS_AttrDoesntExist
+QTSS_Error QTSS_GetAttrInfoByID(QTSS_Object inObject, QTSS_AttributeID inAttrID,
+ QTSS_AttrInfoObject* outAttrInfoObject);
+
+/********************************************************************/
+// QTSS_GetAttrInfoByName
+//
+// Searches for an attribute with the specified name in the specified object.
+// If found, this function returns a QTSS_AttrInfoObject describing the attribute.
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument
+// QTSS_AttrDoesntExist
+QTSS_Error QTSS_GetAttrInfoByName(QTSS_Object inObject, char* inAttrName,
+ QTSS_AttrInfoObject* outAttrInfoObject);
+
+/********************************************************************/
+// QTSS_GetAttrInfoByIndex
+//
+// Allows caller to iterate over all the attributes in the specified object.
+// Returns a QTSS_AttrInfoObject for the attribute with the given index (0.. num attributes).
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument
+// QTSS_AttrDoesntExist
+QTSS_Error QTSS_GetAttrInfoByIndex(QTSS_Object inObject, UInt32 inIndex,
+ QTSS_AttrInfoObject* outAttrInfoObject);
+
+/********************************************************************/
+// QTSS_GetNumAttributes
+//
+// Returns the number of attributes in the specified object.
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+//
+QTSS_Error QTSS_GetNumAttributes (QTSS_Object inObject, UInt32* outNumAttributes);
+
+/********************************************************************/
+// QTSS_GetValuePtr
+//
+// NOT TO BE USED WITH NON-PREEMPTIVE-SAFE attributes (or provide your own locking
+// using QTSS_LockObject).
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+// QTSS_NotPreemptiveSafe: Attempt to get a non-preemptive safe attribute
+// QTSS_BadIndex: Attempt to get non-existent index.
+QTSS_Error QTSS_GetValuePtr (QTSS_Object inObject, QTSS_AttributeID inID, UInt32 inIndex,
+ void** outBuffer, UInt32* outLen);
+
+/********************************************************************/
+// QTSS_GetValue
+//
+// Copies the data into provided buffer. If QTSS_NotEnoughSpace is returned, outLen is still set.
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+// QTSS_NotEnoughSpace: Value is too big for buffer provided.
+// QTSS_BadIndex: Attempt to get non-existent index.
+QTSS_Error QTSS_GetValue (QTSS_Object inObject, QTSS_AttributeID inID, UInt32 inIndex,
+ void* ioBuffer, UInt32* ioLen);
+
+/********************************************************************/
+// QTSS_GetValueAsString
+//
+// Returns the specified attribute converted to a C-string. This call allocates
+// memory for the string which should be disposed of using QTSS_Delete.
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+// QTSS_BadIndex: Attempt to get non-existent index.
+QTSS_Error QTSS_GetValueAsString (QTSS_Object inObject, QTSS_AttributeID inID, UInt32 inIndex,
+ char** outString);
+
+
+/********************************************************************/
+// QTSS_TypeStringToType
+// QTSS_TypeToTypeString
+//
+// Returns a text name for the specified QTSS_AttrDataType, or vice-versa
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument
+QTSS_Error QTSS_TypeStringToType(const char* inTypeString, QTSS_AttrDataType* outType);
+QTSS_Error QTSS_TypeToTypeString(const QTSS_AttrDataType inType, char** outTypeString);
+
+
+/********************************************************************/
+// QTSS_StringToValue
+//
+// Given a C-string and a QTSS_AttrDataType, this function converts the C-string
+// to the specified type and puts the result in ioBuffer. ioBuffer must be allocated
+// by the caller and must be big enough to contain the converted value.
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+// QTSS_NotEnoughSpace: Value is too big for buffer provided.
+//
+// QTSS_ValueToString
+//
+// Given a buffer containing a value of the specified type, this function converts
+// the value to a C-string. This string is allocated internally and must be disposed of
+// using QTSS_Delete
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+QTSS_Error QTSS_StringToValue(const char* inValueAsString, const QTSS_AttrDataType inType, void* ioBuffer, UInt32* ioBufSize);
+QTSS_Error QTSS_ValueToString(const void* inValue, const UInt32 inValueLen, const QTSS_AttrDataType inType, char** outString);
+
+/********************************************************************/
+// QTSS_SetValue
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+// QTSS_ReadOnly: Attribute is read only.
+// QTSS_BadIndex: Attempt to set non-0 index of attribute with a param retrieval function.
+//
+QTSS_Error QTSS_SetValue (QTSS_Object inObject, QTSS_AttributeID inID, UInt32 inIndex, const void* inBuffer, UInt32 inLen);
+
+/********************************************************************/
+// QTSS_SetValuePtr
+//
+// This allows you to have an attribute that simply reflects the value of a variable in your module.
+// If the update to this variable is not atomic, you should protect updates using QTSS_LockObject.
+// This can't be used with indexed attributes. Make sure the inBuffer provided exists as long as this
+// attribute exists.
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+// QTSS_ReadOnly: Attribute is read only.
+//
+QTSS_Error QTSS_SetValuePtr (QTSS_Object inObject, QTSS_AttributeID inID, const void* inBuffer, UInt32 inLen);
+
+/********************************************************************/
+// QTSS_CreateObjectValue
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+// QTSS_ReadOnly: Attribute is read only.
+//
+QTSS_Error QTSS_CreateObjectValue (QTSS_Object inObject, QTSS_AttributeID inID, QTSS_ObjectType inType, UInt32* outIndex, QTSS_Object* outCreatedObject);
+
+/********************************************************************/
+// QTSS_GetNumValues
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+//
+QTSS_Error QTSS_GetNumValues (QTSS_Object inObject, QTSS_AttributeID inID, UInt32* outNumValues);
+
+/********************************************************************/
+// QTSS_RemoveValue
+//
+// This function removes the value with the specified index. If there
+// are any values following this index, they will be reordered.
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+// QTSS_ReadOnly: Attribute is read only.
+// QTSS_BadIndex: Attempt to set non-0 index of attribute with a param retrieval function.
+//
+QTSS_Error QTSS_RemoveValue (QTSS_Object inObject, QTSS_AttributeID inID, UInt32 inIndex);
+
+/*****************************************/
+// STREAM CALLBACKS
+//
+// The QTSS API provides QTSS_StreamRefs as a generalized stream abstraction. Mostly,
+// QTSS_StreamRefs are used for communicating with the client. For instance,
+// in the QTSS_RTSPRequest_Role, modules receive a QTSS_StreamRef which can be
+// used for reading RTSP data from the client, and sending RTSP response data to the client.
+//
+// Additionally, QTSS_StreamRefs are generalized enough to be used in many other situations.
+// For instance, modules receive a QTSS_StreamRef for the error log. When modules want
+// to report errors, they can use these same routines, passing in the error log StreamRef.
+
+/********************************************************************/
+// QTSS_Write
+//
+// Writes data to a stream.
+//
+// Returns: QTSS_NoErr
+// QTSS_WouldBlock: The stream cannot accept any data at this time.
+// QTSS_NotConnected: The stream receiver is no longer connected.
+// QTSS_BadArgument: NULL argument.
+QTSS_Error QTSS_Write(QTSS_StreamRef inRef, const void* inBuffer, UInt32 inLen, UInt32* outLenWritten, QTSS_WriteFlags inFlags);
+
+/********************************************************************/
+// QTSS_WriteV
+//
+// Works similar to the POSIX WriteV, and takes a POSIX iovec.
+// THE FIRST ENTRY OF THE IOVEC MUST BE BLANK!!!
+//
+// Returns: QTSS_NoErr
+// QTSS_WouldBlock: The stream cannot accept any data at this time.
+// QTSS_NotConnected: The stream receiver is no longer connected.
+// QTSS_BadArgument: NULL argument.
+QTSS_Error QTSS_WriteV(QTSS_StreamRef inRef, iovec* inVec, UInt32 inNumVectors, UInt32 inTotalLength, UInt32* outLenWritten);
+
+/********************************************************************/
+// QTSS_Flush
+//
+// Some QTSS_StreamRefs (QTSS_RequestRef, for example) buffers data before sending it
+// out. Calling this forces the stream to write the data immediately.
+//
+// Returns: QTSS_NoErr
+// QTSS_WouldBlock: Stream cannot be completely flushed at this time.
+// QTSS_NotConnected: The stream receiver is no longer connected.
+// QTSS_BadArgument: NULL argument.
+QTSS_Error QTSS_Flush(QTSS_StreamRef inRef);
+
+/********************************************************************/
+// QTSS_Read
+//
+// Reads data out of the stream
+//
+// Arguments inRef: The stream to read from.
+// ioBuffer: A buffer to place the read data
+// inBufLen: The length of ioBuffer.
+// outLengthRead: If function returns QTSS_NoErr, on output this will be set to the
+// amount of data actually read.
+//
+// Returns: QTSS_NoErr
+// QTSS_WouldBlock
+// QTSS_RequestFailed
+// QTSS_BadArgument
+QTSS_Error QTSS_Read(QTSS_StreamRef inRef, void* ioBuffer, UInt32 inBufLen, UInt32* outLengthRead);
+
+/********************************************************************/
+// QTSS_Seek
+//
+// Sets the current stream position to inNewPosition
+//
+// Arguments inRef: The stream to read from.
+// inNewPosition: Offset from the start of the stream.
+//
+// Returns: QTSS_NoErr
+// QTSS_RequestFailed
+// QTSS_BadArgument
+QTSS_Error QTSS_Seek(QTSS_StreamRef inRef, UInt64 inNewPosition);
+
+/********************************************************************/
+// QTSS_Advise
+//
+// Lets the stream know that the specified section of the stream will be read soon.
+//
+// Arguments inRef: The stream to advise.
+// inPosition: Offset from the start of the stream of the advise region.
+// inAdviseSize: Size of the advise region.
+//
+// Returns: QTSS_NoErr
+// QTSS_RequestFailed
+// QTSS_BadArgument
+QTSS_Error QTSS_Advise(QTSS_StreamRef inRef, UInt64 inPosition, UInt32 inAdviseSize);
+
+
+/*****************************************/
+// SERVICES
+//
+// Oftentimes modules have functionality that they want accessable from other
+// modules. An example of this might be a logging module that allows other
+// modules to write messages to the log.
+//
+// Modules can use the following callbacks to register and invoke "services".
+// Adding & finding services works much like adding & finding attributes in
+// an object. A service has a name. In order to invoke a service, the calling
+// module must know the name of the service and resolve that name into an ID.
+//
+// Each service has a parameter block format that is specific to that service.
+// Modules that are exporting services should carefully document the services they
+// export, and modules calling services should take care to fail gracefully
+// if the service isn't present or returns an error.
+
+typedef QTSS_Error (*QTSS_ServiceFunctionPtr)(QTSS_ServiceFunctionArgsPtr);
+
+/********************************************************************/
+// QTSS_AddService
+//
+// This function registers a service with the specified name, and
+// associates it with the specified function pointer.
+// QTSS_AddService may only be called from the QTSS_Register role
+//
+// Returns: QTSS_NoErr
+// QTSS_OutOfState: If this function isn't being called from the Register role
+// QTSS_BadArgument: Service name too long, or NULL arguments.
+QTSS_Error QTSS_AddService(const char* inServiceName, QTSS_ServiceFunctionPtr inFunctionPtr);
+
+
+/********************************************************************/
+// QTSS_IDForService
+//
+// Much like QTSS_IDForAttr, this resolves a service name into its
+// corresponding QTSS_ServiceID. The QTSS_ServiceID can then be used to
+// invoke the service.
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+QTSS_Error QTSS_IDForService(const char* inTag, QTSS_ServiceID* outID);
+
+/********************************************************************/
+// QTSS_DoService
+//
+// Invokes the service. Return value from this function comes from the service
+// function itself, unless the QTSS_IllegalService errorcode is returned,
+// which is returned when the QTSS_ServiceID is bad.
+QTSS_Error QTSS_DoService(QTSS_ServiceID inID, QTSS_ServiceFunctionArgsPtr inArgs);
+
+/********************************************************************/
+// BUILT-IN SERVICES
+//
+// The server registers some built-in services when it starts up.
+// Here are macros for their names & descriptions of what they do
+
+// Rereads the preferences, also causes the QTSS_RereadPrefs_Role to be invoked
+#define QTSS_REREAD_PREFS_SERVICE "RereadPreferences"
+
+
+
+/*****************************************/
+// RTSP HEADER CALLBACKS
+//
+// As a convience to modules that want to send RTSP responses, the server
+// has internal utilities for formatting a proper RTSP response. When a module
+// calls QTSS_SendRTSPHeaders, the server sends a proper RTSP status line, using
+// the request's current status code, and also sends the proper CSeq header,
+// session ID header, and connection header.
+//
+// Any other headers can be appended by calling QTSS_AppendRTSPHeader. They will be
+// sent along with everything else when QTSS_SendRTSPHeaders is called.
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+QTSS_Error QTSS_SendRTSPHeaders(QTSS_RTSPRequestObject inRef);
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+QTSS_Error QTSS_AppendRTSPHeader(QTSS_RTSPRequestObject inRef, QTSS_RTSPHeader inHeader, const char* inValue, UInt32 inValueLen);
+
+
+/*****************************************/
+// QTSS_SendStandardRTSPResponse
+//
+// This function is also provided as an optional convienence to modules who are sending
+// "typical" RTSP responses to clients. The function uses the QTSS_RTSPRequestObject and
+// the QTSS_Object as inputs, where the object may either be a QTSS_ClientSessionObject
+// or a QTSS_RTPStreamObject, depending on the method. The response is written to the
+// stream provided.
+//
+// Below is a description of what is returned for each method this function supports:
+//
+// DESCRIBE:
+//
+// Writes status line, CSeq, SessionID, Connection headers as determined by the request.
+// Writes a Content-Base header with the Content-Base being the URL provided.
+// Writes a Content-Type header of "application/sdp"
+// QTSS_Object must be a QTSS_ClientSessionObject.
+//
+// SETUP:
+//
+// Writes status line, CSeq, SessionID, Connection headers as determined by the request.
+// Writes a Transport header with the client & server ports (if connection is over UDP).
+// QTSS_Object must be a QTSS_RTPStreamObject.
+//
+// PLAY:
+//
+// Writes status line, CSeq, SessionID, Connection headers as determined by the request.
+// QTSS_Object must be a QTSS_ClientSessionObject.
+//
+// Specify whether you want the server to append the seq#, timestamp, & ssrc info to
+// the RTP-Info header via. the qtssPlayRespWriteTrackInfo flag.
+//
+// PAUSE:
+//
+// Writes status line, CSeq, SessionID, Connection headers as determined by the request.
+// QTSS_Object must be a QTSS_ClientSessionObject.
+//
+// TEARDOWN:
+//
+// Writes status line, CSeq, SessionID, Connection headers as determined by the request.
+// QTSS_Object must be a QTSS_ClientSessionObject.
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+QTSS_Error QTSS_SendStandardRTSPResponse(QTSS_RTSPRequestObject inRTSPRequest, QTSS_Object inRTPInfo, UInt32 inFlags);
+
+
+/*****************************************/
+// CLIENT SESSION CALLBACKS
+//
+// QTSS API Modules have the option of generating and sending RTP packets. Only
+// one module currently can generate packets for a particular session. In order
+// to do this, call QTSS_AddRTPStream. This must be done in response to a RTSP
+// request, and typically is done in response to a SETUP request from the client.
+//
+// After one or more streams have been added to the session, the module that "owns"
+// the packet sending for that session can call QTSS_Play to start the streams playing.
+// After calling QTSS_Play, the module will get invoked in the QTSS_SendPackets_Role.
+// Calling QTSS_Pause stops playing.
+//
+// The "owning" module may call QTSS_Teardown at any time. Doing this closes the
+// session and will cause the QTSS_SessionClosing_Role to be invoked for this session.
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+// QTSS_RequestFailed: QTSS_RTPStreamObject couldn't be created.
+QTSS_Error QTSS_AddRTPStream(QTSS_ClientSessionObject inClientSession, QTSS_RTSPRequestObject inRTSPRequest, QTSS_RTPStreamObject* outStream, QTSS_AddStreamFlags inFlags);
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+// QTSS_RequestFailed: No streams added to this session.
+QTSS_Error QTSS_Play(QTSS_ClientSessionObject inClientSession, QTSS_RTSPRequestObject inRTSPRequest, QTSS_PlayFlags inPlayFlags);
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+QTSS_Error QTSS_Pause(QTSS_ClientSessionObject inClientSession);
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+QTSS_Error QTSS_Teardown(QTSS_ClientSessionObject inClientSession);
+
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+QTSS_Error QTSS_RefreshTimeOut(QTSS_ClientSessionObject inClientSession);
+
+/*****************************************/
+// FILE SYSTEM CALLBACKS
+//
+// All modules that interact with the local file system should use these APIs instead
+// of the direct operating system calls.
+//
+// This is for two reasons: 1) to ensure portability of your module across different
+// platforms such as Win32 and different versions of the UNIX operating system.
+//
+// 2) To ensure your module will work properly if there is a 3rd party file system
+// or database that contains media files.
+
+/********************************************************************/
+// QTSS_OpenFileObject
+//
+// Arguments inPath: a NULL-terminated C-string containing a full path to the file to open.
+// inPath must be in the local (operating system) file system path style.
+// inFlags: desired flags.
+// outFileObject: If function returns QTSS_NoErr, on output this will be a QTSS_Object
+// for the file.
+//
+// Returns: QTSS_NoErr
+// QTSS_FileNotFound
+// QTSS_RequestFailed
+// QTSS_BadArgument
+QTSS_Error QTSS_OpenFileObject(char* inPath, QTSS_OpenFileFlags inFlags, QTSS_Object* outFileObject);
+
+/********************************************************************/
+// QTSS_CloseFileObject
+//
+// Closes the file object.
+//
+// Arguments: inFileObject: the file to close
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument
+QTSS_Error QTSS_CloseFileObject(QTSS_Object inFileObject);
+
+
+/*****************************************/
+// SOCKET CALLBACKS
+//
+// It is not necessary for a module that internally uses network I/O to go through
+// the QTSS API for their networking APIs. However, it is highly recommended
+// to use nonblocking network I/O from a module. With nonblocking network I/O, it
+// is very important to be able to receive socket events.
+//
+// To facilitate this, QTSS API provides the following two callbacks to link external
+// sockets into the QTSS API streams framework.
+//
+// Once a module has created a QTSS stream out of its socket, it is possible to use the
+// QTSS_RequestEvent callback to receive events on the socket.
+
+
+/********************************************************************/
+// QTSS_CreateStreamFromSocket
+//
+// Creates a socket stream.
+//
+// Arguments: inFileDesc: the socket
+//
+// Returns: QTSS_NoErr
+QTSS_Error QTSS_CreateStreamFromSocket(int inFileDesc, QTSS_SocketStream* outStream);
+
+
+/********************************************************************/
+// QTSS_DestroySocketStream
+//
+// Creates a socket stream.
+//
+// Arguments: inFileDesc: the socket
+//
+// Returns: QTSS_NoErr
+QTSS_Error QTSS_DestroySocketStream(QTSS_SocketStream inStream);
+
+
+/*****************************************/
+// ASYNC I/O CALLBACKS
+//
+// QTSS modules must be kind in how they use the CPU. The server doesn't
+// prevent a poorly implemented QTSS module from hogging the processing
+// capability of the server, at the expense of other modules and other clients.
+//
+// It is therefore imperitive that a module use non-blocking, or async, I/O.
+// If a module were to block, say, waiting to read file data off disk, this stall
+// would affect the entire server.
+//
+// This problem is resolved in QTSS API in a number of ways.
+//
+// Firstly, all QTSS_StreamRefs provided to modules are non-blocking, or async.
+// Modules should be prepared to receive EWOULDBLOCK errors in response to
+// QTSS_Read, QTSS_Write, & QTSS_WriteV calls, with certain noted exceptions
+// in the case of responding to RTSP requests.
+//
+// Modules that open their own file descriptors for network or file I/O can
+// create separate threads for handling I/O. In this case, these descriptors
+// can remain blocking, as long as they always block on the private module threads.
+//
+// In most cases, however, creating a separate thread for I/O is not viable for the
+// kind of work the module would like to do. For instance, a module may wish
+// to respond to a RTSP DESCRIBE request, but can't immediately because constructing
+// the response would require I/O that would block.
+//
+// The problem is once the module returns from the QTSS_RTSPProcess_Role, the
+// server will mistakenly consider the request handled, and move on. It won't
+// know that the module has more work to do before it finishes processing the DESCRIBE.
+//
+// In this case, the module needs to tell the server to delay processing of the
+// DESCRIBE request until the file descriptor's blocking condition is lifted.
+// The module can do this by using the provided "event" callback routines.
+
+// Returns: QTSS_NoErr
+// QTSS_BadArgument: Bad argument
+// QTSS_OutOfState: if this callback is made from a role that doesn't allow async I/O events
+// QTSS_RequestFailed: Not currently possible to request an event.
+
+QTSS_Error QTSS_RequestEvent(QTSS_StreamRef inStream, QTSS_EventType inEventMask);
+QTSS_Error QTSS_SignalStream(QTSS_StreamRef inStream, QTSS_EventType inEventMask);
+
+QTSS_Error QTSS_SetIdleTimer(SInt64 inIdleMsec);
+QTSS_Error QTSS_SetIntervalRoleTimer(SInt64 inIdleMsec);
+
+QTSS_Error QTSS_RequestGlobalLock();
+Bool16 QTSS_IsGlobalLocked();
+QTSS_Error QTSS_GlobalUnLock();
+
+
+/*****************************************/
+// AUTHENTICATE and AUTHORIZE CALLBACKS
+//
+// All modules that want Authentication outside of the
+// QTSS_RTSPAuthenticate_Role must use the QTSS_Authenticate callback
+// and must pass in the request object
+// All modules that want Authorization outside of the
+// QTSS_RTSPAuthorize_Role should use the QTSS_Authorize callback
+// and must pass in the request object
+/********************************************************************/
+
+// QTSS_Authenticate
+//
+// Arguments inputs: inAuthUserName: the username that is to be authenticated
+// inAuthResourceLocalPath:the resource that is to be authorized access
+// inAuthMoviesDir: the movies directory (reqd. for finding the access file)
+// inAuthRequestAction: the action that is performed for the resource
+// inAuthScheme: the authentication scheme (the password retrieved will be based on it)
+// ioAuthRequestObject: the request object
+// The object is filled with the attributes passed in
+// Returns: QTSS_NoErr
+// QTSS_BadArgument if any of the input arguments are null
+QTSS_Error QTSS_Authenticate( const char* inAuthUserName,
+ const char* inAuthResourceLocalPath,
+ const char* inAuthMoviesDir,
+ QTSS_ActionFlags inAuthRequestAction,
+ QTSS_AuthScheme inAuthScheme,
+ QTSS_RTSPRequestObject ioAuthRequestObject);
+
+// QTSS_Authorize
+//
+// Arguments inputs: inAuthRequestObject: the request object
+//
+// outputs: outAuthRealm: the authentication realm
+// outAuthUserAllowed: true if user is allowed, and false otherwise
+//
+// Returns: QTSS_NoErr
+// QTSS_BadArgument
+QTSS_Error QTSS_Authorize(QTSS_RTSPRequestObject inAuthRequestObject, char** outAuthRealm, Bool16* outAuthUserAllowed);
+
+
+void QTSS_LockStdLib();
+void QTSS_UnlockStdLib();
+
+#ifdef QTSS_OLDROUTINENAMES
+
+//
+// Legacy routines
+
+//
+// QTSS_AddAttribute has been replaced by QTSS_AddStaticAttribute
+QTSS_Error QTSS_AddAttribute(QTSS_ObjectType inObjectType, const char* inAttributeName,
+ void* inUnused);
+
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/APIStubLib/QTSSRTSPProtocol.h b/APIStubLib/QTSSRTSPProtocol.h
new file mode 100644
index 0000000..929478e
--- /dev/null
+++ b/APIStubLib/QTSSRTSPProtocol.h
@@ -0,0 +1,215 @@
+/*
+ *
+ * @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: QTSSRTSPProtocol.h
+
+ Contains: Constant & Enum definitions for RTSP protocol type parts
+ of QTSS API.
+
+
+*/
+
+#ifndef QTSS_RTSPPROTOCOL_H
+#define QTSS_RTSPPROTOCOL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "OSHeaders.h"
+
+enum
+{
+ qtssDescribeMethod = 0,
+ qtssSetupMethod = 1,
+ qtssTeardownMethod = 2,
+ qtssNumVIPMethods = 3,
+
+ qtssPlayMethod = 3,
+ qtssPauseMethod = 4,
+ qtssOptionsMethod = 5,
+ qtssAnnounceMethod = 6,
+ qtssGetParameterMethod = 7,
+ qtssSetParameterMethod = 8,
+ qtssRedirectMethod = 9,
+ qtssRecordMethod = 10,
+
+ qtssNumMethods = 11,
+ qtssIllegalMethod = 11
+
+};
+typedef UInt32 QTSS_RTSPMethod;
+
+
+enum
+{
+ //These are the common request headers (optimized)
+ qtssAcceptHeader = 0,
+ qtssCSeqHeader = 1,
+ qtssUserAgentHeader = 2,
+ qtssTransportHeader = 3,
+ qtssSessionHeader = 4,
+ qtssRangeHeader = 5,
+ qtssNumVIPHeaders = 6,
+
+ //Other request headers
+ qtssAcceptEncodingHeader = 6,
+ qtssAcceptLanguageHeader = 7,
+ qtssAuthorizationHeader = 8,
+ qtssBandwidthHeader = 9,
+ qtssBlockSizeHeader = 10,
+ qtssCacheControlHeader = 11,
+ qtssConferenceHeader = 12,
+ qtssConnectionHeader = 13,
+ qtssContentBaseHeader = 14,
+ qtssContentEncodingHeader = 15,
+ qtssContentLanguageHeader = 16,
+ qtssContentLengthHeader = 17,
+ qtssContentLocationHeader = 18,
+ qtssContentTypeHeader = 19,
+ qtssDateHeader = 20,
+ qtssExpiresHeader = 21,
+ qtssFromHeader = 22,
+ qtssHostHeader = 23,
+ qtssIfMatchHeader = 24,
+ qtssIfModifiedSinceHeader = 25,
+ qtssLastModifiedHeader = 26,
+ qtssLocationHeader = 27,
+ qtssProxyAuthenticateHeader = 28,
+ qtssProxyRequireHeader = 29,
+ qtssRefererHeader = 30,
+ qtssRetryAfterHeader = 31,
+ qtssRequireHeader = 32,
+ qtssRTPInfoHeader = 33,
+ qtssScaleHeader = 34,
+ qtssSpeedHeader = 35,
+ qtssTimestampHeader = 36,
+ qtssVaryHeader = 37,
+ qtssViaHeader = 38,
+ qtssNumRequestHeaders = 39,
+
+ //Additional response headers
+ qtssAllowHeader = 39,
+ qtssPublicHeader = 40,
+ qtssServerHeader = 41,
+ qtssUnsupportedHeader = 42,
+ qtssWWWAuthenticateHeader = 43,
+ qtssSameAsLastHeader = 44,
+
+ //Newly added headers
+ qtssExtensionHeaders = 45,
+
+ qtssXRetransmitHeader = 45,
+ qtssXAcceptRetransmitHeader = 46,
+ qtssXRTPMetaInfoHeader = 47,
+ qtssXTransportOptionsHeader = 48,
+ qtssXPacketRangeHeader = 49,
+ qtssXPreBufferHeader = 50,
+ qtssXDynamicRateHeader = 51,
+ qtssXAcceptDynamicRateHeader= 52,
+
+ // QT Player random data request
+ qtssXRandomDataSizeHeader = 53,
+
+ // 3gpp release 6
+ qtss3GPPLinkCharHeader = 54,
+ qtss3GPPAdaptationHeader = 55,
+ qtss3GPPQOEFeedback = 56,
+ qtss3GPPQOEMetrics = 57,
+
+ // 3gpp annex g
+ qtssXPreDecBufSizeHeader = 58,
+ qtssXInitPredecBufPeriodHeader = 59,
+ qtssXInitPostDecBufPeriodHeader = 60,
+ qtss3GPPVideoPostDecBufSizeHeader = 61,
+
+
+ qtssNumHeaders = 62,
+ qtssIllegalHeader = 62
+
+};
+typedef UInt32 QTSS_RTSPHeader;
+
+
+enum
+{
+ qtssContinue = 0, //100
+ qtssSuccessOK = 1, //200
+ qtssSuccessCreated = 2, //201
+ qtssSuccessAccepted = 3, //202
+ qtssSuccessNoContent = 4, //203
+ qtssSuccessPartialContent = 5, //204
+ qtssSuccessLowOnStorage = 6, //250
+ qtssMultipleChoices = 7, //300
+ qtssRedirectPermMoved = 8, //301
+ qtssRedirectTempMoved = 9, //302
+ qtssRedirectSeeOther = 10, //303
+ qtssRedirectNotModified = 11, //304
+ qtssUseProxy = 12, //305
+ qtssClientBadRequest = 13, //400
+ qtssClientUnAuthorized = 14, //401
+ qtssPaymentRequired = 15, //402
+ qtssClientForbidden = 16, //403
+ qtssClientNotFound = 17, //404
+ qtssClientMethodNotAllowed = 18, //405
+ qtssNotAcceptable = 19, //406
+ qtssProxyAuthenticationRequired = 20, //407
+ qtssRequestTimeout = 21, //408
+ qtssClientConflict = 22, //409
+ qtssGone = 23, //410
+ qtssLengthRequired = 24, //411
+ qtssPreconditionFailed = 25, //412
+ qtssRequestEntityTooLarge = 26, //413
+ qtssRequestURITooLarge = 27, //414
+ qtssUnsupportedMediaType = 28, //415
+ qtssClientParameterNotUnderstood = 29, //451
+ qtssClientConferenceNotFound = 30, //452
+ qtssClientNotEnoughBandwidth = 31, //453
+ qtssClientSessionNotFound = 32, //454
+ qtssClientMethodNotValidInState = 33, //455
+ qtssClientHeaderFieldNotValid = 34, //456
+ qtssClientInvalidRange = 35, //457
+ qtssClientReadOnlyParameter = 36, //458
+ qtssClientAggregateOptionNotAllowed = 37, //459
+ qtssClientAggregateOptionAllowed = 38, //460
+ qtssClientUnsupportedTransport = 39, //461
+ qtssClientDestinationUnreachable = 40, //462
+ qtssServerInternal = 41, //500
+ qtssServerNotImplemented = 42, //501
+ qtssServerBadGateway = 43, //502
+ qtssServerUnavailable = 44, //503
+ qtssServerGatewayTimeout = 45, //505
+ qtssRTSPVersionNotSupported = 46, //504
+ qtssServerOptionNotSupported = 47, //551
+ qtssNumStatusCodes = 48
+
+};
+typedef UInt32 QTSS_RTSPStatusCode;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/APIStubLib/QTSS_Private.cpp b/APIStubLib/QTSS_Private.cpp
new file mode 100644
index 0000000..7161d7c
--- /dev/null
+++ b/APIStubLib/QTSS_Private.cpp
@@ -0,0 +1,404 @@
+/*
+ *
+ * @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: QTSS_Private.c
+
+ Contains: Code for stub library and stub callback functions.
+
+
+
+*/
+
+#include
+#include "SafeStdLib.h"
+#ifndef __Win32__
+#include
+#include
+#endif
+
+#include "QTSS.h"
+#include "QTSS_Private.h"
+
+static QTSS_CallbacksPtr sCallbacks = NULL;
+static QTSS_StreamRef sErrorLogStream = NULL;
+
+QTSS_Error _stublibrary_main(void* inPrivateArgs, QTSS_DispatchFuncPtr inDispatchFunc)
+{
+ QTSS_PrivateArgsPtr theArgs = (QTSS_PrivateArgsPtr)inPrivateArgs;
+
+ // Setup
+
+ sCallbacks = theArgs->inCallbacks;
+ sErrorLogStream = theArgs->inErrorLogStream;
+
+ // Send requested information back to the server
+
+ theArgs->outStubLibraryVersion = QTSS_API_VERSION;
+ theArgs->outDispatchFunction = inDispatchFunc;
+
+ return QTSS_NoErr;
+}
+
+// STUB FUNCTION DEFINITIONS
+
+void* QTSS_New(FourCharCode inMemoryIdentifier, UInt32 inSize)
+{
+ return (void *) ((QTSS_CallbackPtrProcPtr) sCallbacks->addr [kNewCallback]) (inMemoryIdentifier, inSize);
+}
+
+void QTSS_Delete(void* inMemory)
+{
+ (sCallbacks->addr [kDeleteCallback]) (inMemory);
+}
+
+SInt64 QTSS_Milliseconds(void)
+{
+ SInt64 outMilliseconds = 0;
+ (sCallbacks->addr [kMillisecondsCallback]) (&outMilliseconds);
+ return outMilliseconds;
+}
+
+time_t QTSS_MilliSecsTo1970Secs(SInt64 inQTSS_MilliSeconds)
+{
+ time_t outSeconds = 0;
+ (sCallbacks->addr [kConvertToUnixTimeCallback]) (&inQTSS_MilliSeconds, &outSeconds);
+ return outSeconds;
+}
+
+// STARTUP ROUTINES
+
+QTSS_Error QTSS_AddRole(QTSS_Role inRole)
+{
+ return (sCallbacks->addr [kAddRoleCallback]) (inRole);
+}
+
+// DICTIONARY ROUTINES
+
+QTSS_Error QTSS_CreateObjectType(QTSS_ObjectType* outType)
+{
+ return (sCallbacks->addr [kCreateObjectTypeCallback]) (outType);
+}
+
+QTSS_Error QTSS_AddAttribute(QTSS_ObjectType inType, const char* inTag, void* inUnused)
+{
+ return (sCallbacks->addr [kAddAttributeCallback]) (inType, inTag, inUnused);
+}
+
+QTSS_Error QTSS_AddStaticAttribute(QTSS_ObjectType inObjectType, char* inAttrName, void* inUnused, QTSS_AttrDataType inAttrDataType)
+{
+ return (sCallbacks->addr [kAddStaticAttributeCallback]) (inObjectType, inAttrName, inUnused, inAttrDataType);
+}
+
+QTSS_Error QTSS_AddInstanceAttribute(QTSS_Object inObject, char* inAttrName, void* inUnused, QTSS_AttrDataType inAttrDataType)
+{
+ return (sCallbacks->addr [kAddInstanceAttributeCallback]) (inObject, inAttrName, inUnused, inAttrDataType);
+}
+
+QTSS_Error QTSS_RemoveInstanceAttribute(QTSS_Object inObject, QTSS_AttributeID inID)
+{
+ return (sCallbacks->addr [kRemoveInstanceAttributeCallback]) (inObject, inID);
+}
+
+QTSS_Error QTSS_IDForAttr(QTSS_ObjectType inType, const char* inTag, QTSS_AttributeID* outID)
+{
+ return (sCallbacks->addr [kIDForTagCallback]) (inType, inTag, outID);
+}
+
+QTSS_Error QTSS_GetAttrInfoByIndex(QTSS_Object inObject, UInt32 inIndex, QTSS_Object* outAttrInfoObject)
+{
+ return (sCallbacks->addr [kGetAttrInfoByIndexCallback]) (inObject, inIndex, outAttrInfoObject);
+}
+
+QTSS_Error QTSS_GetAttrInfoByID(QTSS_Object inObject, QTSS_AttributeID inAttrID, QTSS_Object* outAttrInfoObject)
+{
+ return (sCallbacks->addr [kGetAttrInfoByIDCallback]) (inObject, inAttrID, outAttrInfoObject);
+}
+
+QTSS_Error QTSS_GetAttrInfoByName(QTSS_Object inObject, char* inAttrName, QTSS_Object* outAttrInfoObject)
+{
+ return (sCallbacks->addr [kGetAttrInfoByNameCallback]) (inObject, inAttrName, outAttrInfoObject);
+}
+
+QTSS_Error QTSS_GetValuePtr (QTSS_Object inDictionary, QTSS_AttributeID inID, UInt32 inIndex, void** outBuffer, UInt32* outLen)
+{
+ return (sCallbacks->addr [kGetAttributePtrByIDCallback]) (inDictionary, inID, inIndex, outBuffer, outLen);
+}
+
+QTSS_Error QTSS_GetValue (QTSS_Object inDictionary, QTSS_AttributeID inID, UInt32 inIndex, void* ioBuffer, UInt32* ioLen)
+{
+ return (sCallbacks->addr [kGetAttributeByIDCallback]) (inDictionary, inID, inIndex, ioBuffer, ioLen);
+}
+
+QTSS_Error QTSS_GetValueAsString (QTSS_Object inObject, QTSS_AttributeID inID, UInt32 inIndex, char** outString)
+{
+ return (sCallbacks->addr [kGetValueAsStringCallback]) (inObject, inID, inIndex, outString);
+}
+
+QTSS_Error QTSS_TypeStringToType(const char* inTypeString, QTSS_AttrDataType* outType)
+{
+ return (sCallbacks->addr [kTypeStringToTypeCallback]) (inTypeString, outType);
+}
+
+QTSS_Error QTSS_TypeToTypeString(const QTSS_AttrDataType inType, char** outTypeString)
+{
+ return (sCallbacks->addr [kTypeToTypeStringCallback]) (inType, outTypeString);
+}
+
+QTSS_Error QTSS_StringToValue(const char* inValueAsString, const QTSS_AttrDataType inType, void* ioBuffer, UInt32* ioBufSize)
+{
+ return (sCallbacks->addr [kStringToValueCallback]) (inValueAsString, inType, ioBuffer, ioBufSize);
+}
+
+QTSS_Error QTSS_ValueToString(const void* inValue, const UInt32 inValueLen, const QTSS_AttrDataType inType, char** outString)
+{
+ return (sCallbacks->addr [kValueToStringCallback]) (inValue, inValueLen, inType, outString);
+}
+
+QTSS_Error QTSS_SetValue (QTSS_Object inDictionary, QTSS_AttributeID inID,UInt32 inIndex, const void* inBuffer, UInt32 inLen)
+{
+ return (sCallbacks->addr [kSetAttributeByIDCallback]) (inDictionary, inID, inIndex, inBuffer, inLen);
+}
+
+QTSS_Error QTSS_SetValuePtr (QTSS_Object inDictionary, QTSS_AttributeID inID, const void* inBuffer, UInt32 inLen)
+{
+ return (sCallbacks->addr [kSetAttributePtrCallback]) (inDictionary, inID, inBuffer, inLen);
+}
+
+QTSS_Error QTSS_CreateObjectValue (QTSS_Object inDictionary, QTSS_AttributeID inID, QTSS_ObjectType inType, UInt32* outIndex, QTSS_Object* outCreatedObject)
+{
+ return (sCallbacks->addr [kCreateObjectValueCallback]) (inDictionary, inID, inType, outIndex, outCreatedObject);
+}
+
+QTSS_Error QTSS_GetNumValues (QTSS_Object inObject, QTSS_AttributeID inID, UInt32* outNumValues)
+{
+ return (sCallbacks->addr [kGetNumValuesCallback]) (inObject, inID, outNumValues);
+}
+
+QTSS_Error QTSS_GetNumAttributes (QTSS_Object inObject, UInt32* outNumValues)
+{
+ return (sCallbacks->addr [kGetNumAttributesCallback]) (inObject, outNumValues);
+}
+
+QTSS_Error QTSS_RemoveValue (QTSS_Object inObject, QTSS_AttributeID inID, UInt32 inIndex)
+{
+ return (sCallbacks->addr [kRemoveValueCallback]) (inObject, inID, inIndex);
+}
+
+// STREAM ROUTINES
+
+QTSS_Error QTSS_Write(QTSS_StreamRef inStream, const void* inBuffer, UInt32 inLen, UInt32* outLenWritten, UInt32 inFlags)
+{
+ return (sCallbacks->addr [kWriteCallback]) (inStream, inBuffer, inLen, outLenWritten, inFlags);
+}
+
+QTSS_Error QTSS_WriteV(QTSS_StreamRef inStream, iovec* inVec, UInt32 inNumVectors, UInt32 inTotalLength, UInt32* outLenWritten)
+{
+ return (sCallbacks->addr [kWriteVCallback]) (inStream, inVec, inNumVectors, inTotalLength, outLenWritten);
+}
+
+QTSS_Error QTSS_Flush(QTSS_StreamRef inStream)
+{
+ return (sCallbacks->addr [kFlushCallback]) (inStream);
+}
+
+QTSS_Error QTSS_Read(QTSS_StreamRef inRef, void* ioBuffer, UInt32 inBufLen, UInt32* outLengthRead)
+{
+ return (sCallbacks->addr [kReadCallback]) (inRef, ioBuffer, inBufLen, outLengthRead);
+}
+
+QTSS_Error QTSS_Seek(QTSS_StreamRef inRef, UInt64 inNewPosition)
+{
+ return (sCallbacks->addr [kSeekCallback]) (inRef, inNewPosition);
+}
+
+QTSS_Error QTSS_Advise(QTSS_StreamRef inRef, UInt64 inPosition, UInt32 inAdviseSize)
+{
+ return (sCallbacks->addr [kAdviseCallback]) (inRef, inPosition, inAdviseSize);
+}
+
+
+
+// SERVICE ROUTINES
+
+QTSS_Error QTSS_AddService(const char* inServiceName, QTSS_ServiceFunctionPtr inFunctionPtr)
+{
+ return (sCallbacks->addr [kAddServiceCallback]) (inServiceName, inFunctionPtr);
+}
+
+QTSS_Error QTSS_IDForService(const char* inTag, QTSS_ServiceID* outID)
+{
+ return (sCallbacks->addr [kIDForServiceCallback]) (inTag, outID);
+}
+
+QTSS_Error QTSS_DoService(QTSS_ServiceID inID, QTSS_ServiceFunctionArgsPtr inArgs)
+{
+ return (sCallbacks->addr [kDoServiceCallback]) (inID, inArgs);
+}
+
+// RTSP ROUTINES
+
+QTSS_Error QTSS_SendRTSPHeaders(QTSS_RTSPRequestObject inRef)
+{
+ return (sCallbacks->addr [kSendRTSPHeadersCallback]) (inRef);
+}
+
+QTSS_Error QTSS_AppendRTSPHeader(QTSS_RTSPRequestObject inRef, QTSS_RTSPHeader inHeader, const char* inValue, UInt32 inValueLen)
+{
+ return (sCallbacks->addr [kAppendRTSPHeadersCallback]) (inRef, inHeader, inValue, inValueLen);
+}
+
+QTSS_Error QTSS_SendStandardRTSPResponse(QTSS_RTSPRequestObject inRTSPRequest, QTSS_Object inRTPInfo, UInt32 inFlags)
+{
+ return (sCallbacks->addr [kSendStandardRTSPCallback]) (inRTSPRequest, inRTPInfo, inFlags);
+}
+
+// RTP ROUTINES
+
+QTSS_Error QTSS_AddRTPStream(QTSS_ClientSessionObject inClientSession, QTSS_RTSPRequestObject inRTSPRequest, QTSS_RTPStreamObject* outStream, QTSS_AddStreamFlags inFlags)
+{
+ return (sCallbacks->addr [kAddRTPStreamCallback]) (inClientSession, inRTSPRequest, outStream, inFlags);
+}
+
+QTSS_Error QTSS_Play(QTSS_ClientSessionObject inClientSession, QTSS_RTSPRequestObject inRTSPRequest, QTSS_PlayFlags inPlayFlags)
+{
+ return (sCallbacks->addr [kPlayCallback]) (inClientSession, inRTSPRequest, inPlayFlags);
+}
+
+QTSS_Error QTSS_Pause(QTSS_ClientSessionObject inClientSession)
+{
+ return (sCallbacks->addr [kPauseCallback]) (inClientSession);
+}
+
+QTSS_Error QTSS_Teardown(QTSS_ClientSessionObject inClientSession)
+{
+ return (sCallbacks->addr [kTeardownCallback]) (inClientSession);
+}
+
+QTSS_Error QTSS_RefreshTimeOut(QTSS_ClientSessionObject inClientSession)
+{
+ return (sCallbacks->addr [kRefreshTimeOutCallback]) (inClientSession);
+}
+
+
+// FILE SYSTEM ROUTINES
+
+QTSS_Error QTSS_OpenFileObject(char* inPath, QTSS_OpenFileFlags inFlags, QTSS_Object* outFileObject)
+{
+ return (sCallbacks->addr [kOpenFileObjectCallback]) (inPath, inFlags, outFileObject);
+}
+
+QTSS_Error QTSS_CloseFileObject(QTSS_Object inFileObject)
+{
+ return (sCallbacks->addr [kCloseFileObjectCallback]) (inFileObject);
+}
+
+// SOCKET ROUTINES
+
+QTSS_Error QTSS_CreateStreamFromSocket(int inFileDesc, QTSS_StreamRef* outStream)
+{
+ return (sCallbacks->addr [kCreateSocketStreamCallback]) (inFileDesc, outStream);
+}
+
+QTSS_Error QTSS_DestroySocketStream(QTSS_StreamRef inStream)
+{
+ return (sCallbacks->addr [kDestroySocketStreamCallback]) (inStream);
+
+}
+
+// ASYNC I/O STREAM ROUTINES
+
+QTSS_Error QTSS_RequestEvent(QTSS_StreamRef inStream, QTSS_EventType inEventMask)
+{
+ return (sCallbacks->addr [kRequestEventCallback]) (inStream, inEventMask);
+}
+
+QTSS_Error QTSS_SignalStream(QTSS_StreamRef inStream)
+{
+ return (sCallbacks->addr [kSignalStreamCallback]) (inStream);
+}
+
+QTSS_Error QTSS_SetIdleTimer(SInt64 inIdleMsec)
+{
+ return (sCallbacks->addr [kSetIdleTimerCallback]) (inIdleMsec);
+}
+
+QTSS_Error QTSS_SetIntervalRoleTimer(SInt64 inIdleMsec)
+{
+ return (sCallbacks->addr [kSetIntervalRoleTimerCallback]) (inIdleMsec);
+}
+
+QTSS_Error QTSS_RequestGlobalLock()
+{
+ return (sCallbacks->addr [kRequestGlobalLockCallback]) ();
+}
+
+// SYNCH GLOBAL MULTIPLE READERS/SINGLE WRITER ROUTINES
+
+Bool16 QTSS_IsGlobalLocked()
+{
+ return (Bool16) (sCallbacks->addr [kIsGlobalLockedCallback]) ();
+}
+
+QTSS_Error QTSS_GlobalUnLock()
+{
+ return (sCallbacks->addr [kUnlockGlobalLock]) ();
+}
+
+QTSS_Error QTSS_LockObject(QTSS_Object inObject)
+{
+ return (sCallbacks->addr [kLockObjectCallback]) (inObject);
+}
+
+QTSS_Error QTSS_UnlockObject(QTSS_Object inObject)
+{
+ return (sCallbacks->addr [kUnlockObjectCallback]) (inObject);
+}
+
+// AUTHENTICATION AND AUTHORIZATION ROUTINE
+QTSS_Error QTSS_Authenticate( const char* inAuthUserName,
+ const char* inAuthResourceLocalPath,
+ const char* inAuthMoviesDir,
+ QTSS_ActionFlags inAuthRequestAction,
+ QTSS_AuthScheme inAuthScheme,
+ QTSS_RTSPRequestObject ioAuthRequestObject)
+{
+ return (sCallbacks->addr [kAuthenticateCallback]) (inAuthUserName, inAuthResourceLocalPath, inAuthMoviesDir, inAuthRequestAction, inAuthScheme, ioAuthRequestObject);
+}
+
+QTSS_Error QTSS_Authorize(QTSS_RTSPRequestObject inAuthRequestObject, char** outAuthRealm, Bool16* outAuthUserAllowed)
+{
+ return (sCallbacks->addr [kAuthorizeCallback]) (inAuthRequestObject, outAuthRealm, outAuthUserAllowed);
+}
+
+void QTSS_LockStdLib()
+{
+ (sCallbacks->addr [kLockStdLibCallback]) ();
+}
+
+void QTSS_UnlockStdLib()
+{
+ (sCallbacks->addr [kUnlockStdLibCallback]) ();
+}
+
diff --git a/APIStubLib/QTSS_Private.h b/APIStubLib/QTSS_Private.h
new file mode 100644
index 0000000..2385406
--- /dev/null
+++ b/APIStubLib/QTSS_Private.h
@@ -0,0 +1,158 @@
+/*
+ *
+ * @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: QTSS_Private.h
+
+ Contains: Implementation-specific structures and typedefs used by the
+ implementation of QTSS API in the Darwin Streaming Server
+
+
+
+*/
+
+
+#ifndef QTSS_PRIVATE_H
+#define QTSS_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "OSHeaders.h"
+#include "QTSS.h"
+
+class QTSSModule;
+class Task;
+
+typedef QTSS_Error (*QTSS_CallbackProcPtr)(...);
+typedef void* (*QTSS_CallbackPtrProcPtr)(...);
+
+enum
+{
+ // Indexes for each callback routine. Addresses of the callback routines get
+ // placed in an array.
+ // IMPORTANT: When adding new callbacks, add only to the end of the list and increment the
+ // kLastCallback value. Inserting or changing the index order will break dynamic modules
+ // built with another release.
+
+ kNewCallback = 0,
+ kDeleteCallback = 1,
+ kMillisecondsCallback = 2,
+ kConvertToUnixTimeCallback = 3,
+ kAddRoleCallback = 4,
+ kAddAttributeCallback = 5,
+ kIDForTagCallback = 6,
+ kGetAttributePtrByIDCallback = 7,
+ kGetAttributeByIDCallback = 8,
+ kSetAttributeByIDCallback = 9,
+ kWriteCallback = 10,
+ kWriteVCallback = 11,
+ kFlushCallback = 12,
+ kAddServiceCallback = 13,
+ kIDForServiceCallback = 14,
+ kDoServiceCallback = 15,
+ kSendRTSPHeadersCallback = 16,
+ kAppendRTSPHeadersCallback = 17,
+ kSendStandardRTSPCallback = 18,
+ kAddRTPStreamCallback = 19,
+ kPlayCallback = 20,
+ kPauseCallback = 21,
+ kTeardownCallback = 22,
+ kRequestEventCallback = 23,
+ kSetIdleTimerCallback = 24,
+ kOpenFileObjectCallback = 25,
+ kCloseFileObjectCallback = 26,
+ kReadCallback = 27,
+ kSeekCallback = 28,
+ kAdviseCallback = 29,
+ kGetNumValuesCallback = 30,
+ kGetNumAttributesCallback = 31,
+ kSignalStreamCallback = 32,
+ kCreateSocketStreamCallback = 33,
+ kDestroySocketStreamCallback = 34,
+ kAddStaticAttributeCallback = 35,
+ kAddInstanceAttributeCallback = 36,
+ kRemoveInstanceAttributeCallback= 37,
+ kGetAttrInfoByIndexCallback = 38,
+ kGetAttrInfoByNameCallback = 39,
+ kGetAttrInfoByIDCallback = 40,
+ kGetValueAsStringCallback = 41,
+ kTypeToTypeStringCallback = 42,
+ kTypeStringToTypeCallback = 43,
+ kStringToValueCallback = 44,
+ kValueToStringCallback = 45,
+ kRemoveValueCallback = 46,
+ kRequestGlobalLockCallback = 47,
+ kIsGlobalLockedCallback = 48,
+ kUnlockGlobalLock = 49,
+ kAuthenticateCallback = 50,
+ kAuthorizeCallback = 51,
+ kRefreshTimeOutCallback = 52,
+ kCreateObjectValueCallback = 53,
+ kCreateObjectTypeCallback = 54,
+ kLockObjectCallback = 55,
+ kUnlockObjectCallback = 56,
+ kSetAttributePtrCallback = 57,
+ kSetIntervalRoleTimerCallback = 58,
+ kLockStdLibCallback = 59,
+ kUnlockStdLibCallback = 60,
+ kLastCallback = 61
+};
+
+typedef struct {
+ // Callback function pointer array
+ QTSS_CallbackProcPtr addr [kLastCallback];
+} QTSS_Callbacks, *QTSS_CallbacksPtr;
+
+typedef struct
+{
+ UInt32 inServerAPIVersion;
+ QTSS_CallbacksPtr inCallbacks;
+ QTSS_StreamRef inErrorLogStream;
+ UInt32 outStubLibraryVersion;
+ QTSS_DispatchFuncPtr outDispatchFunction;
+
+} QTSS_PrivateArgs, *QTSS_PrivateArgsPtr;
+
+typedef struct
+{
+ QTSSModule* curModule; // this structure is setup in each thread
+ QTSS_Role curRole; // before invoking a module in a role. Sometimes
+ Task* curTask; // this info. helps callback implementation
+ Bool16 eventRequested;
+ Bool16 globalLockRequested; // request event with global lock.
+ Bool16 isGlobalLocked;
+ SInt64 idleTime; // If a module has requested idle time.
+
+} QTSS_ModuleState, *QTSS_ModuleStatePtr;
+
+QTSS_StreamRef GetErrorLogStream();
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/APPLE_LICENSE b/APPLE_LICENSE
new file mode 100644
index 0000000..fe81a60
--- /dev/null
+++ b/APPLE_LICENSE
@@ -0,0 +1,367 @@
+APPLE PUBLIC SOURCE LICENSE
+Version 2.0 - August 6, 2003
+
+Please read this License carefully before downloading this software.
+By downloading or using this software, you are agreeing to be bound by
+the terms of this License. If you do not or cannot agree to the terms
+of this License, please do not download or use the software.
+
+1. General; Definitions. This License applies to any program or other
+work which Apple Computer, Inc. ("Apple") makes publicly available and
+which contains a notice placed by Apple identifying such program or
+work as "Original Code" and stating that it is subject to the terms of
+this Apple Public Source License version 2.0 ("License"). As used in
+this License:
+
+1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is
+the grantor of rights, (i) claims of patents that are now or hereafter
+acquired, owned by or assigned to Apple and (ii) that cover subject
+matter contained in the Original Code, but only to the extent
+necessary to use, reproduce and/or distribute the Original Code
+without infringement; and (b) in the case where You are the grantor of
+rights, (i) claims of patents that are now or hereafter acquired,
+owned by or assigned to You and (ii) that cover subject matter in Your
+Modifications, taken alone or in combination with Original Code.
+
+1.2 "Contributor" means any person or entity that creates or
+contributes to the creation of Modifications.
+
+1.3 "Covered Code" means the Original Code, Modifications, the
+combination of Original Code and any Modifications, and/or any
+respective portions thereof.
+
+1.4 "Externally Deploy" means: (a) to sublicense, distribute or
+otherwise make Covered Code available, directly or indirectly, to
+anyone other than You; and/or (b) to use Covered Code, alone or as
+part of a Larger Work, in any way to provide a service, including but
+not limited to delivery of content, through electronic communication
+with a client other than You.
+
+1.5 "Larger Work" means a work which combines Covered Code or portions
+thereof with code not governed by the terms of this License.
+
+1.6 "Modifications" mean any addition to, deletion from, and/or change
+to, the substance and/or structure of the Original Code, any previous
+Modifications, the combination of Original Code and any previous
+Modifications, and/or any respective portions thereof. When code is
+released as a series of files, a Modification is: (a) any addition to
+or deletion from the contents of a file containing Covered Code;
+and/or (b) any new file or other representation of computer program
+statements that contains any part of Covered Code.
+
+1.7 "Original Code" means (a) the Source Code of a program or other
+work as originally made available by Apple under this License,
+including the Source Code of any updates or upgrades to such programs
+or works made available by Apple under this License, and that has been
+expressly identified by Apple as such in the header file(s) of such
+work; and (b) the object code compiled from such Source Code and
+originally made available by Apple under this License.
+
+1.8 "Source Code" means the human readable form of a program or other
+work that is suitable for making modifications to it, including all
+modules it contains, plus any associated interface definition files,
+scripts used to control compilation and installation of an executable
+(object code).
+
+1.9 "You" or "Your" means an individual or a legal entity exercising
+rights under this License. For legal entities, "You" or "Your"
+includes any entity which controls, is controlled by, or is under
+common control with, You, where "control" means (a) the power, direct
+or indirect, to cause the direction or management of such entity,
+whether by contract or otherwise, or (b) ownership of fifty percent
+(50%) or more of the outstanding shares or beneficial ownership of
+such entity.
+
+2. Permitted Uses; Conditions & Restrictions. Subject to the terms
+and conditions of this License, Apple hereby grants You, effective on
+the date You accept this License and download the Original Code, a
+world-wide, royalty-free, non-exclusive license, to the extent of
+Apple's Applicable Patent Rights and copyrights covering the Original
+Code, to do the following:
+
+2.1 Unmodified Code. You may use, reproduce, display, perform,
+internally distribute within Your organization, and Externally Deploy
+verbatim, unmodified copies of the Original Code, for commercial or
+non-commercial purposes, provided that in each instance:
+
+(a) You must retain and reproduce in all copies of Original Code the
+copyright and other proprietary notices and disclaimers of Apple as
+they appear in the Original Code, and keep intact all notices in the
+Original Code that refer to this License; and
+
+(b) You must include a copy of this License with every copy of Source
+Code of Covered Code and documentation You distribute or Externally
+Deploy, and You may not offer or impose any terms on such Source Code
+that alter or restrict this License or the recipients' rights
+hereunder, except as permitted under Section 6.
+
+2.2 Modified Code. You may modify Covered Code and use, reproduce,
+display, perform, internally distribute within Your organization, and
+Externally Deploy Your Modifications and Covered Code, for commercial
+or non-commercial purposes, provided that in each instance You also
+meet all of these conditions:
+
+(a) You must satisfy all the conditions of Section 2.1 with respect to
+the Source Code of the Covered Code;
+
+(b) You must duplicate, to the extent it does not already exist, the
+notice in Exhibit A in each file of the Source Code of all Your
+Modifications, and cause the modified files to carry prominent notices
+stating that You changed the files and the date of any change; and
+
+(c) If You Externally Deploy Your Modifications, You must make
+Source Code of all Your Externally Deployed Modifications either
+available to those to whom You have Externally Deployed Your
+Modifications, or publicly available. Source Code of Your Externally
+Deployed Modifications must be released under the terms set forth in
+this License, including the license grants set forth in Section 3
+below, for as long as you Externally Deploy the Covered Code or twelve
+(12) months from the date of initial External Deployment, whichever is
+longer. You should preferably distribute the Source Code of Your
+Externally Deployed Modifications electronically (e.g. download from a
+web site).
+
+2.3 Distribution of Executable Versions. In addition, if You
+Externally Deploy Covered Code (Original Code and/or Modifications) in
+object code, executable form only, You must include a prominent
+notice, in the code itself as well as in related documentation,
+stating that Source Code of the Covered Code is available under the
+terms of this License with information on how and where to obtain such
+Source Code.
+
+2.4 Third Party Rights. You expressly acknowledge and agree that
+although Apple and each Contributor grants the licenses to their
+respective portions of the Covered Code set forth herein, no
+assurances are provided by Apple or any Contributor that the Covered
+Code does not infringe the patent or other intellectual property
+rights of any other entity. Apple and each Contributor disclaim any
+liability to You for claims brought by any other entity based on
+infringement of intellectual property rights or otherwise. As a
+condition to exercising the rights and licenses granted hereunder, You
+hereby assume sole responsibility to secure any other intellectual
+property rights needed, if any. For example, if a third party patent
+license is required to allow You to distribute the Covered Code, it is
+Your responsibility to acquire that license before distributing the
+Covered Code.
+
+3. Your Grants. In consideration of, and as a condition to, the
+licenses granted to You under this License, You hereby grant to any
+person or entity receiving or distributing Covered Code under this
+License a non-exclusive, royalty-free, perpetual, irrevocable license,
+under Your Applicable Patent Rights and other intellectual property
+rights (other than patent) owned or controlled by You, to use,
+reproduce, display, perform, modify, sublicense, distribute and
+Externally Deploy Your Modifications of the same scope and extent as
+Apple's licenses under Sections 2.1 and 2.2 above.
+
+4. Larger Works. You may create a Larger Work by combining Covered
+Code with other code not governed by the terms of this License and
+distribute the Larger Work as a single product. In each such instance,
+You must make sure the requirements of this License are fulfilled for
+the Covered Code or any portion thereof.
+
+5. Limitations on Patent License. Except as expressly stated in
+Section 2, no other patent rights, express or implied, are granted by
+Apple herein. Modifications and/or Larger Works may require additional
+patent licenses from Apple which Apple may grant in its sole
+discretion.
+
+6. Additional Terms. You may choose to offer, and to charge a fee for,
+warranty, support, indemnity or liability obligations and/or other
+rights consistent with the scope of the license granted herein
+("Additional Terms") to one or more recipients of Covered Code.
+However, You may do so only on Your own behalf and as Your sole
+responsibility, and not on behalf of Apple or any Contributor. You
+must obtain the recipient's agreement that any such Additional Terms
+are offered by You alone, and You hereby agree to indemnify, defend
+and hold Apple and every Contributor harmless for any liability
+incurred by or claims asserted against Apple or such Contributor by
+reason of any such Additional Terms.
+
+7. Versions of the License. Apple may publish revised and/or new
+versions of this License from time to time. Each version will be given
+a distinguishing version number. Once Original Code has been published
+under a particular version of this License, You may continue to use it
+under the terms of that version. You may also choose to use such
+Original Code under the terms of any subsequent version of this
+License published by Apple. No one other than Apple has the right to
+modify the terms applicable to Covered Code created under this
+License.
+
+8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in
+part pre-release, untested, or not fully tested works. The Covered
+Code may contain errors that could cause failures or loss of data, and
+may be incomplete or contain inaccuracies. You expressly acknowledge
+and agree that use of the Covered Code, or any portion thereof, is at
+Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND
+WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND
+APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE
+PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM
+ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF
+MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR
+PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD
+PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST
+INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE
+FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS,
+THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR
+ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO
+ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE
+AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY.
+You acknowledge that the Covered Code is not intended for use in the
+operation of nuclear facilities, aircraft navigation, communication
+systems, or air traffic control machines in which case the failure of
+the Covered Code could lead to death, personal injury, or severe
+physical or environmental damage.
+
+9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO
+EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL,
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING
+TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR
+ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY,
+TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF
+APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY
+REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF
+INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY
+TO YOU. In no event shall Apple's total liability to You for all
+damages (other than as may be required by applicable law) under this
+License exceed the amount of fifty dollars ($50.00).
+
+10. Trademarks. This License does not grant any rights to use the
+trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS",
+"QuickTime", "QuickTime Streaming Server" or any other trademarks,
+service marks, logos or trade names belonging to Apple (collectively
+"Apple Marks") or to any trademark, service mark, logo or trade name
+belonging to any Contributor. You agree not to use any Apple Marks in
+or as part of the name of products derived from the Original Code or
+to endorse or promote products derived from the Original Code other
+than as expressly permitted by and in strict compliance at all times
+with Apple's third party trademark usage guidelines which are posted
+at http://www.apple.com/legal/guidelinesfor3rdparties.html.
+
+11. Ownership. Subject to the licenses granted under this License,
+each Contributor retains all rights, title and interest in and to any
+Modifications made by such Contributor. Apple retains all rights,
+title and interest in and to the Original Code and any Modifications
+made by or on behalf of Apple ("Apple Modifications"), and such Apple
+Modifications will not be automatically subject to this License. Apple
+may, at its sole discretion, choose to license such Apple
+Modifications under this License, or on different terms from those
+contained in this License or may choose not to license them at all.
+
+12. Termination.
+
+12.1 Termination. This License and the rights granted hereunder will
+terminate:
+
+(a) automatically without notice from Apple if You fail to comply with
+any term(s) of this License and fail to cure such breach within 30
+days of becoming aware of such breach;
+
+(b) immediately in the event of the circumstances described in Section
+13.5(b); or
+
+(c) automatically without notice from Apple if You, at any time during
+the term of this License, commence an action for patent infringement
+against Apple; provided that Apple did not first commence
+an action for patent infringement against You in that instance.
+
+12.2 Effect of Termination. Upon termination, You agree to immediately
+stop any further use, reproduction, modification, sublicensing and
+distribution of the Covered Code. All sublicenses to the Covered Code
+which have been properly granted prior to termination shall survive
+any termination of this License. Provisions which, by their nature,
+should remain in effect beyond the termination of this License shall
+survive, including but not limited to Sections 3, 5, 8, 9, 10, 11,
+12.2 and 13. No party will be liable to any other for compensation,
+indemnity or damages of any sort solely as a result of terminating
+this License in accordance with its terms, and termination of this
+License will be without prejudice to any other right or remedy of
+any party.
+
+13. Miscellaneous.
+
+13.1 Government End Users. The Covered Code is a "commercial item" as
+defined in FAR 2.101. Government software and technical data rights in
+the Covered Code include only those rights customarily provided to the
+public as defined in this License. This customary commercial license
+in technical data and software is provided in accordance with FAR
+12.211 (Technical Data) and 12.212 (Computer Software) and, for
+Department of Defense purchases, DFAR 252.227-7015 (Technical Data --
+Commercial Items) and 227.7202-3 (Rights in Commercial Computer
+Software or Computer Software Documentation). Accordingly, all U.S.
+Government End Users acquire Covered Code with only those rights set
+forth herein.
+
+13.2 Relationship of Parties. This License will not be construed as
+creating an agency, partnership, joint venture or any other form of
+legal association between or among You, Apple or any Contributor, and
+You will not represent to the contrary, whether expressly, by
+implication, appearance or otherwise.
+
+13.3 Independent Development. Nothing in this License will impair
+Apple's right to acquire, license, develop, have others develop for
+it, market and/or distribute technology or products that perform the
+same or similar functions as, or otherwise compete with,
+Modifications, Larger Works, technology or products that You may
+develop, produce, market or distribute.
+
+13.4 Waiver; Construction. Failure by Apple or any Contributor to
+enforce any provision of this License will not be deemed a waiver of
+future enforcement of that or any other provision. Any law or
+regulation which provides that the language of a contract shall be
+construed against the drafter will not apply to this License.
+
+13.5 Severability. (a) If for any reason a court of competent
+jurisdiction finds any provision of this License, or portion thereof,
+to be unenforceable, that provision of the License will be enforced to
+the maximum extent permissible so as to effect the economic benefits
+and intent of the parties, and the remainder of this License will
+continue in full force and effect. (b) Notwithstanding the foregoing,
+if applicable law prohibits or restricts You from fully and/or
+specifically complying with Sections 2 and/or 3 or prevents the
+enforceability of either of those Sections, this License will
+immediately terminate and You must immediately discontinue any use of
+the Covered Code and destroy all copies of it that are in your
+possession or control.
+
+13.6 Dispute Resolution. Any litigation or other dispute resolution
+between You and Apple relating to this License shall take place in the
+Northern District of California, and You and Apple hereby consent to
+the personal jurisdiction of, and venue in, the state and federal
+courts within that District with respect to this License. The
+application of the United Nations Convention on Contracts for the
+International Sale of Goods is expressly excluded.
+
+13.7 Entire Agreement; Governing Law. This License constitutes the
+entire agreement between the parties with respect to the subject
+matter hereof. This License shall be governed by the laws of the
+United States and the State of California, except that body of
+California law concerning conflicts of law.
+
+Where You are located in the province of Quebec, Canada, the following
+clause applies: The parties hereby confirm that they have requested
+that this License and all related documents be drafted in English. Les
+parties ont exige que le present contrat et tous les documents
+connexes soient rediges en anglais.
+
+EXHIBIT A.
+
+"Portions Copyright (c) 1999-2003 Apple Computer, 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."
diff --git a/AtomicLib/README b/AtomicLib/README
new file mode 100644
index 0000000..7dd7c39
--- /dev/null
+++ b/AtomicLib/README
@@ -0,0 +1,139 @@
+/* --- Version 4.0 --- 12-Feb-1999 --- */
+/*
+ * Include the following routines :
+ * atomic_or()
+ * scaledtimestamp()
+ *
+ * Updated the copyright.
+ */
+
+/*
+ * unsigned int atomic_or(unsigned int *area, unsigned int mask)
+ *
+ * Atomically or the mask into *area.
+ * Returns the old value.
+ */
+/*
+ * long long scaledtimestamp(double scale)
+ *
+ * Read the PPC timebase. Convert the time base value based on
+ * scale.
+ *
+ * Caveat: scale can not be 0, NaN, Inf. It's upto the caller
+ * to validate scale before calling this.
+ */
+
+/* --- Version 3.0 --- 23-Oct-1998 --- */
+/*
+ * Made the headers c++ friendly
+ * Build a static library with dynamic code generation.
+ * If you make a copy of the libatomic.a do not forget to run "ranlib".
+ */
+
+/* --- Version 2.0 --- 23-Oct-1998 --- */
+/*
+ * Added routines described in timestamp.h
+ * Test program for these is in hmi.c
+ */
+
+/* --- Version 1.0 --- 12-Oct-1998 --- */
+/*
+ * void spin_lock_init(spin_lock_t)
+ *
+ * Initialize a spin lock.
+ * These locks should be cache aligned and a multiple of cache size.
+ */
+
+/*
+ * void spin_lock_unlock(spin_lock_t)
+ *
+ * Unconditionally release lock.
+ */
+
+/*
+ * unsigned int spin_lock_lock(spin_lock_t)
+ *
+ * Try to acquire spin-lock. Return success (1).
+ */
+
+/*
+ * unsigned int spin_lock_bit(spin_lock_t, unsigned int bits)
+ *
+ * Try to acquire spin-lock. The second parameter is the bit mask to
+ * test and set. multiple bits may be set.
+ * Return success (1).
+ */
+
+/*
+ * unsigned int spin_unlock_bit(spin_lock_t, unsigned int bits)
+ *
+ * Release bit based spin-lock. The second parameter is the bit mask to
+ * clear. Multiple bits may be cleared.
+ */
+
+/*
+ * unsigned int spin_lock_try(spin_lock_t)
+ *
+ * Try to acquire spin-lock. Return success (1) or failure (0).
+ */
+
+/*
+ * unsigned int spin_lock_held(spin_lock_t)
+ *
+ * Return 1 if lock is held
+ * N.B. Racy, of course.
+ */
+
+/*
+ * unsigned int compare_and_store(unsigned int oval,
+ * unsigned int nval, unsigned int *area)
+ *
+ * Compare oval to area if equal, store nval, and return true
+ * else return false and no store
+ * This is an atomic operation
+ */
+
+/*
+ * unsigned int atomic_add(unsigned int *area, int val)
+ *
+ * Atomically add the second parameter to the first.
+ * Returns the result.
+ */
+
+/*
+ * unsigned int atomic_sub(unsigned int *area, int val)
+ *
+ * Atomically subtract the second parameter from the first.
+ * Returns the result.
+ */
+
+/*
+ * void queue_atomic(unsigned int * anchor,
+ * unsigned int * elem, unsigned int disp)
+ *
+ * Atomically inserts the element at the head of the list
+ * anchor is the pointer to the first element
+ * element is the pointer to the element to insert
+ * disp is the displacement into the element to the chain pointer
+ */
+
+/*
+ * void queue_atomic_list(unsigned int * anchor,
+ * unsigned int * first, unsigned int * last,
+ * unsigned int disp)
+ *
+ * Atomically inserts the list of elements at the head of the list
+ * anchor is the pointer to the first element
+ * first is the pointer to the first element to insert
+ * last is the pointer to the last element to insert
+ * disp is the displacement into the element to the chain pointer
+ */
+
+/*
+ * unsigned int *dequeue_atomic(unsigned int *anchor, unsigned int disp)
+ *
+ * Atomically removes the first element in a list and returns it.
+ * anchor is the pointer to the first element
+ * disp is the displacement into the element to the chain pointer
+ * Returns element if found, 0 if empty.
+ */
diff --git a/AtomicLib/atomic.h b/AtomicLib/atomic.h
new file mode 100644
index 0000000..42a9bc1
--- /dev/null
+++ b/AtomicLib/atomic.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @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@
+ */
+/*
+ *
+ * History:
+ * 11-Feb-1999 Umesh Vaishampayan (umeshv@apple.com)
+ * Added atomic_or().
+ *
+ * 26-Oct-1998 Umesh Vaishampayan (umeshv@apple.com)
+ * Made the header c++ friendly.
+ *
+ * 12-Oct-1998 Umesh Vaishampayan (umeshv@apple.com)
+ * Changed simple_ to spin_ so as to coexist with cthreads till
+ * the merge to the system framework.
+ *
+ * 8-Oct-1998 Umesh Vaishampayan (umeshv@apple.com)
+ * Created from the kernel code to be in a dynamic shared library.
+ * Kernel code created by: Bill Angell (angell@apple.com)
+ */
+
+#ifndef _ATOMIC_H_
+#define _ATOMIC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Locking routines */
+
+struct spin_lock { /* sizeof cache line */
+ unsigned int lock_data;
+ unsigned int pad[7];
+};
+
+typedef struct spin_lock *spin_lock_t;
+
+extern void spin_lock_init(spin_lock_t);
+
+extern void spin_lock_unlock(spin_lock_t);
+
+extern unsigned int spin_lock_lock(spin_lock_t);
+
+extern unsigned int spin_lock_bit(spin_lock_t, unsigned int bits);
+
+extern unsigned int spin_unlock_bit(spin_lock_t, unsigned int bits);
+
+extern unsigned int spin_lock_try(spin_lock_t);
+
+extern unsigned int spin_lock_held(spin_lock_t);
+
+/* Other atomic routines */
+
+extern unsigned int compare_and_store(unsigned int oval,
+ unsigned int nval, unsigned int *area);
+
+extern unsigned int atomic_add(unsigned int *area, int val);
+
+extern unsigned int atomic_or(unsigned int *area, unsigned int mask);
+
+extern unsigned int atomic_sub(unsigned int *area, int val);
+
+extern void queue_atomic(unsigned int *anchor,
+ unsigned int *elem, unsigned int disp);
+
+extern void queue_atomic_list(unsigned int *anchor,
+ unsigned int *first, unsigned int *last,
+ unsigned int disp);
+
+extern unsigned int *dequeue_atomic(unsigned int *anchor, unsigned int disp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ATOMIC_H_ */
diff --git a/AtomicLib/atomic_subr.s b/AtomicLib/atomic_subr.s
new file mode 100644
index 0000000..241d16c
--- /dev/null
+++ b/AtomicLib/atomic_subr.s
@@ -0,0 +1,304 @@
+/*
+ *
+ * @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: atomic_subr.s
+ *
+ * History:
+ * 11-Feb-1999 Umesh Vaishampayan (umeshv@apple.com)
+ * Added atomic_or().
+ *
+ * 12-Oct-1998 Umesh Vaishampayan (umeshv@apple.com)
+ * Changed simple_ to spin_ so as to coexist with cthreads till
+ * the merge to the system framework.
+ *
+ * 8-Oct-1998 Umesh Vaishampayan (umeshv@apple.com)
+ * Created from the kernel code to be in a dynamic shared library.
+ * Kernel code created by: Bill Angell (angell@apple.com)
+ */
+
+#include
+
+/*
+ * void spin_lock_init(spin_lock_t)
+ *
+ * Initialize a spin lock.
+ * These locks should be cache aligned and a multiple of cache size.
+ */
+LEAF(_spin_lock_init)
+ li r0, 0 /* set lock to free == 0 */
+ stw r0, 0(r3) /* Initialize the lock */
+ blr
+
+/*
+ * void spin_lock_unlock(spin_lock_t)
+ *
+ * Unconditionally release lock.
+ */
+LEAF(_spin_lock_unlock)
+ sync /* Flush writes done under lock */
+ li r0, 0 /* set lock to free */
+ stw r0, 0(r3)
+ blr
+
+/*
+ * unsigned int spin_lock_lock(spin_lock_t)
+ *
+ * Try to acquire spin-lock. Return success (1).
+ */
+LEAF(_spin_lock_lock)
+ mr r5,r3 /* Get the address of the lock */
+
+Lcktry:
+ lwarx r6,0,r5 /* Grab the lock value */
+ li r3,1 /* Set the return value */
+ mr. r6,r6 /* Is it locked? */
+ bne- Lcksniff /* Yeah, wait for it to clear... */
+ stwcx. r3,0,r5 /* Try to sieze that darn lock */
+ beq+ Lckgot /* We got it, yahoo... */
+ b Lcktry /* Try again if the store failed... */
+
+Lcksniff:
+ lwz r3,0(r5) /* Get that lock in here */
+ mr. r3,r3 /* Is it free yet? */
+ beq+ Lcktry /* Yeah, try for it again... */
+ b Lcksniff /* keep trying... */
+
+Lckgot:
+ isync /* Make sure we don't use a */
+ /* speculativily loaded value */
+ blr
+
+/*
+ * unsigned int spin_lock_bit(spin_lock_t, unsigned int bits)
+ *
+ * Try to acquire spin-lock. The second parameter is the bit mask to
+ * test and set. multiple bits may be set.
+ * Return success (1).
+ */
+LEAF(_spin_lock_bit)
+Lbittry:
+ lwarx r6,0,r3 /* Grab the lock value */
+ and. r0,r6,r4 /* See if any of the lock bits are on */
+ or r6,r6,r4 /* Turn on the lock bits */
+ bne- Lbitsniff /* Yeah, wait for it to clear... */
+ stwcx. r6,0,r3 /* Try to sieze that there durn lock */
+ beq+ Lbitgot /* We got it, yahoo... */
+ b Lbittry /* Try again if the store failed... */
+
+Lbitsniff:
+ lwz r6,0(r3) /* Get that lock in here */
+ and. r0,r6,r4 /* See if any of the lock bits are on */
+ beq+ Lbittry /* Yeah, try for it again... */
+ b Lbitsniff /* keep trying... */
+
+Lbitgot:
+ li r3,1 /* Set good return code */
+ isync /* Make sure we don't use a */
+ /* speculativily loaded value */
+ blr
+
+
+/*
+ * unsigned int spin_unlock_bit(spin_lock_t, unsigned int bits)
+ *
+ * Release bit based spin-lock. The second parameter is the bit mask to
+ * clear. Multiple bits may be cleared.
+ */
+LEAF(_spin_unlock_bit)
+Lubittry:
+ lwarx r0,0,r3 /* Grab the lock value */
+ andc r0,r0,r4 /* Clear the lock bits */
+ stwcx. r0,0,r3 /* Try to clear that there durn lock */
+ bne- Lubittry /* Try again, couldn't save it... */
+ blr /* Leave... */
+
+/*
+ * unsigned int spin_lock_try(spin_lock_t)
+ *
+ * Try to acquire spin-lock. Return success (1) or failure (0).
+ */
+LEAF(_spin_lock_try)
+ li r4, 1 /* value to be stored... 1==taken */
+
+L_lock_try_loop:
+ lwarx r5, 0,r3 /* Ld from addr of arg and reserve */
+ cmpwi r5, 0 /* TEST... */
+ bne- L_lock_try_failed /* branch if taken. Predict free */
+
+ stwcx. r4, 0,r3 /* And SET (if still reserved) */
+ bne- L_lock_try_loop /* If set failed, loop back */
+
+ isync
+ li r3,1 /* Set that the lock was free */
+ blr
+
+L_lock_try_failed:
+ li r3,0 /* FAILURE - lock was taken */
+ blr
+
+/*
+ * unsigned int spin_lock_held(spin_lock_t)
+ *
+ * Return 1 if lock is held
+ * N.B. Racy, of course.
+ */
+LEAF(_spin_lock_held)
+ isync /* Make sure we don't use a */
+ /* speculativily fetched lock */
+ lwz r3, 0(r3) /* Return value of lock */
+ blr
+
+/*
+ * unsigned int compare_and_store(unsigned int oval,
+ * unsigned int nval, unsigned int *area)
+ *
+ * Compare oval to area if equal, store nval, and return true
+ * else return false and no store
+ * This is an atomic operation
+ */
+LEAF(_compare_and_store)
+ mr r6,r3 /* Save the old value */
+
+Lcstry:
+ lwarx r9,0,r5 /* Grab the area value */
+ li r3,1 /* Assume it works */
+ cmplw cr0,r9,r6 /* Does it match the old value? */
+ bne- Lcsfail /* No, it must have changed... */
+ stwcx. r4,0,r5 /* Try to save the new value */
+ bne- Lcstry /* Didn't get it, try again... */
+ isync /* Just hold up prefetch */
+ blr /* Return... */
+
+Lcsfail:
+ li r3,0 /* Set failure */
+ blr
+
+/*
+ * unsigned int atomic_add(unsigned int *area, int val)
+ *
+ * Atomically add the second parameter to the first.
+ * Returns the result.
+ */
+LEAF(_atomic_add)
+ mr r6,r3 /* Save the area */
+
+Laddtry:
+ lwarx r3,0,r6 /* Grab the area value */
+ add r3,r3,r4 /* Add the value */
+ stwcx. r3,0,r6 /* Try to save the new value */
+ bne- Laddtry /* Didn't get it, try again... */
+ blr /* Return... */
+
+
+/*
+ * unsigned int atomic_or(unsigned int *area, unsigned int mask)
+ *
+ * Atomically or the mask into *area.
+ * Returns the old value.
+ */
+LEAF(_atomic_or)
+ mr r6,r3 /* Save the area */
+
+Lortry:
+ lwarx r3,0,r6 /* Grab the area value */
+ or r5,r3,r4 /* or the mask */
+ stwcx. r5,0,r6 /* Try to save the new value */
+ bne- Lortry /* Didn't get it, try again... */
+ blr /* Return the old value... */
+
+
+/*
+ * unsigned int atomic_sub(unsigned int *area, int val)
+ *
+ * Atomically subtract the second parameter from the first.
+ * Returns the result.
+ */
+LEAF(_atomic_sub)
+ mr r6,r3 /* Save the area */
+
+Lsubtry:
+ lwarx r3,0,r6 /* Grab the area value */
+ sub r3,r3,r4 /* Subtract the value */
+ stwcx. r3,0,r6 /* Try to save the new value */
+ bne- Lsubtry /* Didn't get it, try again... */
+ blr /* Return... */
+
+/*
+ * void queue_atomic(unsigned int * anchor,
+ * unsigned int * elem, unsigned int disp)
+ *
+ * Atomically inserts the element at the head of the list
+ * anchor is the pointer to the first element
+ * element is the pointer to the element to insert
+ * disp is the displacement into the element to the chain pointer
+ */
+LEAF(_queue_atomic)
+ mr r7,r4 /* Make end point the same as start */
+ mr r8,r5 /* Copy the displacement also */
+ b Lqueue_comm /* Join common code... */
+
+/*
+ * void queue_atomic_list(unsigned int * anchor,
+ * unsigned int * first, unsigned int * last,
+ * unsigned int disp)
+ *
+ * Atomically inserts the list of elements at the head of the list
+ * anchor is the pointer to the first element
+ * first is the pointer to the first element to insert
+ * last is the pointer to the last element to insert
+ * disp is the displacement into the element to the chain pointer
+ */
+LEAF(_queue_atomic_list)
+ mr r7,r5 /* Make end point the same as start */
+ mr r8,r6 /* Copy the displacement also */
+
+Lqueue_comm:
+ lwarx r9,0,r3 /* Pick up the anchor */
+ stwx r9,r8,r7 /* Chain that to the end of the new stuff */
+ stwcx. r4,0,r3 /* Try to chain into the front */
+ bne- Lqueue_comm /* Didn't make it, try again... */
+ blr /* Return... */
+
+/*
+ * unsigned int *dequeue_atomic(unsigned int *anchor, unsigned int disp)
+ *
+ * Atomically removes the first element in a list and returns it.
+ * anchor is the pointer to the first element
+ * disp is the displacement into the element to the chain pointer
+ * Returns element if found, 0 if empty.
+ */
+LEAF(_dequeue_atomic)
+ mr r5,r3 /* Save the anchor */
+
+Ldequeue_comm:
+ lwarx r3,0,r5 /* Pick up the anchor */
+ mr. r3,r3 /* Is the list empty? */
+ beqlr- /* Leave it list empty... */
+ lwzx r9,r4,r3 /* Get the next in line */
+ stwcx. r9,0,r5 /* Try to chain into the front */
+ bne- Ldequeue_comm /* Didn't make it, try again... */
+ blr /* Return... */
+
diff --git a/AtomicLib/hmi.c b/AtomicLib/hmi.c
new file mode 100644
index 0000000..72551ea
--- /dev/null
+++ b/AtomicLib/hmi.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @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@
+ */
+/*
+ * to test use of timestamp()
+ */
+
+ /* cc hmi.c -o hmi libatomic.a */
+
+#include
+#include "timestamp.h"
+
+void
+main(int argc, char **argv)
+{
+ int i, j;
+ SInt64 ts1, ts2;
+ SInt64 ts3, ts4;
+ struct timescale tsc;
+ double scale;
+
+ if (argc <= 1) {
+ qtss_fprintf(stderr, "Usage: %s loop-count\n", argv[0]);
+ exit(1);
+ }
+
+ j = atoi(argv[1]);
+
+ ts1 = timestamp(); /* START */
+
+ /* Loop for the given loop-count */
+ for (i=0; i < j; i++) {
+ ;
+ }
+
+ ts2 = timestamp(); /* END */
+
+ qtss_printf("ts1 = %qd, ts2 = %qd\n", ts1, ts2);
+
+ utimescale(&tsc);
+ scale = (double)tsc.tsc_numerator / (double)tsc.tsc_denominator;
+
+ ts1 = (SInt64)((double)ts1 * scale);
+ ts2 = (SInt64)((double)ts2 * scale);
+
+ qtss_printf("ts1 = %qd, ts2 = %qd, micro seconds = %qd\n",
+ ts1, ts2, (ts2 - ts1));
+
+ /* Use the scaledtimestamp() now */
+
+ ts3 = scaledtimestamp(scale); /* START */
+
+ /* Loop for the given loop-count */
+ for (i=0; i < j; i++) {
+ ;
+ }
+
+ ts4 = scaledtimestamp(scale); /* END */
+
+ qtss_printf("ts3 = %qd, ts4 = %qd, micro seconds = %qd\n",
+ ts3, ts4, (ts4 - ts3));
+
+}
diff --git a/AtomicLib/timescale.c b/AtomicLib/timescale.c
new file mode 100644
index 0000000..841b89a
--- /dev/null
+++ b/AtomicLib/timescale.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @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: timescale.c
+ *
+ * History:
+ * 23-Oct-1998 Umesh Vaishampayan (umeshv@apple.com)
+ * Created.
+ */
+
+#include "timestamp.h"
+#include
+#include "SafeStdLib.h"
+void
+utimescale(struct timescale *tscp)
+{
+// stop not supported
+ char *death = 0;
+ *death = 0;
+ exit (-1);
+#if 0 // old code
+ unsigned int theNanosecondNumerator = 0;
+ unsigned int theNanosecondDenominator = 0;
+
+ MKGetTimeBaseInfo(NULL, &theNanosecondNumerator, &theNanosecondDenominator, NULL, NULL);
+ tscp->tsc_numerator = theNanosecondNumerator / 1000; /* PPC magic number */
+ tscp->tsc_denominator = theNanosecondDenominator;
+ return;
+#endif
+
+}
+
diff --git a/AtomicLib/timestamp.h b/AtomicLib/timestamp.h
new file mode 100644
index 0000000..f1884d1
--- /dev/null
+++ b/AtomicLib/timestamp.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @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@
+ */
+/*
+ *
+ * History:
+ * 08-Feb-1999 Umesh Vaishampayan (umeshv@apple.com)
+ * Added scaledtimestamp().
+ *
+ * 26-Oct-1998 Umesh Vaishampayan (umeshv@apple.com)
+ * Made the header c++ friendly.
+ *
+ * 23-Oct-1998 Umesh Vaishampayan (umeshv@apple.com)
+ * Created.
+ */
+
+#ifndef _TIMESTAMP_H_
+#define _TIMESTAMP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "OSHeaders.h"
+
+/* Get a 64 bit timestamp */
+extern SInt64 timestamp(void);
+
+struct timescale {
+ SInt64 tsc_numerator;
+ SInt64 tsc_denominator;
+};
+
+/*
+ * Get numerator and denominator to convert value returned
+ * by timestamp() to microseconds
+ */
+extern void utimescale(struct timescale *tscp);
+
+extern SInt64 scaledtimestamp(double scale);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TIMESTAMP_H_ */
diff --git a/AtomicLib/timestamp.s b/AtomicLib/timestamp.s
new file mode 100644
index 0000000..dc4e28f
--- /dev/null
+++ b/AtomicLib/timestamp.s
@@ -0,0 +1,133 @@
+/*
+ *
+ * @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: timestamp.s
+ *
+ * History:
+ * 12-Feb-1999 Umesh Vaishampayan (umeshv@apple.com)
+ * Integrated scaledtimestamp() written by
+ * Joe Sokol (sokol1@apple.com)
+ *
+ * 23-Oct-1998 Umesh Vaishampayan (umeshv@apple.com)
+ * Created.
+ */
+
+#include
+
+/*
+ * long long timestamp(void)
+ *
+ * Read the PPC timebase.
+ */
+LEAF(_timestamp)
+Lagain:
+ mftbu r3
+ mftb r4
+ mftbu r6
+ cmpw r6, r3
+ bne- Lagain
+ blr
+
+
+/*
+ * long long scaledtimestamp(double scale)
+ *
+ * Read the PPC timebase. Convert the time base value based on
+ * scale.
+ *
+ * Caveat: scale can not be 0, NaN, Inf. It's upto the caller
+ * to validate scale before calling this.
+ */
+
+LEAF(_scaledtimestamp)
+Lagain1:
+ mftbu r3
+ mftb r4
+ mftbu r6
+ cmpw r6,r3
+ bne- Lagain1
+
+; r3 and r4 have the time base.
+; convert the long long value to double
+L_LLtoD:
+ cntlzw r0,r3
+ cmplwi cr0,r0,31
+ bc 12,1,L2
+ subfic r10,r0,63
+ subfic r11,r10,52
+ subfic r0,r11,32
+ srw r0,r4,r0
+ slw r9,r3,r11
+ or r3,r9,r0
+ b L6
+L2:
+ cntlzw r0,r4
+ subfic r10,r0,31
+ subfic r11,r10,52
+ cmplwi cr0,r11,31
+ bc 4,1,L4
+ addi r0,r11,-32
+ slw r3,r4,r0
+ li r4,0
+ b L3
+L4:
+ subfic r0,r11,32
+ srw r3,r4,r0
+L6:
+ slw r4,r4,r11
+L3:
+ addi r0,r10,1023
+ slwi r0,r0,20
+ rlwimi r3,r0,0,0,11
+ stw r3,-8(r1)
+ stw r4,-4(r1)
+ lfd f0,-8(r1) ; load the double representation of time base
+
+ fmul f0,f0,f1 ; f1 has scale to convert timestamp
+
+; convert the double to long long
+L_DtoLL:
+ stfd f0,-8(r1)
+ lwz r3,-8(r1)
+ lwz r4,-4(r1)
+ srwi r0,r3,20
+ rlwinm r9,r3,0,12,31
+ subfic r8,r0,1075
+ cmplwi cr0,r8,31
+ oris r3,r9,0x10
+ bc 4,1,L8
+ addi r0,r8,-32
+ srw r4,r3,r0
+ li r3,0
+ blr
+L8:
+ subfic r0,r8,32
+ slw r0,r3,r0
+ srw r9,r4,r8
+ or r4,r9,r0
+ srw r3,r3,r8
+ blr
+
diff --git a/CommonUtilitiesLib/ConfParser.cpp b/CommonUtilitiesLib/ConfParser.cpp
new file mode 100644
index 0000000..8f88c0f
--- /dev/null
+++ b/CommonUtilitiesLib/ConfParser.cpp
@@ -0,0 +1,251 @@
+/*
+ *
+ * @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@
+ *
+ */
+
+
+#include "ConfParser.h"
+#include "OSMemory.h"
+
+#include "MyAssert.h"
+#include "OSHeaders.h"
+
+#include
+
+#include "GetWord.h"
+#include "Trim.h"
+
+
+#include
+#include
+
+
+static Bool16 SampleConfigSetter( const char* paramName, const char* paramValue[], void* userData );
+static void DisplayConfigErr( const char*fname, int lineCount, const char*lineBuff, const char *errMessage );
+
+
+
+void TestParseConfigFile()
+{
+ ParseConfigFile( false, "qtss.conf", SampleConfigSetter, NULL );
+
+}
+
+static Bool16 SampleConfigSetter( const char* paramName, const char* paramValue[], void* /*userData*/ )
+{
+ qtss_printf( "param: %s", paramName );
+
+ int x = 0;
+
+ while ( paramValue[x] )
+ {
+ qtss_printf( " value(%"_S32BITARG_"): %s ", (SInt32)x, (char *) paramValue[x] );
+ x++;
+ }
+
+ qtss_printf( "\n" );
+
+ return false;
+}
+
+
+static void DisplayConfigErr( const char*fname, int lineCount, const char*lineBuff, const char *errMessage )
+{
+
+ qtss_printf( "- Configuration file error:\n" );
+
+
+ if ( lineCount )
+ qtss_printf( " file: %s, line# %i\n", fname, lineCount );
+ else
+ qtss_printf( " file: %s\n", fname );
+
+ if ( lineBuff )
+ qtss_printf( " text: %s", lineBuff ); // lineBuff already includes a \n
+
+ if ( errMessage )
+ qtss_printf( " reason: %s\n", errMessage ); // lineBuff already includes a \n
+}
+
+
+int ParseConfigFile(
+ Bool16 allowNullValues
+ , const char* fname
+ , Bool16 (*ConfigSetter)( const char* paramName, const char* paramValue[], void* userData )
+ , void* userData )
+{
+ int error = -1;
+ FILE *configFile;
+ int lineCount = 0;
+
+ Assert( fname );
+ Assert( ConfigSetter );
+
+
+ if (!fname) return error;
+ if (!ConfigSetter) return error;
+
+
+ configFile = fopen( fname, "r" );
+
+// Assert( configFile );
+
+ if ( configFile )
+ {
+ SInt32 lineBuffSize = kConfParserMaxLineSize;
+ SInt32 wordBuffSize = kConfParserMaxParamSize;
+
+
+ char lineBuff[kConfParserMaxLineSize];
+ char wordBuff[kConfParserMaxParamSize];
+
+ char *next;
+
+ // debug assistance -- CW debugger won't display large char arrays as strings
+ //char* l = lineBuff;
+ //char* w = wordBuff;
+
+
+ do
+ {
+ next = lineBuff;
+
+ // get a line ( fgets adds \n+ 0x00 )
+
+ if ( fgets( lineBuff, lineBuffSize, configFile ) == NULL )
+ break;
+
+ lineCount++;
+ error = 0; // allow empty lines at beginning of file.
+
+ // trim the leading whitespace
+ next = TrimLeft( lineBuff );
+
+ if (*next)
+ {
+
+ if ( *next == '#' )
+ {
+ // it's a comment
+ // prabably do nothing in release version?
+
+ // qtss_printf( "comment: %s" , &lineBuff[1] );
+
+ error = 0;
+
+ }
+ else
+ { char* param;
+
+ // grab the param name, quoted or not
+ if ( *next == '"' )
+ next = GetQuotedWord( wordBuff, next, wordBuffSize );
+ else
+ next = GetWord( wordBuff, next, wordBuffSize );
+
+ Assert( *wordBuff );
+
+ param = NEW char[strlen( wordBuff ) + 1 ];
+
+ Assert( param );
+
+ if ( param )
+ {
+ const char* values[kConfParserMaxParamValues+1];
+ int maxValues = 0;
+
+ strcpy( param, wordBuff );
+
+
+ values[maxValues] = NULL;
+
+ while ( maxValues < kConfParserMaxParamValues && *next )
+ {
+ // ace
+ next = TrimLeft( next );
+
+ if (*next)
+ {
+ if ( *next == '"' )
+ next = GetQuotedWord( wordBuff, next, wordBuffSize );
+ else
+ next = GetWord( wordBuff, next, wordBuffSize );
+
+ char* value = NEW char[strlen( wordBuff ) + 1 ];
+
+ Assert( value );
+
+ if ( value )
+ {
+ strcpy( value, wordBuff );
+
+ values[maxValues++] = value;
+ values[maxValues] = 0;
+ }
+
+ }
+
+ }
+
+ if ( (maxValues > 0 || allowNullValues) && !(*ConfigSetter)( param, values, userData ) )
+ error = 0;
+ else
+ { error = -1;
+ if ( maxValues > 0 )
+ DisplayConfigErr( fname, lineCount, lineBuff, "Parameter could not be set." );
+ else
+ DisplayConfigErr( fname, lineCount, lineBuff, "No value to set." );
+ }
+
+ delete [] param;
+
+ maxValues = 0;
+
+ while ( values[maxValues] )
+ { char** tempValues = (char**)values; // Need to do this to delete a const
+ delete [] tempValues[maxValues];
+ maxValues++;
+ }
+
+
+ }
+
+ }
+
+
+ }
+
+
+
+ } while ( feof( configFile ) == 0 && error == 0 );
+
+ (void)fclose( configFile );
+ }
+// else
+// {
+// qtss_printf("Couldn't open config file at: %s\n", fname);
+// }
+
+ return error;
+
+}
diff --git a/CommonUtilitiesLib/ConfParser.h b/CommonUtilitiesLib/ConfParser.h
new file mode 100644
index 0000000..d3dc0cf
--- /dev/null
+++ b/CommonUtilitiesLib/ConfParser.h
@@ -0,0 +1,49 @@
+#ifndef __ConfParser__
+#define __ConfParser__
+
+/*
+ *
+ * @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@
+ *
+ */
+
+
+
+#include "OSHeaders.h"
+
+// the maximum size + 1 of a parameter
+#define kConfParserMaxParamSize 512
+
+
+// the maximum size + 1 of single line in the file
+#define kConfParserMaxLineSize 1024
+
+// the maximum number of values per config parameter
+#define kConfParserMaxParamValues 10
+
+
+void TestParseConfigFile();
+
+int ParseConfigFile( Bool16 allowNullValues, const char* fname \
+ , Bool16 (*ConfigSetter)( const char* paramName, const char* paramValue[], void * userData ) \
+ , void* userData );
+#endif
diff --git a/CommonUtilitiesLib/DateTranslator.cpp b/CommonUtilitiesLib/DateTranslator.cpp
new file mode 100644
index 0000000..1002311
--- /dev/null
+++ b/CommonUtilitiesLib/DateTranslator.cpp
@@ -0,0 +1,159 @@
+/*
+ *
+ * @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: DateTranslator.h
+
+ Contains: Efficient routines & data structures for converting from
+ RFC 1123 compliant date strings to local file system dates & vice versa.
+
+
+*/
+
+#include "DateTranslator.h"
+
+#include
+
+#include "OSHeaders.h"
+#include "OS.h"
+
+#include "StringParser.h"
+#include "StrPtrLen.h"
+
+// If you assign values of 0 - 25 for all the letters, and sum up the values of
+// the letters in each month, you get a table that looks like this. For instance,
+// "Jul" = 9 + 20 + 11 = 40. The value of July in a C tm struct is 6, so position
+// 40 = 6 in this array.
+
+const UInt32 kMonthHashTable[] =
+{
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, // 0 - 9
+ 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, // 10 - 19
+ 12, 12, 0, 12, 12, 12, 7, 12, 12, 2, // 20 - 29
+ 12, 12, 3, 12, 12, 9, 4, 8, 12, 12, // 30 - 39
+ 6, 12, 5, 12, 12, 12, 12, 12, 10, 12 // 40 - 49
+};
+const UInt32 kMonthHashTableSize = 49;
+
+
+SInt64 DateTranslator::ParseDate(StrPtrLen* inDateString)
+{
+ //SEE RFC 1123 for details on the date string format
+ //ex: Mon, 04 Nov 1996 21:42:17 GMT
+
+ // Parse the date buffer, filling out a tm struct
+ struct tm theDateStruct;
+ ::memset(&theDateStruct, 0, sizeof(theDateStruct));
+
+ // All RFC 1123 dates are the same length.
+ if (inDateString->Len != DateBuffer::kDateBufferLen)
+ return 0;
+
+ StringParser theDateParser(inDateString);
+
+ // the day of the week is redundant... we can skip it!
+ theDateParser.ConsumeLength(NULL, 5);
+
+ // We are at the date now.
+ theDateStruct.tm_mday = theDateParser.ConsumeInteger(NULL);
+ theDateParser.ConsumeWhitespace();
+
+ // We are at the month now. Use our hand-crafted perfect hash table
+ // to get the right value to place in the tm struct
+ if (theDateParser.GetDataRemaining() < 4)
+ return 0;
+
+ UInt32 theIndex = ConvertCharToMonthTableIndex(theDateParser.GetCurrentPosition()[0]) +
+ ConvertCharToMonthTableIndex(theDateParser.GetCurrentPosition()[1]) +
+ ConvertCharToMonthTableIndex(theDateParser.GetCurrentPosition()[2]);
+
+ if (theIndex > kMonthHashTableSize)
+ return 0;
+
+ theDateStruct.tm_mon = kMonthHashTable[theIndex];
+
+ // If the month is illegal, return an error
+ if (theDateStruct.tm_mon >= 12)
+ return 0;
+
+ // Skip over the date
+ theDateParser.ConsumeLength(NULL, 4);
+
+ // Grab the year (years since 1900 is what the tm struct wants)
+ theDateStruct.tm_year = theDateParser.ConsumeInteger(NULL) - 1900;
+ theDateParser.ConsumeWhitespace();
+
+ // Now just grab hour, minute, second
+ theDateStruct.tm_hour = theDateParser.ConsumeInteger(NULL);
+ theDateStruct.tm_hour += OS::GetGMTOffset();
+
+ theDateParser.ConsumeLength(NULL, 1); //skip over ':'
+
+ theDateStruct.tm_min = theDateParser.ConsumeInteger(NULL);
+ theDateParser.ConsumeLength(NULL, 1); //skip over ':'
+
+ theDateStruct.tm_sec = theDateParser.ConsumeInteger(NULL);
+
+ // Ok, we've filled out the tm struct completely, now convert it to a time_t
+ time_t theTime = ::mktime(&theDateStruct);
+ return (SInt64)theTime * 1000; // convert to a time value in our timebase.
+}
+
+void DateTranslator::UpdateDateBuffer(DateBuffer* inDateBuffer, const SInt64& inDate, time_t gmtoffset)
+{
+ if (inDateBuffer == NULL)
+ return;
+
+ struct tm* gmt = NULL;
+ struct tm timeResult;
+
+ if (inDate == 0)
+ {
+ time_t calendarTime = ::time(NULL) + gmtoffset;
+ gmt = ::qtss_gmtime(&calendarTime, &timeResult);
+ }
+ else
+ {
+ time_t convertedTime = (time_t)(inDate / (SInt64)1000) + gmtoffset ; // Convert from msec to sec
+ gmt = ::qtss_gmtime(&convertedTime, &timeResult);
+ }
+
+ Assert(gmt != NULL); //is it safe to assert this?
+ size_t size = 0;
+ if (0 == gmtoffset)
+ size = qtss_strftime( inDateBuffer->fDateBuffer, sizeof(inDateBuffer->fDateBuffer),
+ "%a, %d %b %Y %H:%M:%S GMT", gmt);
+
+ Assert(size == DateBuffer::kDateBufferLen);
+}
+
+void DateBuffer::InexactUpdate()
+{
+ SInt64 theCurTime = OS::Milliseconds();
+ if ((fLastDateUpdate == 0) || ((fLastDateUpdate + kUpdateInterval) < theCurTime))
+ {
+ fLastDateUpdate = theCurTime;
+ this->Update(0);
+ }
+}
diff --git a/CommonUtilitiesLib/DateTranslator.h b/CommonUtilitiesLib/DateTranslator.h
new file mode 100644
index 0000000..6c2373e
--- /dev/null
+++ b/CommonUtilitiesLib/DateTranslator.h
@@ -0,0 +1,113 @@
+/*
+ *
+ * @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: DateTranslator.h
+
+ Contains: Efficient routines & data structures for converting from
+ RFC 1123 compliant date strings to local file system dates & vice versa.
+
+
+
+*/
+
+#ifndef _DATE_TRANSLATOR_H_
+#define _DATE_TRANSLATOR_H_
+
+#include
+#include
+#include "StrPtrLen.h"
+
+class DateBuffer;
+
+class DateTranslator
+{
+ public:
+
+ // this updates the DateBuffer to be the current date / time.
+ // If you wish to set the DateBuffer to a particular date, pass in that date.
+ // Dates should be in the OS.h compliant format
+ static void UpdateDateBuffer(DateBuffer* inDateBuffer, const SInt64& inDate, time_t gmtoffset = 0);
+
+ //Given an HTTP/1.1 compliant date string (in one of the three 1.1 formats)
+ //this returns an OS.h compliant date/time value.
+ static SInt64 ParseDate(StrPtrLen* inDateString);
+
+ private:
+
+ static UInt32 ConvertCharToMonthTableIndex(int inChar)
+ {
+ return (UInt32)(toupper(inChar) - 'A'); // Convert to a value between 0 - 25
+ }
+};
+
+class DateBuffer
+{
+public:
+
+ // This class provides no protection against being accessed from multiple threads
+ // simultaneously. Update & InexactUpdate rewrite the date buffer, so care should
+ // be taken to protect against simultaneous access.
+
+ DateBuffer() : fLastDateUpdate(0) { fDateBuffer[0] = 0; }
+ ~DateBuffer() {}
+
+ //SEE RFC 1123 for details on the date string format
+ //ex: Mon, 04 Nov 1996 21:42:17 GMT
+
+ //RFC 1123 date strings are always of this length
+ enum
+ {
+ kDateBufferLen = 29
+ };
+
+ // Updates this date buffer to reflect the current time.
+ // If a date is provided, this updates the DateBuffer to be that date.
+ void Update(const SInt64& inDate) { DateTranslator::UpdateDateBuffer(this, inDate); }
+
+ // Updates this date buffer to reflect the current time, with a certain degree
+ // of inexactitude (the range of error is defined by the kUpdateInterval value)
+ void InexactUpdate();
+
+ //returns a NULL terminated C-string always of kHTTPDateLen length.
+ char *GetDateBuffer() { return fDateBuffer; }
+
+private:
+
+ enum
+ {
+ kUpdateInterval = 60000 // Update every minute
+ };
+
+ //+1 for terminator +1 for padding
+ char fDateBuffer[kDateBufferLen + 2];
+ SInt64 fLastDateUpdate;
+
+ friend class DateTranslator;
+};
+
+
+#endif
+
+
diff --git a/CommonUtilitiesLib/DssStopwatch.h b/CommonUtilitiesLib/DssStopwatch.h
new file mode 100644
index 0000000..8a52717
--- /dev/null
+++ b/CommonUtilitiesLib/DssStopwatch.h
@@ -0,0 +1,106 @@
+/*
+ *
+ * @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@
+ *
+ */
+#ifndef __DSS_STOPWATCH__
+#define __DSS_STOPWATCH__
+//#include "DssStopwatch.h"
+
+#include "OS.h"
+
+
+class DssEggtimer {
+ public:
+ enum { kDurationNeverExpire = -1 };
+
+ DssEggtimer( SInt64 inMilliseconds ) { fTimerDuration = inMilliseconds; Reset(); }
+
+ void OneShotSetTo( SInt64 inMilliseconds )
+ {
+ // set the egg timer to this time for one cycle.
+ // there after Reset will use fTimerDuration
+ fExpirationMilliseconds = OS::Milliseconds() + inMilliseconds;
+ }
+ void Reset()
+ {
+ //if ( fTimerDuration != (SInt64)kDurationNeverExpire )
+ fExpirationMilliseconds = OS::Milliseconds() + fTimerDuration;
+ }
+
+ void ResetTo(SInt64 inMilliseconds)
+ {
+ fTimerDuration = inMilliseconds;
+ this->Reset();
+ }
+
+ Bool16 Expired()
+ {
+ //if (fTimerDuration == (SInt64)kDurationNeverExpire )
+ // return false;
+
+ return fExpirationMilliseconds <= OS::Milliseconds();
+ }
+ SInt64 MaxDuration() { return fTimerDuration; }
+
+ private:
+ SInt64 fTimerDuration;
+ SInt64 fExpirationMilliseconds;
+
+};
+
+class DssMillisecondStopwatch {
+
+ public:
+ DssMillisecondStopwatch() :
+ fIsStarted(false)
+ , fTimerDuration(-1)
+ {}
+ ;
+ void Start() { fStartedAt = OS::Milliseconds(); fIsStarted = true; }
+ void Stop() { fTimerDuration = OS::Milliseconds() - fStartedAt; }
+
+ SInt64 Duration() { return fTimerDuration; }
+
+ private:
+ Bool16 fIsStarted;
+ SInt64 fTimerDuration;
+ SInt64 fStartedAt;
+};
+
+class DssDurationTimer {
+
+ public:
+ DssDurationTimer() { fStartedAtMsec = OS::Milliseconds(); }
+ void Reset() { fStartedAtMsec = OS::Milliseconds(); }
+ void ResetToDuration( SInt64 inDurationInMsec ) { fStartedAtMsec = OS::Milliseconds() - inDurationInMsec; }
+ SInt64 DurationInMilliseconds() { return OS::Milliseconds() - fStartedAtMsec; }
+ SInt64 DurationInSeconds() { return (OS::Milliseconds() - fStartedAtMsec) / (SInt64)1000; }
+
+
+ private:
+ SInt64 fStartedAtMsec;
+};
+
+
+#endif
+
diff --git a/CommonUtilitiesLib/EventContext.cpp b/CommonUtilitiesLib/EventContext.cpp
new file mode 100644
index 0000000..7cec317
--- /dev/null
+++ b/CommonUtilitiesLib/EventContext.cpp
@@ -0,0 +1,283 @@
+/*
+ *
+ * @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: EventContext.cpp
+
+ Contains: Impelments object in .h file
+
+
+
+*/
+
+#include "EventContext.h"
+#include "OSThread.h"
+#include "atomic.h"
+
+#include
+#include
+
+#ifndef __Win32__
+#include
+#endif
+
+#if MACOSXEVENTQUEUE
+#include "tempcalls.h" //includes MacOS X prototypes of event queue functions
+#endif
+
+#define EVENT_CONTEXT_DEBUG 0
+
+#if EVENT_CONTEXT_DEBUG
+#include "OS.h"
+#endif
+
+#ifdef __Win32__
+unsigned int EventContext::sUniqueID = WM_USER; // See commentary in RequestEvent
+#else
+unsigned int EventContext::sUniqueID = 1;
+#endif
+
+EventContext::EventContext(int inFileDesc, EventThread* inThread)
+: fFileDesc(inFileDesc),
+ fUniqueID(0),
+ fUniqueIDStr((char*)&fUniqueID, sizeof(fUniqueID)),
+ fEventThread(inThread),
+ fWatchEventCalled(false),
+ fAutoCleanup(true)
+{}
+
+
+void EventContext::InitNonBlocking(int inFileDesc)
+{
+ fFileDesc = inFileDesc;
+
+#ifdef __Win32__
+ u_long one = 1;
+ int err = ::ioctlsocket(fFileDesc, FIONBIO, &one);
+#else
+ int flag = ::fcntl(fFileDesc, F_GETFL, 0);
+ int err = ::fcntl(fFileDesc, F_SETFL, flag | O_NONBLOCK);
+#endif
+ AssertV(err == 0, OSThread::GetErrno());
+}
+
+void EventContext::Cleanup()
+{
+ int err = 0;
+
+ if (fFileDesc != kInvalidFileDesc)
+ {
+ //if this object is registered in the table, unregister it now
+ if (fUniqueID > 0)
+ {
+ fEventThread->fRefTable.UnRegister(&fRef);
+
+#if !MACOSXEVENTQUEUE
+ select_removeevent(fFileDesc);//The eventqueue / select shim requires this
+#ifdef __Win32__
+ err = ::closesocket(fFileDesc);
+#endif
+
+#else
+ //On Linux (possibly other UNIX implementations) you MUST NOT close the fd before
+ //removing the fd from the select mask, and having the select function wake up
+ //to register this fact. If you close the fd first, bad things may happen, like
+ //the socket not getting unbound from the port & IP addr.
+ //
+ //So, what we do is have the select thread itself call close. This is triggered
+ //by calling removeevent.
+ err = ::close(fFileDesc);
+#endif
+ }
+ else
+#ifdef __Win32__
+ err = ::closesocket(fFileDesc);
+#else
+ err = ::close(fFileDesc);
+#endif
+ }
+
+ fFileDesc = kInvalidFileDesc;
+ fUniqueID = 0;
+
+ AssertV(err == 0, OSThread::GetErrno());//we don't really care if there was an error, but it's nice to know
+}
+
+
+void EventContext::SnarfEventContext( EventContext &fromContext )
+{
+ //+ show that we called watchevent
+ // copy the unique id
+ // set our fUniqueIDStr to the unique id
+ // copy the eventreq
+ // find the old event object
+ // show us as the object in the fRefTable
+ // we take the OSRef from the old context, point it at our context
+ //
+ //TODO - this whole operation causes a race condition for Event posting
+ // way up the chain we need to disable event posting
+ // or copy the posted events afer this op completes
+
+ fromContext.fFileDesc = kInvalidFileDesc;
+
+ fWatchEventCalled = fromContext.fWatchEventCalled;
+ fUniqueID = fromContext.fUniqueID;
+ fUniqueIDStr.Set((char*)&fUniqueID, sizeof(fUniqueID)),
+
+ ::memcpy( &fEventReq, &fromContext.fEventReq, sizeof( struct eventreq ) );
+
+ fRef.Set( fUniqueIDStr, this );
+ fEventThread->fRefTable.Swap(&fRef);
+ fEventThread->fRefTable.UnRegister(&fromContext.fRef);
+}
+
+void EventContext::RequestEvent(int theMask)
+{
+#if DEBUG
+ fModwatched = true;
+#endif
+
+ //
+ // The first time this function gets called, we're supposed to
+ // call watchevent. Each subsequent time, call modwatch. That's
+ // the way the MacOS X event queue works.
+
+ if (fWatchEventCalled)
+ {
+ fEventReq.er_eventbits = theMask;
+#if MACOSXEVENTQUEUE
+ if (modwatch(&fEventReq, theMask) != 0)
+#else
+ if (select_modwatch(&fEventReq, theMask) != 0)
+#endif
+ AssertV(false, OSThread::GetErrno());
+ }
+ else
+ {
+ //allocate a Unique ID for this socket, and add it to the ref table
+
+#ifdef __Win32__
+ //
+ // Kind of a hack. On Win32, the way that we pass around the unique ID is
+ // by making it the message ID of our Win32 message (see win32ev.cpp).
+ // Messages must be >= WM_USER. Hence this code to restrict the numberspace
+ // of our UniqueIDs.
+ if (!compare_and_store(8192, WM_USER, &sUniqueID)) // Fix 2466667: message IDs above a
+ fUniqueID = (PointerSizedInt)atomic_add(&sUniqueID, 1); // level are ignored, so wrap at 8192
+ else
+ fUniqueID = (PointerSizedInt)WM_USER;
+#else
+ if (!compare_and_store(10000000, 1, &sUniqueID))
+ fUniqueID = (PointerSizedInt)atomic_add(&sUniqueID, 1);
+ else
+ fUniqueID = 1;
+#endif
+
+ fRef.Set(fUniqueIDStr, this);
+ fEventThread->fRefTable.Register(&fRef);
+
+ //fill out the eventreq data structure
+ ::memset( &fEventReq, '\0', sizeof(fEventReq));
+ fEventReq.er_type = EV_FD;
+ fEventReq.er_handle = fFileDesc;
+ fEventReq.er_eventbits = theMask;
+ fEventReq.er_data = (void*)fUniqueID;
+
+ fWatchEventCalled = true;
+#if MACOSXEVENTQUEUE
+ if (watchevent(&fEventReq, theMask) != 0)
+#else
+ if (select_watchevent(&fEventReq, theMask) != 0)
+#endif
+ //this should never fail, but if it does, cleanup.
+ AssertV(false, OSThread::GetErrno());
+
+ }
+}
+
+void EventThread::Entry()
+{
+ struct eventreq theCurrentEvent;
+ ::memset( &theCurrentEvent, '\0', sizeof(theCurrentEvent) );
+
+ while (true)
+ {
+ int theErrno = EINTR;
+ while (theErrno == EINTR)
+ {
+#if MACOSXEVENTQUEUE
+ int theReturnValue = waitevent(&theCurrentEvent, NULL);
+#else
+ int theReturnValue = select_waitevent(&theCurrentEvent, NULL);
+#endif
+ //Sort of a hack. In the POSIX version of the server, waitevent can return
+ //an actual POSIX errorcode.
+ if (theReturnValue >= 0)
+ theErrno = theReturnValue;
+ else
+ theErrno = OSThread::GetErrno();
+ }
+
+ AssertV(theErrno == 0, theErrno);
+
+ //ok, there's data waiting on this socket. Send a wakeup.
+ if (theCurrentEvent.er_data != NULL)
+ {
+ //The cookie in this event is an ObjectID. Resolve that objectID into
+ //a pointer.
+ StrPtrLen idStr((char*)&theCurrentEvent.er_data, sizeof(theCurrentEvent.er_data));
+ OSRef* ref = fRefTable.Resolve(&idStr);
+ if (ref != NULL)
+ {
+ EventContext* theContext = (EventContext*)ref->GetObject();
+#if DEBUG
+ theContext->fModwatched = false;
+#endif
+ theContext->ProcessEvent(theCurrentEvent.er_eventbits);
+ fRefTable.Release(ref);
+
+
+ }
+ }
+
+#if EVENT_CONTEXT_DEBUG
+ SInt64 yieldStart = OS::Milliseconds();
+#endif
+
+ this->ThreadYield();
+
+#if EVENT_CONTEXT_DEBUG
+ SInt64 yieldDur = OS::Milliseconds() - yieldStart;
+ static SInt64 numZeroYields;
+
+ if ( yieldDur > 1 )
+ {
+ qtss_printf( "EventThread time in OSTHread::Yield %i, numZeroYields %i\n", (SInt32)yieldDur, (SInt32)numZeroYields );
+ numZeroYields = 0;
+ }
+ else
+ numZeroYields++;
+#endif
+ }
+}
diff --git a/CommonUtilitiesLib/EventContext.h b/CommonUtilitiesLib/EventContext.h
new file mode 100644
index 0000000..6fa8b83
--- /dev/null
+++ b/CommonUtilitiesLib/EventContext.h
@@ -0,0 +1,180 @@
+/*
+ *
+ * @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: EventContext.h
+
+ Contains: An event context provides the intelligence to take an event
+ generated from a UNIX file descriptor (usually EV_RE or EV_WR)
+ and signal a Task.
+
+
+
+
+*/
+
+#ifndef __EVENT_CONTEXT_H__
+#define __EVENT_CONTEXT_H__
+
+#include "OSThread.h"
+#include "Task.h"
+#include "OSRef.h"
+
+#if MACOSXEVENTQUEUE
+ #ifdef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
+ #include
+ #else
+ #include "ev.h"
+ #endif
+#else
+ #include "ev.h"
+#endif
+
+
+//enable to trace event context execution and the task associated with the context
+#define EVENTCONTEXT_DEBUG 0
+
+class EventThread;
+
+class EventContext
+{
+ public:
+
+ //
+ // Constructor. Pass in the EventThread you would like to receive
+ // events for this context, and the fd that this context applies to
+ EventContext(int inFileDesc, EventThread* inThread);
+ virtual ~EventContext() { if (fAutoCleanup) this->Cleanup(); }
+
+ //
+ // InitNonBlocking
+ //
+ // Sets inFileDesc to be non-blocking. Once this is called, the
+ // EventContext object "owns" the file descriptor, and will close it
+ // when Cleanup is called. This is necessary because of some weird
+ // select() behavior. DON'T CALL CLOSE ON THE FD ONCE THIS IS CALLED!!!!
+ void InitNonBlocking(int inFileDesc);
+
+ //
+ // Cleanup. Will be called by the destructor, but can be called earlier
+ void Cleanup();
+
+ //
+ // Arms this EventContext. Pass in the events you would like to receive
+ void RequestEvent(int theMask = EV_RE);
+
+
+ //
+ // Provide the task you would like to be notified
+ void SetTask(Task* inTask)
+ {
+ fTask = inTask;
+ if (EVENTCONTEXT_DEBUG)
+ {
+ if (fTask== NULL)
+ qtss_printf("EventContext::SetTask context=%p task= NULL\n", (void *) this);
+ else
+ qtss_printf("EventContext::SetTask context=%p task= %p name=%s\n",(void *) this,(void *) fTask, fTask->fTaskName);
+ }
+ }
+
+ // when the HTTP Proxy tunnels takes over a TCPSocket, we need to maintain this context too
+ void SnarfEventContext( EventContext &fromContext );
+
+ // Don't cleanup this socket automatically
+ void DontAutoCleanup() { fAutoCleanup = false; }
+
+ // Direct access to the FD is not recommended, but is needed for modules
+ // that want to use the Socket classes and need to request events on the fd.
+ int GetSocketFD() { return fFileDesc; }
+
+ enum
+ {
+ kInvalidFileDesc = -1 //int
+ };
+
+ protected:
+
+ //
+ // ProcessEvent
+ //
+ // When an event occurs on this file descriptor, this function
+ // will get called. Default behavior is to Signal the associated
+ // task, but that behavior may be altered / overridden.
+ //
+ // Currently, we always generate a Task::kReadEvent
+ virtual void ProcessEvent(int /*eventBits*/)
+ {
+ if (EVENTCONTEXT_DEBUG)
+ {
+ if (fTask== NULL)
+ qtss_printf("EventContext::ProcessEvent context=%p task=NULL\n",(void *) this);
+ else
+ qtss_printf("EventContext::ProcessEvent context=%p task=%p TaskName=%s\n",(void *)this,(void *) fTask, fTask->fTaskName);
+ }
+
+ if (fTask != NULL)
+ fTask->Signal(Task::kReadEvent);
+ }
+
+ int fFileDesc;
+
+ private:
+
+ struct eventreq fEventReq;
+
+ OSRef fRef;
+ PointerSizedInt fUniqueID;
+ StrPtrLen fUniqueIDStr;
+ EventThread* fEventThread;
+ Bool16 fWatchEventCalled;
+ int fEventBits;
+ Bool16 fAutoCleanup;
+
+ Task* fTask;
+#if DEBUG
+ Bool16 fModwatched;
+#endif
+
+ static unsigned int sUniqueID;
+
+ friend class EventThread;
+};
+
+class EventThread : public OSThread
+{
+ public:
+
+ EventThread() : OSThread() {}
+ virtual ~EventThread() {}
+
+ private:
+
+ virtual void Entry();
+ OSRefTable fRefTable;
+
+ friend class EventContext;
+};
+
+#endif //__EVENT_CONTEXT_H__
diff --git a/CommonUtilitiesLib/FastCopyMacros.h b/CommonUtilitiesLib/FastCopyMacros.h
new file mode 100644
index 0000000..4dd8ef2
--- /dev/null
+++ b/CommonUtilitiesLib/FastCopyMacros.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * @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@
+ *
+ */
+#ifndef __FastCopyMacros__
+#define __FastCopyMacros__
+
+#define COPY_BYTE( dest, src ) ( *((char*)(dest)) = *((char*)(src)) )
+#define COPY_WORD( dest, src ) ( *((SInt16*)(dest)) = *((SInt16*)(src)) )
+#define COPY_LONG_WORD( dest, src ) ( *((SInt32*)(dest)) = *((SInt32*)(src)) )
+#define COPY_LONG_LONG_WORD( dest, src ) ( *((SInt64*)(dest)) = *((SInt64**)(src)) )
+
+#define MOVE_BYTE( dest, src ) ( dest = *((char*)(src)) )
+#define MOVE_WORD( dest, src ) ( dest = *((SInt16*)(src)) )
+#define MOVE_LONG_WORD( dest, src ) ( dest = *((SInt32*)(src)) )
+#define MOVE_LONG_LONG_WORD( dest, src ) ( dest = *((SInt64**)(src)) )
+
+
+#endif
+
diff --git a/CommonUtilitiesLib/GetWord.c b/CommonUtilitiesLib/GetWord.c
new file mode 100644
index 0000000..0fb53f1
--- /dev/null
+++ b/CommonUtilitiesLib/GetWord.c
@@ -0,0 +1,102 @@
+/*
+ *
+ * @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@
+ *
+ */
+
+#include "GetWord.h"
+
+char* GetWord( char* toWordPtr, char* fromStrPtr, SInt32 limit )
+{
+ // get a word from a string
+ // copy result into toWordPtr, return one past end of the
+ // word, limit is max for toWordPtr
+ // fromStrPtr
+
+ // trim any leading white space
+ while ( (unsigned char)*fromStrPtr <= 0x20 && *fromStrPtr )
+ fromStrPtr++;
+
+ // copy until we find more white space
+ while ( limit && (unsigned char)*fromStrPtr > 0x20 && *fromStrPtr )
+ {
+ *toWordPtr++ = *fromStrPtr++;
+ limit--;
+ }
+
+ *toWordPtr = 0x00;
+
+ return (char *) fromStrPtr;
+}
+
+char * GetQuotedWord( char* toWordPtr, char* fromStrPtr, SInt32 limit )
+{
+ // get a quote encoded word from a string
+ // make include white space
+ int lastWasQuote = 0;
+
+ // trim any leading white space
+ while ( ( (unsigned char)*fromStrPtr <= 0x20 ) && *fromStrPtr )
+ fromStrPtr++;
+
+
+ if ( (unsigned char)*fromStrPtr == '"' )
+ { // must lead with quote sign after white space
+ fromStrPtr++;
+
+
+
+ // copy until we find the last single quote
+ while ( limit && *fromStrPtr )
+ {
+ if ( (unsigned char)*fromStrPtr == '"' )
+ {
+ if ( lastWasQuote )
+ {
+ *toWordPtr++ = '"';
+ lastWasQuote = 0;
+ limit--;
+ }
+ else
+ lastWasQuote = 1;
+ }
+ else
+ {
+ if ( !lastWasQuote )
+ { *toWordPtr++ = *fromStrPtr;
+ limit--;
+ }
+ else // we're done, hit a quote by itself
+ break;
+
+ }
+
+ // consume the char we read
+ fromStrPtr++;
+
+ }
+ }
+
+ *toWordPtr = 0x00;
+
+ return (char *) fromStrPtr;
+}
diff --git a/CommonUtilitiesLib/GetWord.h b/CommonUtilitiesLib/GetWord.h
new file mode 100644
index 0000000..45dae24
--- /dev/null
+++ b/CommonUtilitiesLib/GetWord.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * @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@
+ *
+ */
+
+#ifndef __getword__
+#define __getword__
+
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+
+ #include "OSHeaders.h"
+
+ char* GetWord( char* toWordPtr, char* fromStrPtr, SInt32 limit );
+ char* GetQuotedWord( char* toWordPtr, char* fromStrPtr, SInt32 limit );
+
+ #ifdef __cplusplus
+ }
+ #endif
+
+
+#endif
diff --git a/CommonUtilitiesLib/IdleTask.cpp b/CommonUtilitiesLib/IdleTask.cpp
new file mode 100644
index 0000000..3614040
--- /dev/null
+++ b/CommonUtilitiesLib/IdleTask.cpp
@@ -0,0 +1,124 @@
+/*
+ *
+ * @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: IdleTask.cpp
+
+ Contains: IdleTasks are identical to normal tasks (see task.h) with one exception:
+
+ You can schedule them for timeouts. If you call SetIdleTimer
+ on one, after the time has elapsed the task object will receive an
+ OS_IDLE event.
+
+
+
+*/
+
+#include "IdleTask.h"
+#include "OSMemory.h"
+#include "OS.h"
+
+//IDLETASKTHREAD IMPLEMENTATION:
+IdleTaskThread* IdleTask::sIdleThread = NULL;
+
+void IdleTaskThread::SetIdleTimer(IdleTask *activeObj, SInt64 msec)
+{
+ //note: OSHeap doesn't support a random remove, so this function
+ //won't change the timeout value if there is already one set
+ if (activeObj->fIdleElem.IsMemberOfAnyHeap())
+ return;
+ activeObj->fIdleElem.SetValue(OS::Milliseconds() + msec);
+
+ {
+ OSMutexLocker locker(&fHeapMutex);
+ fIdleHeap.Insert(&activeObj->fIdleElem);
+ }
+ fHeapCond.Signal();
+}
+
+void IdleTaskThread::CancelTimeout(IdleTask* idleObj)
+{
+ Assert(idleObj != NULL);
+ OSMutexLocker locker(&fHeapMutex);
+ fIdleHeap.Remove(&idleObj->fIdleElem);
+}
+
+void
+IdleTaskThread::Entry()
+{
+ OSMutexLocker locker(&fHeapMutex);
+
+ while (true)
+ {
+ //if there are no events to process, block.
+ if (fIdleHeap.CurrentHeapSize() == 0)
+ fHeapCond.Wait(&fHeapMutex);
+ SInt64 msec = OS::Milliseconds();
+
+ //pop elements out of the heap as long as their timeout time has arrived
+ while ((fIdleHeap.CurrentHeapSize() > 0) && (fIdleHeap.PeekMin()->GetValue() <= msec))
+ {
+ IdleTask* elem = (IdleTask*)fIdleHeap.ExtractMin()->GetEnclosingObject();
+ Assert(elem != NULL);
+ elem->Signal(Task::kIdleEvent);
+ }
+
+ //we are done sending idle events. If there is a lowest tick count, then
+ //we need to sleep until that time.
+ if (fIdleHeap.CurrentHeapSize() > 0)
+ {
+ SInt64 timeoutTime = fIdleHeap.PeekMin()->GetValue();
+ //because sleep takes a 32 bit number
+ timeoutTime -= msec;
+ Assert(timeoutTime > 0);
+ UInt32 smallTime = (UInt32)timeoutTime;
+ fHeapCond.Wait(&fHeapMutex, smallTime);
+ }
+ }
+}
+
+void IdleTask::Initialize()
+{
+ if (sIdleThread == NULL)
+ {
+ sIdleThread = NEW IdleTaskThread();
+ sIdleThread->Start();
+ }
+}
+
+IdleTask::~IdleTask()
+{
+ //clean up stuff used by idle thread routines
+ Assert(sIdleThread != NULL);
+
+ OSMutexLocker locker(&sIdleThread->fHeapMutex);
+
+ //Check to see if there is a pending timeout. If so, get this object
+ //out of the heap
+ if (fIdleElem.IsMemberOfAnyHeap())
+ sIdleThread->CancelTimeout(this);
+}
+
+
+
diff --git a/CommonUtilitiesLib/IdleTask.h b/CommonUtilitiesLib/IdleTask.h
new file mode 100644
index 0000000..5465f34
--- /dev/null
+++ b/CommonUtilitiesLib/IdleTask.h
@@ -0,0 +1,108 @@
+/*
+ *
+ * @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: IdleTask.h
+
+ Contains: IdleTasks are identical to normal tasks (see task.h) with one exception:
+
+ You can schedule them for timeouts. If you call SetIdleTimer
+ on one, after the time has elapsed the task object will receive an
+ OS_IDLE event.
+
+
+
+*/
+
+
+#ifndef _IDLETASK_H_
+#define _IDLETASK_H_
+
+#include "Task.h"
+
+#include "OSThread.h"
+#include "OSHeap.h"
+#include "OSMutex.h"
+#include "OSCond.h"
+
+class IdleTask;
+
+//merely a private implementation detail of IdleTask
+class IdleTaskThread : private OSThread
+{
+private:
+
+ IdleTaskThread() : OSThread(), fHeapMutex() {}
+ virtual ~IdleTaskThread() { Assert(fIdleHeap.CurrentHeapSize() == 0); }
+
+ void SetIdleTimer(IdleTask *idleObj, SInt64 msec);
+ void CancelTimeout(IdleTask *idleObj);
+
+ virtual void Entry();
+ OSHeap fIdleHeap;
+ OSMutex fHeapMutex;
+ OSCond fHeapCond;
+ friend class IdleTask;
+};
+
+
+class IdleTask : public Task
+{
+
+public:
+
+ //Call Initialize before using this class
+ static void Initialize();
+
+ IdleTask() : Task(), fIdleElem() { this->SetTaskName("IdleTask"); fIdleElem.SetEnclosingObject(this); }
+
+ //This object does a "best effort" of making sure a timeout isn't
+ //pending for an object being deleted. In other words, if there is
+ //a timeout pending, and the destructor is called, things will get cleaned
+ //up. But callers must ensure that SetIdleTimer isn't called at the same
+ //time as the destructor, or all hell will break loose.
+ virtual ~IdleTask();
+
+ //SetIdleTimer:
+ //This object will receive an OS_IDLE event in the following number of milliseconds.
+ //Only one timeout can be outstanding, if there is already a timeout scheduled, this
+ //does nothing.
+ void SetIdleTimer(SInt64 msec) { sIdleThread->SetIdleTimer(this, msec); }
+
+ //CancelTimeout
+ //If there is a pending timeout for this object, this function cancels it.
+ //If there is no pending timeout, this function does nothing.
+ //Currently not supported because OSHeap doesn't support random remove
+ void CancelTimeout() { sIdleThread->CancelTimeout(this); }
+
+private:
+
+ OSHeapElem fIdleElem;
+
+ //there is only one idle thread shared by all idle tasks.
+ static IdleTaskThread* sIdleThread;
+
+ friend class IdleTaskThread;
+};
+#endif
diff --git a/CommonUtilitiesLib/MakeDir.c b/CommonUtilitiesLib/MakeDir.c
new file mode 100644
index 0000000..819831c
--- /dev/null
+++ b/CommonUtilitiesLib/MakeDir.c
@@ -0,0 +1,110 @@
+
+/*
+ *
+ * @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@
+ *
+ */
+
+
+#include "MakeDir.h"
+
+#include "PathDelimiter.h"
+
+
+#if (! __MACOS__)
+ #include
+ #include
+ #include
+ #ifndef __solaris__
+ #include
+ #endif
+ #include
+#else
+ #include "BogusDefs.h"
+#endif
+
+#include
+#include
+#include "SafeStdLib.h"
+
+
+int MakeDir(const char* inPath, int mode)
+{
+ struct stat theStatBuffer;
+ if (stat(inPath, &theStatBuffer) == -1)
+ {
+ //this directory doesn't exist, so let's try to create it
+ if (mkdir(inPath, mode) == -1)
+ return -1; //€- (QTSS_ErrorCode)OSThread::GetErrno();
+ }
+ else if (!S_ISDIR(theStatBuffer.st_mode))
+ return -1; //€- QTSS_FileExists;//there is a file at this point in the path!
+
+ //directory exists
+ return 0; //€- QTSS_NoErr;
+}
+
+int RecursiveMakeDir(const char* inPath, int mode)
+{
+ //PL_ASSERT(inPath != NULL);
+ char pathCopy[256];
+ char* thePathTraverser = pathCopy;
+ int theErr;
+ char oldChar;
+
+
+ if ( strlen( inPath ) > 255 )
+ return -1;
+
+ //iterate through the path, replacing kPathDelimiterChar with '\0' as we go
+
+ strcpy( pathCopy, inPath );
+
+ //skip over the first / in the path.
+ if (*thePathTraverser == kPathDelimiterChar )
+ thePathTraverser++;
+
+ while (*thePathTraverser != '\0')
+ {
+ if (*thePathTraverser == kPathDelimiterChar)
+ {
+ //we've found a filename divider. Now that we have a complete
+ //filename, see if this partial path exists.
+
+ //make the partial path into a C string
+ // mkdir does not like a trailing '/'
+ oldChar = *thePathTraverser;
+ *thePathTraverser = '\0';
+ theErr = MakeDir(pathCopy, mode);
+ //there is a directory here. Just continue in our traversal
+ *thePathTraverser = oldChar;
+
+ if (theErr)
+ return theErr;
+ }
+
+ thePathTraverser++;
+ }
+
+ //need to create the last directory in the path
+ return MakeDir(inPath, mode);
+}
diff --git a/CommonUtilitiesLib/MakeDir.h b/CommonUtilitiesLib/MakeDir.h
new file mode 100644
index 0000000..16713e2
--- /dev/null
+++ b/CommonUtilitiesLib/MakeDir.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * @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@
+ *
+ */
+
+#ifndef __makedir__
+#define __makedir__
+
+#if (! __MACOS__)
+ #include
+ #include
+ #include
+ #ifndef __solaris__ || __hpux__
+ #include
+ #endif
+ #include
+#else
+ #include "BogusDefs.h"
+#endif
+
+#ifndef S_IRWXU
+ #define S_IRWXU 0
+#endif
+
+
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+
+
+ int MakeDir( const char* path, int mode );
+ int RecursiveMakeDir( const char*inPath, int mode);
+
+ #ifdef __cplusplus
+ }
+ #endif
+
+
+#endif
diff --git a/CommonUtilitiesLib/MyAssert.cpp b/CommonUtilitiesLib/MyAssert.cpp
new file mode 100644
index 0000000..2b98644
--- /dev/null
+++ b/CommonUtilitiesLib/MyAssert.cpp
@@ -0,0 +1,50 @@
+/*
+ *
+ * @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@
+ *
+ */
+
+#include "MyAssert.h"
+#include "OSHeaders.h"
+#include "SafeStdLib.h"
+
+static AssertLogger* sLogger = NULL;
+
+void SetAssertLogger(AssertLogger* theLogger)
+{
+ sLogger = theLogger;
+}
+
+void MyAssert(char *inMessage)
+{
+ if (sLogger != NULL)
+ sLogger->LogAssert(inMessage);
+ else
+ {
+ qtss_printf("%s\n", inMessage);
+#if __Win32__
+ DebugBreak();
+#else
+ (*(SInt32*)0) = 0;
+#endif
+ }
+}
diff --git a/CommonUtilitiesLib/MyAssert.h b/CommonUtilitiesLib/MyAssert.h
new file mode 100644
index 0000000..6588f40
--- /dev/null
+++ b/CommonUtilitiesLib/MyAssert.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * @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@
+ *
+ */
+
+#ifndef _MYASSERT_H_
+#define _MYASSERT_H_
+
+#include
+#include "SafeStdLib.h"
+
+#ifdef __cplusplus
+class AssertLogger
+{
+ public:
+ // An interface so the MyAssert function can write a message
+ virtual void LogAssert(char* inMessage) = 0;
+ virtual ~AssertLogger(){};
+};
+
+// If a logger is provided, asserts will be logged. Otherwise, asserts will cause a bus error
+void SetAssertLogger(AssertLogger* theLogger);
+#endif
+
+#if ASSERT
+ void MyAssert(char *s);
+
+ #define kAssertBuffSize 256
+
+ #define Assert(condition) { \
+ \
+ if (!(condition)) \
+ { \
+ char s[kAssertBuffSize]; \
+ s[kAssertBuffSize -1] = 0; \
+ qtss_snprintf (s,kAssertBuffSize -1, "_Assert: %s, %d",__FILE__, __LINE__ ); \
+ MyAssert(s); \
+ } }
+
+
+ #define AssertV(condition,errNo) { \
+ if (!(condition)) \
+ { \
+ char s[kAssertBuffSize]; \
+ s[kAssertBuffSize -1] = 0; \
+ qtss_snprintf( s,kAssertBuffSize -1, "_AssertV: %s, %d (%d)",__FILE__, __LINE__, errNo ); \
+ MyAssert(s); \
+ } }
+
+
+ #define Warn(condition) { \
+ if (!(condition)) \
+ qtss_printf( "_Warn: %s, %d\n",__FILE__, __LINE__ ); }
+
+ #define WarnV(condition,msg) { \
+ if (!(condition)) \
+ qtss_printf ("_WarnV: %s, %d (%s)\n",__FILE__, __LINE__, msg ); }
+
+ #define WarnVE(condition,msg,err) { \
+ if (!(condition)) \
+ { char buffer[kAssertBuffSize]; \
+ buffer[kAssertBuffSize -1] = 0; \
+ qtss_printf ("_WarnV: %s, %d (%s, %s [err=%d])\n",__FILE__, __LINE__, msg, qtss_strerror(err,buffer,sizeof(buffer) -1), err ); \
+ } }
+
+#else
+
+ #define Assert(condition) ((void) 0)
+ #define AssertV(condition,errNo) ((void) 0)
+ #define Warn(condition) ((void) 0)
+ #define WarnV(condition,msg) ((void) 0)
+
+#endif
+#endif //_MY_ASSERT_H_
diff --git a/CommonUtilitiesLib/OS.cpp b/CommonUtilitiesLib/OS.cpp
new file mode 100644
index 0000000..6db5045
--- /dev/null
+++ b/CommonUtilitiesLib/OS.cpp
@@ -0,0 +1,470 @@
+/*
+ *
+ * @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: OS.cpp
+
+ Contains: OS utility functions
+
+
+
+
+*/
+
+#include
+#include "SafeStdLib.h"
+#include
+
+#include
+#include
+#include
+
+#include
+
+#ifndef __Win32__
+#include
+#endif
+
+#ifdef __sgi__
+#include
+#endif
+
+#include "OS.h"
+#include "OSThread.h"
+#include "MyAssert.h"
+#include "OSFileSource.h"
+
+#if __MacOSX__
+
+#ifndef __COREFOUNDATION__
+#include
+//extern "C" { void Microseconds (UnsignedWide *microTickCount); }
+#endif
+
+#endif
+
+
+#if (__FreeBSD__ || __MacOSX__)
+ #include
+#endif
+
+#if (__solaris__ || __linux__ || __linuxppc__)
+ #include "StringParser.h"
+#endif
+
+#if __sgi__
+ #include
+#endif
+
+
+double OS::sDivisor = 0;
+double OS::sMicroDivisor = 0;
+SInt64 OS::sMsecSince1970 = 0;
+SInt64 OS::sMsecSince1900 = 0;
+SInt64 OS::sInitialMsec = 0;
+SInt64 OS::sWrapTime = 0;
+SInt64 OS::sCompareWrap = 0;
+SInt64 OS::sLastTimeMilli = 0;
+OSMutex OS::sStdLibOSMutex;
+
+#if DEBUG || __Win32__
+#include "OSMutex.h"
+#include "OSMemory.h"
+static OSMutex* sLastMillisMutex = NULL;
+#endif
+
+void OS::Initialize()
+{
+ Assert (sInitialMsec == 0); // do only once
+ if (sInitialMsec != 0) return;
+ ::tzset();
+
+ //setup t0 value for msec since 1900
+
+ //t.tv_sec is number of seconds since Jan 1, 1970. Convert to seconds since 1900
+ SInt64 the1900Sec = (SInt64) (24 * 60 * 60) * (SInt64) ((70 * 365) + 17) ;
+ sMsecSince1900 = the1900Sec * 1000;
+
+ sWrapTime = (SInt64) 0x00000001 << 32;
+ sCompareWrap = (SInt64) 0xffffffff << 32;
+ sLastTimeMilli = 0;
+
+ sInitialMsec = OS::Milliseconds(); //Milliseconds uses sInitialMsec so this assignment is valid only once.
+
+ sMsecSince1970 = ::time(NULL); // POSIX time always returns seconds since 1970
+ sMsecSince1970 *= 1000; // Convert to msec
+
+
+#if DEBUG || __Win32__
+ sLastMillisMutex = NEW OSMutex();
+#endif
+}
+
+SInt64 OS::Milliseconds()
+{
+/*
+#if __MacOSX__
+
+#if DEBUG
+ OSMutexLocker locker(sLastMillisMutex);
+#endif
+
+ UnsignedWide theMicros;
+ ::Microseconds(&theMicros);
+ SInt64 scalarMicros = theMicros.hi;
+ scalarMicros <<= 32;
+ scalarMicros += theMicros.lo;
+ scalarMicros = ((scalarMicros / 1000) - sInitialMsec) + sMsecSince1970; // convert to msec
+
+#if DEBUG
+ static SInt64 sLastMillis = 0;
+ //Assert(scalarMicros >= sLastMillis); // currently this fails on dual processor machines
+ sLastMillis = scalarMicros;
+#endif
+ return scalarMicros;
+*/
+#if __Win32__
+ OSMutexLocker locker(sLastMillisMutex);
+ // curTimeMilli = timeGetTime() + ((sLastTimeMilli/ 2^32) * 2^32)
+ // using binary & to reduce it to one operation from two
+ // sCompareWrap and sWrapTime are constants that are never changed
+ // sLastTimeMilli is updated with the curTimeMilli after each call to this function
+ SInt64 curTimeMilli = (UInt32) ::timeGetTime() + (sLastTimeMilli & sCompareWrap);
+ if((curTimeMilli - sLastTimeMilli) < 0)
+ {
+ curTimeMilli += sWrapTime;
+ }
+ sLastTimeMilli = curTimeMilli;
+
+ // For debugging purposes
+ //SInt64 tempCurMsec = (curTimeMilli - sInitialMsec) + sMsecSince1970;
+ //SInt32 tempCurSec = tempCurMsec / 1000;
+ //char buffer[kTimeStrSize];
+ //qtss_printf("OS::MilliSeconds current time = %s\n", qtss_ctime(&tempCurSec, buffer, sizeof(buffer)));
+
+ return (curTimeMilli - sInitialMsec) + sMsecSince1970; // convert to application time
+#else
+ struct timeval t;
+ int theErr = ::gettimeofday(&t, NULL);
+ Assert(theErr == 0);
+
+ SInt64 curTime;
+ curTime = t.tv_sec;
+ curTime *= 1000; // sec -> msec
+ curTime += t.tv_usec / 1000; // usec -> msec
+
+ return (curTime - sInitialMsec) + sMsecSince1970;
+#endif
+
+}
+
+SInt64 OS::Microseconds()
+{
+/*
+#if __MacOSX__
+ UnsignedWide theMicros;
+ ::Microseconds(&theMicros);
+ SInt64 theMillis = theMicros.hi;
+ theMillis <<= 32;
+ theMillis += theMicros.lo;
+ return theMillis;
+*/
+#if __Win32__
+ SInt64 curTime = (SInt64) ::timeGetTime(); // system time in milliseconds
+ curTime -= sInitialMsec; // convert to application time
+ curTime *= 1000; // convert to microseconds
+ return curTime;
+#else
+ struct timeval t;
+ int theErr = ::gettimeofday(&t, NULL);
+ Assert(theErr == 0);
+
+ SInt64 curTime;
+ curTime = t.tv_sec;
+ curTime *= 1000000; // sec -> usec
+ curTime += t.tv_usec;
+
+ return curTime - (sInitialMsec * 1000);
+#endif
+}
+
+SInt32 OS::GetGMTOffset()
+{
+#ifdef __Win32__
+ TIME_ZONE_INFORMATION tzInfo;
+ DWORD theErr = ::GetTimeZoneInformation(&tzInfo);
+ if (theErr == TIME_ZONE_ID_INVALID)
+ return 0;
+
+ return ((tzInfo.Bias / 60) * -1);
+#else
+
+ time_t clock;
+ struct tm *tmptr= localtime(&clock);
+ if (tmptr == NULL)
+ return 0;
+
+ return tmptr->tm_gmtoff / 3600;//convert seconds to hours before or after GMT
+#endif
+}
+
+
+SInt64 OS::HostToNetworkSInt64(SInt64 hostOrdered)
+{
+#if BIGENDIAN
+ return hostOrdered;
+#else
+ return (SInt64) ( (UInt64) (hostOrdered << 56) | (UInt64) (((UInt64) 0x00ff0000 << 32) & (hostOrdered << 40))
+ | (UInt64) ( ((UInt64) 0x0000ff00 << 32) & (hostOrdered << 24)) | (UInt64) (((UInt64) 0x000000ff << 32) & (hostOrdered << 8))
+ | (UInt64) ( ((UInt64) 0x00ff0000 << 8) & (hostOrdered >> 8)) | (UInt64) ((UInt64) 0x00ff0000 & (hostOrdered >> 24))
+ | (UInt64) ( (UInt64) 0x0000ff00 & (hostOrdered >> 40)) | (UInt64) ((UInt64) 0x00ff & (hostOrdered >> 56)) );
+#endif
+}
+
+SInt64 OS::NetworkToHostSInt64(SInt64 networkOrdered)
+{
+#if BIGENDIAN
+ return networkOrdered;
+#else
+ return (SInt64) ( (UInt64) (networkOrdered << 56) | (UInt64) (((UInt64) 0x00ff0000 << 32) & (networkOrdered << 40))
+ | (UInt64) ( ((UInt64) 0x0000ff00 << 32) & (networkOrdered << 24)) | (UInt64) (((UInt64) 0x000000ff << 32) & (networkOrdered << 8))
+ | (UInt64) ( ((UInt64) 0x00ff0000 << 8) & (networkOrdered >> 8)) | (UInt64) ((UInt64) 0x00ff0000 & (networkOrdered >> 24))
+ | (UInt64) ( (UInt64) 0x0000ff00 & (networkOrdered >> 40)) | (UInt64) ((UInt64) 0x00ff & (networkOrdered >> 56)) );
+#endif
+}
+
+
+OS_Error OS::MakeDir(char *inPath)
+{
+ struct stat theStatBuffer;
+ if (::stat(inPath, &theStatBuffer) == -1)
+ {
+ //this directory doesn't exist, so let's try to create it
+#ifdef __Win32__
+ if (::mkdir(inPath) == -1)
+#else
+ if (::mkdir(inPath, S_IRWXU) == -1)
+#endif
+ return (OS_Error)OSThread::GetErrno();
+ }
+#ifdef __Win32__
+ else if (!(theStatBuffer.st_mode & _S_IFDIR)) // MSVC++ doesn't define the S_ISDIR macro
+ return EEXIST; // there is a file at this point in the path!
+#else
+ else if (!S_ISDIR(theStatBuffer.st_mode))
+ return EEXIST;//there is a file at this point in the path!
+#endif
+
+ //directory exists
+ return OS_NoErr;
+}
+
+OS_Error OS::RecursiveMakeDir(char *inPath)
+{
+ Assert(inPath != NULL);
+
+ //iterate through the path, replacing '/' with '\0' as we go
+ char *thePathTraverser = inPath;
+
+ //skip over the first / in the path.
+ if (*thePathTraverser == kPathDelimiterChar)
+ thePathTraverser++;
+
+ while (*thePathTraverser != '\0')
+ {
+ if (*thePathTraverser == kPathDelimiterChar)
+ {
+ //we've found a filename divider. Now that we have a complete
+ //filename, see if this partial path exists.
+
+ //make the partial path into a C string
+ *thePathTraverser = '\0';
+ OS_Error theErr = MakeDir(inPath);
+ //there is a directory here. Just continue in our traversal
+ *thePathTraverser = kPathDelimiterChar;
+
+ if (theErr != OS_NoErr)
+ return theErr;
+ }
+ thePathTraverser++;
+ }
+
+ //need to create the last directory in the path
+ return MakeDir(inPath);
+}
+
+Bool16 OS::ThreadSafe()
+{
+
+#if (__MacOSX__) // check for version 7 or greater for thread safe stdlib
+ char releaseStr[32] = "";
+ size_t strLen = sizeof(releaseStr);
+ int mib[2];
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_OSRELEASE;
+
+ UInt32 majorVers = 0;
+ int err = sysctl(mib,2, releaseStr, &strLen, NULL,0);
+ if (err == 0)
+ {
+ StrPtrLen rStr(releaseStr,strLen);
+ char* endMajor = rStr.FindString(".");
+ if (endMajor != NULL) // truncate to an int value.
+ *endMajor = 0;
+
+ if (::strlen(releaseStr) > 0) //convert to an int
+ ::sscanf(releaseStr, "%"_U32BITARG_"", &majorVers);
+ }
+ if (majorVers < 7) // less than OS X Panther 10.3
+ return false; // force 1 worker thread because < 10.3 means std c lib is not thread safe.
+
+#endif
+
+ return true;
+
+}
+
+
+UInt32 OS::GetNumProcessors()
+{
+#if (__Win32__)
+ SYSTEM_INFO theSystemInfo;
+ ::GetSystemInfo(&theSystemInfo);
+
+ return (UInt32)theSystemInfo.dwNumberOfProcessors;
+#endif
+
+#if (__MacOSX__ || __FreeBSD__)
+ int numCPUs = 1;
+ size_t len = sizeof(numCPUs);
+ int mib[2];
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ (void) ::sysctl(mib,2,&numCPUs,&len,NULL,0);
+ if (numCPUs < 1)
+ numCPUs = 1;
+ return (UInt32) numCPUs;
+#endif
+
+#if(__linux__ || __linuxppc__)
+
+ char cpuBuffer[8192] = "";
+ StrPtrLen cpuInfoBuf(cpuBuffer, sizeof(cpuBuffer));
+ FILE *cpuFile = ::fopen( "/proc/cpuinfo", "r" );
+ if (cpuFile)
+ { cpuInfoBuf.Len = ::fread(cpuInfoBuf.Ptr, sizeof(char), cpuInfoBuf.Len, cpuFile);
+ ::fclose(cpuFile);
+ }
+
+ StringParser cpuInfoFileParser(&cpuInfoBuf);
+ StrPtrLen line;
+ StrPtrLen word;
+ UInt32 numCPUs = 0;
+
+ while( cpuInfoFileParser.GetDataRemaining() != 0 )
+ {
+ cpuInfoFileParser.GetThruEOL(&line); // Read each line
+ StringParser lineParser(&line);
+ lineParser.ConsumeWhitespace(); //skip over leading whitespace
+
+ if (lineParser.GetDataRemaining() == 0) // must be an empty line
+ continue;
+
+ lineParser.ConsumeUntilWhitespace(&word);
+
+ if ( word.Equal("processor") ) // found a processor as first word in line
+ { numCPUs ++;
+ }
+ }
+
+ if (numCPUs == 0)
+ numCPUs = 1;
+
+ return numCPUs;
+#endif
+
+#if(__solaris__)
+{
+ UInt32 numCPUs = 0;
+ char linebuff[512] = "";
+ StrPtrLen line(linebuff, sizeof(linebuff));
+ StrPtrLen word;
+
+ FILE *p = ::popen("uname -X","r");
+ while((::fgets(linebuff, sizeof(linebuff -1), p)) > 0)
+ {
+ StringParser lineParser(&line);
+ lineParser.ConsumeWhitespace(); //skip over leading whitespace
+
+ if (lineParser.GetDataRemaining() == 0) // must be an empty line
+ continue;
+
+ lineParser.ConsumeUntilWhitespace(&word);
+
+ if ( word.Equal("NumCPU")) // found a tag as first word in line
+ {
+ lineParser.GetThru(NULL,'=');
+ lineParser.ConsumeWhitespace(); //skip over leading whitespace
+ lineParser.ConsumeUntilWhitespace(&word); //read the number of cpus
+ if (word.Len > 0)
+ ::sscanf(word.Ptr, "%"_U32BITARG_"", &numCPUs);
+
+ break;
+ }
+ }
+ if (numCPUs == 0)
+ numCPUs = 1;
+
+ ::pclose(p);
+
+ return numCPUs;
+}
+#endif
+
+#if(__sgi__)
+ UInt32 numCPUs = 0;
+
+ numCPUs = sysconf(_SC_NPROC_ONLN);
+
+ return numCPUs;
+#endif
+
+
+ return 1;
+}
+
+
+//CISCO provided fix for integer + fractional fixed64.
+SInt64 OS::TimeMilli_To_Fixed64Secs(SInt64 inMilliseconds)
+{
+ SInt64 result = inMilliseconds / 1000; // The result is in lower bits.
+ result <<= 32; // shift it to higher 32 bits
+ // Take the remainder (rem = inMilliseconds%1000) and multiply by
+ // 2**32, divide by 1000, effectively this gives (rem/1000) as a
+ // binary fraction.
+ double p = ldexp((double)(inMilliseconds%1000), +32) / 1000.;
+ UInt32 frac = (UInt32)p;
+ result |= frac;
+ return result;
+}
diff --git a/CommonUtilitiesLib/OS.h b/CommonUtilitiesLib/OS.h
new file mode 100644
index 0000000..0fa77e9
--- /dev/null
+++ b/CommonUtilitiesLib/OS.h
@@ -0,0 +1,144 @@
+/*
+ *
+ * @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: OS.h
+
+ Contains: OS utility functions. Memory allocation, time, etc.
+
+
+
+*/
+
+#ifndef _OS_H_
+#define _OS_H_
+
+
+#include "OSHeaders.h"
+#include "OSMutex.h"
+#include
+
+class OS
+{
+ public:
+
+ //call this before calling anything else
+ static void Initialize();
+
+ static SInt32 Min(SInt32 a, SInt32 b) { if (a < b) return a; return b; }
+
+ //
+ // Milliseconds always returns milliseconds since Jan 1, 1970 GMT.
+ // This basically makes it the same as a POSIX time_t value, except
+ // in msec, not seconds. To convert to a time_t, divide by 1000.
+ static SInt64 Milliseconds();
+
+ static SInt64 Microseconds();
+
+ // Some processors (MIPS, Sparc) cannot handle non word aligned memory
+ // accesses. So, we need to provide functions to safely get at non-word
+ // aligned memory.
+ static inline UInt32 GetUInt32FromMemory(UInt32* inP);
+
+ //because the OS doesn't seem to have these functions
+ static SInt64 HostToNetworkSInt64(SInt64 hostOrdered);
+ static SInt64 NetworkToHostSInt64(SInt64 networkOrdered);
+
+ static SInt64 TimeMilli_To_Fixed64Secs(SInt64 inMilliseconds); //new CISCO provided implementation
+ //disable: calculates integer value only { return (SInt64) ( (Float64) inMilliseconds / 1000) * ((SInt64) 1 << 32 ) ; }
+ static SInt64 Fixed64Secs_To_TimeMilli(SInt64 inFixed64Secs)
+ { UInt64 value = (UInt64) inFixed64Secs; return (value >> 32) * 1000 + (((value % ((UInt64) 1 << 32)) * 1000) >> 32); }
+
+ //This converts the local time (from OS::Milliseconds) to NTP time.
+ static SInt64 TimeMilli_To_1900Fixed64Secs(SInt64 inMilliseconds)
+ { return TimeMilli_To_Fixed64Secs(sMsecSince1900) + TimeMilli_To_Fixed64Secs(inMilliseconds); }
+
+ static SInt64 TimeMilli_To_UnixTimeMilli(SInt64 inMilliseconds)
+ { return inMilliseconds; }
+
+ static time_t TimeMilli_To_UnixTimeSecs(SInt64 inMilliseconds)
+ { return (time_t) ( (SInt64) TimeMilli_To_UnixTimeMilli(inMilliseconds) / (SInt64) 1000); }
+
+ static time_t UnixTime_Secs(void) // Seconds since 1970
+ { return TimeMilli_To_UnixTimeSecs(Milliseconds()); }
+
+ static time_t Time1900Fixed64Secs_To_UnixTimeSecs(SInt64 in1900Fixed64Secs)
+ { return (time_t)( (SInt64) ((SInt64) ( in1900Fixed64Secs - TimeMilli_To_Fixed64Secs(sMsecSince1900) ) / ((SInt64) 1 << 32) ) ); }
+
+ static SInt64 Time1900Fixed64Secs_To_TimeMilli(SInt64 in1900Fixed64Secs)
+ { return ( (SInt64) ( (Float64) ((SInt64) in1900Fixed64Secs - (SInt64) TimeMilli_To_Fixed64Secs(sMsecSince1900) ) / (Float64) ((SInt64) 1 << 32) ) * 1000) ; }
+
+ // Returns the offset in hours between local time and GMT (or UTC) time.
+ static SInt32 GetGMTOffset();
+
+ //Both these functions return QTSS_NoErr, QTSS_FileExists, or POSIX errorcode
+ //Makes whatever directories in this path that don't exist yet
+ static OS_Error RecursiveMakeDir(char *inPath);
+ //Makes the directory at the end of this path
+ static OS_Error MakeDir(char *inPath);
+
+ // Discovery of how many processors are on this machine
+ static UInt32 GetNumProcessors();
+
+ // CPU Load
+ static Float32 GetCurrentCPULoadPercent();
+
+ // Mutex for StdLib calls
+ static OSMutex* GetStdLibMutex() { return &sStdLibOSMutex; }
+
+ static SInt64 InitialMSec() { return sInitialMsec; }
+ static Float32 StartTimeMilli_Float() { return (Float32) ( (Float64) ( (SInt64) OS::Milliseconds() - (SInt64) OS::InitialMSec()) / (Float64) 1000.0 ); }
+ static SInt64 StartTimeMilli_Int() { return (OS::Milliseconds() - OS::InitialMSec()); }
+
+ static Bool16 ThreadSafe();
+
+ private:
+
+ static double sDivisor;
+ static double sMicroDivisor;
+ static SInt64 sMsecSince1900;
+ static SInt64 sMsecSince1970;
+ static SInt64 sInitialMsec;
+ static SInt32 sMemoryErr;
+ static void SetDivisor();
+ static SInt64 sWrapTime;
+ static SInt64 sCompareWrap;
+ static SInt64 sLastTimeMilli;
+ static OSMutex sStdLibOSMutex;
+};
+
+inline UInt32 OS::GetUInt32FromMemory(UInt32* inP)
+{
+#if ALLOW_NON_WORD_ALIGN_ACCESS
+ return *inP;
+#else
+ char* tempPtr = (char*)inP;
+ UInt32 temp = 0;
+ ::memcpy(&temp, tempPtr, sizeof(UInt32));
+ return temp;
+#endif
+}
+
+
+#endif
diff --git a/CommonUtilitiesLib/OSArrayObjectDeleter.h b/CommonUtilitiesLib/OSArrayObjectDeleter.h
new file mode 100644
index 0000000..d75df30
--- /dev/null
+++ b/CommonUtilitiesLib/OSArrayObjectDeleter.h
@@ -0,0 +1,88 @@
+/*
+ *
+ * @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: OSArrayObjectDeleter.h
+
+ Contains: Auto object for deleting arrays.
+
+
+
+*/
+
+#ifndef __OS_ARRAY_OBJECT_DELETER_H__
+#define __OS_ARRAY_OBJECT_DELETER_H__
+
+#include "MyAssert.h"
+
+template
+class OSArrayObjectDeleter
+{
+ public:
+ OSArrayObjectDeleter() : fT(NULL) {}
+ OSArrayObjectDeleter(T* victim) : fT(victim) {}
+ ~OSArrayObjectDeleter() { delete [] fT; }
+
+ void ClearObject() { fT = NULL; }
+
+ void SetObject(T* victim)
+ {
+ Assert(fT == NULL);
+ fT = victim;
+ }
+ T* GetObject() { return fT; }
+
+ operator T*() { return fT; }
+
+ private:
+
+ T* fT;
+};
+
+
+template
+class OSPtrDeleter
+{
+ public:
+ OSPtrDeleter() : fT(NULL) {}
+ OSPtrDeleter(T* victim) : fT(victim) {}
+ ~OSPtrDeleter() { delete fT; }
+
+ void ClearObject() { fT = NULL; }
+
+ void SetObject(T* victim)
+ { Assert(fT == NULL);
+ fT = victim;
+ }
+
+ private:
+
+ T* fT;
+};
+
+
+typedef OSArrayObjectDeleter OSCharPointerArrayDeleter;
+typedef OSArrayObjectDeleter OSCharArrayDeleter;
+
+#endif //__OS_OBJECT_ARRAY_DELETER_H__
diff --git a/CommonUtilitiesLib/OSBufferPool.cpp b/CommonUtilitiesLib/OSBufferPool.cpp
new file mode 100644
index 0000000..177dd69
--- /dev/null
+++ b/CommonUtilitiesLib/OSBufferPool.cpp
@@ -0,0 +1,60 @@
+/*
+ *
+ * @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: OSBufferPool.cpp
+
+ Contains: Fast access to fixed size buffers.
+
+ Written By: Denis Serenyi
+
+*/
+
+#include "OSBufferPool.h"
+#include "OSMemory.h"
+
+void* OSBufferPool::Get()
+{
+ OSMutexLocker locker(&fMutex);
+ if (fQueue.GetLength() == 0)
+ {
+ fTotNumBuffers++;
+ char* theNewBuf = NEW char[fBufSize + sizeof(OSQueueElem)];
+
+ //
+ // We need to construct a Queue Element, but we don't actually need
+ // to use it in this function, so to avoid a compiler warning just
+ // don't assign the result to anything.
+ (void)new (theNewBuf) OSQueueElem(theNewBuf + sizeof(OSQueueElem));
+
+ return theNewBuf + sizeof(OSQueueElem);
+ }
+ return fQueue.DeQueue()->GetEnclosingObject();
+}
+
+void OSBufferPool::Put(void* inBuffer)
+{
+ OSMutexLocker locker(&fMutex);
+ fQueue.EnQueue((OSQueueElem*)((char*)inBuffer - sizeof(OSQueueElem)));
+}
diff --git a/CommonUtilitiesLib/OSBufferPool.h b/CommonUtilitiesLib/OSBufferPool.h
new file mode 100644
index 0000000..feebe44
--- /dev/null
+++ b/CommonUtilitiesLib/OSBufferPool.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * @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: OSBufferPool.h
+
+ Contains: Fast access to fixed size buffers.
+
+ Written By: Denis Serenyi
+
+*/
+
+#ifndef __OS_BUFFER_POOL_H__
+#define __OS_BUFFER_POOL_H__
+
+#include "OSQueue.h"
+#include "OSMutex.h"
+
+class OSBufferPool
+{
+ public:
+
+ OSBufferPool(UInt32 inBufferSize) : fBufSize(inBufferSize), fTotNumBuffers(0) {}
+
+ //
+ // This object currently *does not* clean up for itself when
+ // you destruct it!
+ ~OSBufferPool() {}
+
+ //
+ // ACCESSORS
+ UInt32 GetTotalNumBuffers() { return fTotNumBuffers; }
+ UInt32 GetNumAvailableBuffers() { return fQueue.GetLength(); }
+
+ //
+ // All these functions are thread-safe
+
+ //
+ // Gets a buffer out of the pool. This buffer must be replaced
+ // by calling Put when you are done with it.
+ void* Get();
+
+ //
+ // Returns a buffer retreived by Get back to the pool.
+ void Put(void* inBuffer);
+
+ private:
+
+ OSMutex fMutex;
+ OSQueue fQueue;
+ UInt32 fBufSize;
+ UInt32 fTotNumBuffers;
+};
+
+#endif //__OS_BUFFER_POOL_H__
diff --git a/CommonUtilitiesLib/OSCodeFragment.cpp b/CommonUtilitiesLib/OSCodeFragment.cpp
new file mode 100644
index 0000000..c831113
--- /dev/null
+++ b/CommonUtilitiesLib/OSCodeFragment.cpp
@@ -0,0 +1,153 @@
+/*
+ *
+ * @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: OSCodeFragment.cpp
+
+ Contains: Implementation of object defined in OSCodeFragment.h
+
+
+*/
+
+#include
+#include "SafeStdLib.h"
+#include
+#include "MyAssert.h"
+
+#if __Win32__
+ // Win32 includes here
+#elif __MacOSX__
+ #include
+ #include
+#else
+#include
+#endif
+
+#include "OSCodeFragment.h"
+
+void OSCodeFragment::Initialize()
+{
+// does nothing...should do any CFM initialization here
+}
+
+OSCodeFragment::OSCodeFragment(const char* inPath)
+: fFragmentP(NULL)
+{
+#if defined(HPUX) || defined(HPUX10)
+ shl_t handle;
+ fFragmentP = shl_load(inPath, BIND_IMMEDIATE|BIND_VERBOSE|BIND_NOSTART, 0L);
+#elif defined(OSF1) ||\
+ (defined(__FreeBSD_version) && (__FreeBSD_version >= 220000))
+ fFragmentP = dlopen((char *)inPath, RTLD_NOW | RTLD_GLOBAL);
+#elif defined(__FreeBSD__)
+ fFragmentP = dlopen(inPath, RTLD_NOW);
+#elif defined(__sgi__)
+ fFragmentP = dlopen(inPath, RTLD_NOW); // not sure this should be either RTLD_NOW or RTLD_LAZY
+#elif defined(__Win32__)
+ fFragmentP = ::LoadLibrary(inPath);
+#elif defined(__MacOSX__)
+ CFStringRef theString = CFStringCreateWithCString( kCFAllocatorDefault, inPath, kCFStringEncodingASCII);
+
+ //
+ // In MacOSX, our "fragments" are CF bundles, which are really
+ // directories, so our paths are paths to a directory
+ CFURLRef bundleURL = CFURLCreateWithFileSystemPath( kCFAllocatorDefault,
+ theString,
+ kCFURLPOSIXPathStyle,
+ true);
+
+ //
+ // I figure CF is safe about having NULL passed
+ // into its functions (if fBundle failed to get created).
+ // So, I won't worry about error checking myself
+ fFragmentP = CFBundleCreate( kCFAllocatorDefault, bundleURL );
+ Boolean success = false;
+ if (fFragmentP != NULL)
+ success = CFBundleLoadExecutable( fFragmentP );
+ if (!success && fFragmentP != NULL)
+ {
+ CFRelease( fFragmentP );
+ fFragmentP = NULL;
+ }
+
+ CFRelease(bundleURL);
+ CFRelease(theString);
+
+#else
+ fFragmentP = dlopen(inPath, RTLD_NOW | RTLD_GLOBAL);
+ //fprintf (stderr, "%s\n", dlerror());
+
+#endif
+}
+
+OSCodeFragment::~OSCodeFragment()
+{
+ if (fFragmentP == NULL)
+ return;
+
+#if defined(HPUX) || defined(HPUX10)
+ shl_unload((shl_t)fFragmentP);
+#elif defined(__Win32__)
+ BOOL theErr = ::FreeLibrary(fFragmentP);
+ Assert(theErr);
+#elif defined(__MacOSX__)
+ CFBundleUnloadExecutable( fFragmentP );
+ CFRelease( fFragmentP );
+#else
+ dlclose(fFragmentP);
+#endif
+}
+
+void* OSCodeFragment::GetSymbol(const char* inSymbolName)
+{
+ if (fFragmentP == NULL)
+ return NULL;
+
+#if defined(HPUX) || defined(HPUX10)
+ void *symaddr = NULL;
+ int status;
+
+ errno = 0;
+ status = shl_findsym((shl_t *)&fFragmentP, symname, TYPE_PROCEDURE, &symaddr);
+ if (status == -1 && errno == 0) /* try TYPE_DATA instead */
+ status = shl_findsym((shl_t *)&fFragmentP, inSymbolName, TYPE_DATA, &symaddr);
+ return (status == -1 ? NULL : symaddr);
+#elif defined(DLSYM_NEEDS_UNDERSCORE)
+ char *symbol = (char*)malloc(sizeof(char)*(strlen(inSymbolName)+2));
+ void *retval;
+ qtss_sprintf(symbol, "_%s", inSymbolName);
+ retval = dlsym(fFragmentP, symbol);
+ free(symbol);
+ return retval;
+#elif defined(__Win32__)
+ return ::GetProcAddress(fFragmentP, inSymbolName);
+#elif defined(__MacOSX__)
+ CFStringRef theString = CFStringCreateWithCString( kCFAllocatorDefault, inSymbolName, kCFStringEncodingASCII);
+ void* theSymbol = (void*)CFBundleGetFunctionPointerForName( fFragmentP, theString );
+ CFRelease(theString);
+ return theSymbol;
+#else
+ return dlsym(fFragmentP, inSymbolName);
+#endif
+}
diff --git a/CommonUtilitiesLib/OSCodeFragment.h b/CommonUtilitiesLib/OSCodeFragment.h
new file mode 100644
index 0000000..bf0c001
--- /dev/null
+++ b/CommonUtilitiesLib/OSCodeFragment.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * @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: OSDynamicLoader.h
+
+ Contains: OS abstraction for loading code fragments.
+
+
+
+*/
+
+#ifndef _OS_CODEFRAGMENT_H_
+#define _OS_CODEFRAGMENT_H_
+
+#include
+#include "SafeStdLib.h"
+#include "OSHeaders.h"
+
+#ifdef __MacOSX__
+#include
+#endif
+
+class OSCodeFragment
+{
+ public:
+
+ static void Initialize();
+
+ OSCodeFragment(const char* inPath);
+ ~OSCodeFragment();
+
+ Bool16 IsValid() { return (fFragmentP != NULL); }
+ void* GetSymbol(const char* inSymbolName);
+
+ private:
+
+#ifdef __Win32__
+ HMODULE fFragmentP;
+#elif __MacOSX__
+ CFBundleRef fFragmentP;
+#else
+ void* fFragmentP;
+#endif
+};
+
+#endif//_OS_CODEFRAGMENT_H_
diff --git a/CommonUtilitiesLib/OSCond.cpp b/CommonUtilitiesLib/OSCond.cpp
new file mode 100644
index 0000000..673a72e
--- /dev/null
+++ b/CommonUtilitiesLib/OSCond.cpp
@@ -0,0 +1,119 @@
+/*
+ *
+ * @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: OSCond.cpp
+
+ Contains: Implementation of OSCond class
+
+
+
+*/
+
+#include "OSCond.h"
+#include "OSMutex.h"
+#include "OSThread.h"
+#include "MyAssert.h"
+
+#if __PTHREADS_MUTEXES__
+#include
+#endif
+
+
+OSCond::OSCond()
+{
+#ifdef __Win32__
+ fCondition = ::CreateEvent(NULL, FALSE, FALSE, NULL);
+ Assert(fCondition != NULL);
+#elif __PTHREADS_MUTEXES__
+ #if __MacOSX__
+ int ret = pthread_cond_init(&fCondition, NULL);
+ Assert(ret == 0);
+ #else
+ pthread_condattr_t cond_attr;
+ pthread_condattr_init(&cond_attr);
+ int ret = pthread_cond_init(&fCondition, &cond_attr);
+ Assert(ret == 0);
+ #endif
+#else
+ fCondition = mycondition_alloc();
+ Assert(fCondition != NULL);
+#endif
+}
+
+OSCond::~OSCond()
+{
+#ifdef __Win32__
+ BOOL theErr = ::CloseHandle(fCondition);
+ Assert(theErr == TRUE);
+#elif __PTHREADS_MUTEXES__
+ pthread_cond_destroy(&fCondition);
+#else
+ Assert(fCondition != NULL);
+ mycondition_free(fCondition);
+#endif
+}
+
+#if __PTHREADS_MUTEXES__
+void OSCond::TimedWait(OSMutex* inMutex, SInt32 inTimeoutInMilSecs)
+{
+ struct timespec ts;
+ struct timeval tv;
+ struct timezone tz;
+ int sec, usec;
+
+ //These platforms do refcounting manually, and wait will release the mutex,
+ // so we need to update the counts here
+
+ inMutex->fHolderCount--;
+ inMutex->fHolder = 0;
+
+
+ if (inTimeoutInMilSecs == 0)
+ (void)pthread_cond_wait(&fCondition, &inMutex->fMutex);
+ else
+ {
+ gettimeofday(&tv, &tz);
+ sec = inTimeoutInMilSecs / 1000;
+ inTimeoutInMilSecs = inTimeoutInMilSecs - (sec * 1000);
+ Assert(inTimeoutInMilSecs < 1000);
+ usec = inTimeoutInMilSecs * 1000;
+ Assert(tv.tv_usec < 1000000);
+ ts.tv_sec = tv.tv_sec + sec;
+ ts.tv_nsec = (tv.tv_usec + usec) * 1000;
+ Assert(ts.tv_nsec < 2000000000);
+ if(ts.tv_nsec > 999999999)
+ {
+ ts.tv_sec++;
+ ts.tv_nsec -= 1000000000;
+ }
+ (void)pthread_cond_timedwait(&fCondition, &inMutex->fMutex, &ts);
+ }
+
+
+ inMutex->fHolderCount++;
+ inMutex->fHolder = pthread_self();
+
+}
+#endif
diff --git a/CommonUtilitiesLib/OSCond.h b/CommonUtilitiesLib/OSCond.h
new file mode 100644
index 0000000..c72ce5a
--- /dev/null
+++ b/CommonUtilitiesLib/OSCond.h
@@ -0,0 +1,129 @@
+/*
+ *
+ * @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: OSCond.h
+
+ Contains: A simple condition variable abstraction
+
+
+
+
+*/
+
+#ifndef _OSCOND_H_
+#define _OSCOND_H_
+
+#ifndef __Win32__
+ #if __PTHREADS_MUTEXES__
+ #include
+ #else
+ #include "mycondition.h"
+ #endif
+#endif
+
+#include "OSMutex.h"
+#include "MyAssert.h"
+
+class OSCond
+{
+ public:
+
+ OSCond();
+ ~OSCond();
+
+ inline void Signal();
+ inline void Wait(OSMutex* inMutex, SInt32 inTimeoutInMilSecs = 0);
+ inline void Broadcast();
+
+ private:
+
+#ifdef __Win32__
+ HANDLE fCondition;
+ UInt32 fWaitCount;
+#elif __PTHREADS_MUTEXES__
+ pthread_cond_t fCondition;
+ void TimedWait(OSMutex* inMutex, SInt32 inTimeoutInMilSecs);
+#else
+ mycondition_t fCondition;
+#endif
+};
+
+inline void OSCond::Wait(OSMutex* inMutex, SInt32 inTimeoutInMilSecs)
+{
+#ifdef __Win32__
+ DWORD theTimeout = INFINITE;
+ if (inTimeoutInMilSecs > 0)
+ theTimeout = inTimeoutInMilSecs;
+ inMutex->Unlock();
+ fWaitCount++;
+ DWORD theErr = ::WaitForSingleObject(fCondition, theTimeout);
+ fWaitCount--;
+ Assert((theErr == WAIT_OBJECT_0) || (theErr == WAIT_TIMEOUT));
+ inMutex->Lock();
+#elif __PTHREADS_MUTEXES__
+ this->TimedWait(inMutex, inTimeoutInMilSecs);
+#else
+ Assert(fCondition != NULL);
+ mycondition_wait(fCondition, inMutex->fMutex, inTimeoutInMilSecs);
+#endif
+}
+
+inline void OSCond::Signal()
+{
+#ifdef __Win32__
+ BOOL theErr = ::SetEvent(fCondition);
+ Assert(theErr == TRUE);
+#elif __PTHREADS_MUTEXES__
+ pthread_cond_signal(&fCondition);
+#else
+ Assert(fCondition != NULL);
+ mycondition_signal(fCondition);
+#endif
+}
+
+inline void OSCond::Broadcast()
+{
+#ifdef __Win32__
+ //
+ // There doesn't seem like any more elegant way to
+ // implement Broadcast using events in Win32.
+ // This will work, it may generate spurious wakeups,
+ // but condition variables are allowed to generate
+ // spurious wakeups
+ UInt32 waitCount = fWaitCount;
+ for (UInt32 x = 0; x < waitCount; x++)
+ {
+ BOOL theErr = ::SetEvent(fCondition);
+ Assert(theErr == TRUE);
+ }
+#elif __PTHREADS_MUTEXES__
+ pthread_cond_broadcast(&fCondition);
+#else
+ Assert(fCondition != NULL);
+ mycondition_broadcast(fCondition);
+#endif
+}
+
+#endif //_OSCOND_H_
diff --git a/CommonUtilitiesLib/OSFileSource.cpp b/CommonUtilitiesLib/OSFileSource.cpp
new file mode 100644
index 0000000..b16d5ba
--- /dev/null
+++ b/CommonUtilitiesLib/OSFileSource.cpp
@@ -0,0 +1,633 @@
+/*
+ *
+ * @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: osfile.cpp
+
+ Contains: simple file abstraction
+
+
+
+
+*/
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#ifndef __Win32__
+#include
+#endif
+
+#include "OSFileSource.h"
+#include "OSMemory.h"
+#include "OSThread.h"
+#include "OS.h"
+#include "OSQueue.h"
+#include "OSHeaders.h"
+
+#define FILE_SOURCE_DEBUG 0
+#define FILE_SOURCE_BUFFTEST 0
+#define TEST_TIME 0
+
+#if TEST_TIME
+static SInt64 startTime = 0;
+static SInt64 durationTime = 0;
+static SInt32 sReadCount = 0;
+static SInt32 sByteCount = 0;
+static Bool16 sMovie = false;
+
+#endif
+
+
+#if READ_LOG
+extern UInt32 xTrackID;
+void OSFileSource::SetLog(const char *inPath)
+{
+ fFilePath[0] =0;
+ ::strcpy(fFilePath,inPath);
+
+ if (fFile != -1 && fFileLog == NULL)
+ {
+ ::strcat(fFilePath,inPath);
+ ::strcat(fFilePath,".readlog");
+ fFileLog = ::fopen(fFilePath,"w+");
+ if (fFileLog && IsValid())
+ { qtss_fprintf(fFileLog, "%s","QTFILE_READ_LOG\n");
+ qtss_fprintf(fFileLog, "size: %qu\n",GetLength());
+ qtss_printf("OSFileSource::SetLog=%s\n",fFilePath);
+
+ }
+ ::fclose(fFileLog);
+ }
+}
+#else
+void OSFileSource::SetLog(const char *inPath)
+{
+
+#if FILE_SOURCE_DEBUG
+ qtss_printf("OSFileSource::SetLog=%s\n",inPath);
+#endif
+
+}
+#endif
+
+
+
+FileBlockBuffer::~FileBlockBuffer(void)
+{
+ if (fDataBuffer != NULL)
+ {
+ Assert (fDataBuffer[fBufferSize] == 0);
+
+#if FILE_SOURCE_DEBUG
+ ::memset( (char *)fDataBuffer,0, fBufferSize);
+ qtss_printf("FileBlockBuffer::~FileBlockBuffer delete %"_U32BITARG_" this=%"_U32BITARG_"\n",fDataBuffer, this);
+#endif
+ delete fDataBuffer;
+ fDataBuffer = NULL;
+ fArrayIndex = -1;
+ }
+ else
+ Assert(false);
+}
+
+void FileBlockBuffer::AllocateBuffer(UInt32 buffSize)
+{
+ fBufferSize = buffSize;
+ fDataBuffer = NEW char[buffSize + 1];
+ fDataBuffer[buffSize] = 0;
+
+#if FILE_SOURCE_DEBUG
+ this->CleanBuffer();
+ qtss_printf("FileBlockBuffer::FileBlockBuffer allocate buff ptr =%"_U32BITARG_" len=%"_U32BITARG_" this=%"_U32BITARG_"\n",fDataBuffer,buffSize,this);
+#endif
+
+}
+
+void FileBlockBuffer::TestBuffer(void)
+{
+
+#if FILE_SOURCE_BUFFTEST
+ if (fDataBuffer != NULL)
+ Assert (fDataBuffer[fBufferSize] == 0);
+#endif
+
+}
+
+void FileBlockPool::MarkUsed(FileBlockBuffer* inBuffPtr)
+{
+ if (NULL == inBuffPtr)
+ return;
+
+ if (fQueue.GetTail() != inBuffPtr->GetQElem()) // Least Recently Used tail is last accessed
+ {
+ fQueue.Remove(inBuffPtr->GetQElem());
+ fQueue.EnQueue(inBuffPtr->GetQElem()); // put on tail
+ }
+}
+
+FileBlockBuffer *FileBlockPool::GetBufferElement(UInt32 bufferSizeBytes)
+{
+ FileBlockBuffer* theNewBuf = NULL;
+ if ( fNumCurrentBuffers < fMaxBuffers)
+ {
+#if FILE_SOURCE_DEBUG
+ qtss_printf("FileBlockPool::GetBufferElement NEW element fNumCurrentBuffers=%"_U32BITARG_" fMaxBuffers=%"_U32BITARG_" fBufferUnitSizeBytes=%"_U32BITARG_" bufferSizeBytes=%"_U32BITARG_"\n",fNumCurrentBuffers,fMaxBuffers,fBufferUnitSizeBytes,bufferSizeBytes);
+#endif
+ theNewBuf = NEW FileBlockBuffer();
+ theNewBuf->AllocateBuffer(bufferSizeBytes);
+ fNumCurrentBuffers++;
+ theNewBuf->fQElem.SetEnclosingObject(theNewBuf);
+ fQueue.EnQueue(theNewBuf->GetQElem()); // put on tail
+ Assert(theNewBuf != NULL);
+ return theNewBuf;
+ }
+
+ OSQueueElem *theElem = fQueue.DeQueue(); // get head
+
+ Assert(theElem != NULL);
+
+ if (theElem == NULL)
+ return NULL;
+
+ theNewBuf = (FileBlockBuffer*) theElem->GetEnclosingObject();
+ Assert(theNewBuf != NULL);
+ //qtss_printf("FileBlockPool::GetBufferElement reuse buffer theNewBuf=%"_U32BITARG_" fDataBuffer=%"_U32BITARG_" fArrayIndex=%"_S32BITARG_"\n",theNewBuf,theNewBuf->fDataBuffer,theNewBuf->fArrayIndex);
+
+ return theNewBuf;
+
+}
+
+void FileBlockPool::DeleteBlockPool(void)
+{
+
+ FileBlockBuffer *buffer = NULL;
+ OSQueueElem* theElem = fQueue.DeQueue();
+ while (theElem != NULL)
+ { buffer = (FileBlockBuffer *) theElem->GetEnclosingObject();
+ delete buffer;
+ theElem = fQueue.DeQueue();
+ }
+
+ fMaxBuffers = 1;
+ fNumCurrentBuffers = 0;
+ fBufferUnitSizeBytes = kBufferUnitSize;
+}
+
+FileBlockPool::~FileBlockPool(void)
+{
+
+ this->DeleteBlockPool();
+}
+
+
+void FileMap::AllocateBufferMap(UInt32 inUnitSizeInK, UInt32 inNumBuffSizeUnits, UInt32 inBufferIncCount, UInt32 inMaxBitRateBuffSizeInBlocks, UInt64 fileLen, UInt32 inBitRate)
+{
+
+ if (fFileMapArray != NULL && fNumBuffSizeUnits == inNumBuffSizeUnits && inBufferIncCount == fBlockPool.GetMaxBuffers())
+ return;
+
+ if( inUnitSizeInK < 1 )
+ inUnitSizeInK = 1;
+
+ fBlockPool.SetBufferUnitSize(inUnitSizeInK);
+
+ if (inBitRate == 0) // just use the maximum possible size
+ inBitRate = inMaxBitRateBuffSizeInBlocks * fBlockPool.GetBufferUnitSizeBytes();
+
+ if (inNumBuffSizeUnits == 0) // calculate the buffer size ourselves
+ {
+ inNumBuffSizeUnits = inBitRate / fBlockPool.GetBufferUnitSizeBytes();
+
+ if( inNumBuffSizeUnits > inMaxBitRateBuffSizeInBlocks) // max is 8 * buffUnit Size (32k) = 256K
+ { inNumBuffSizeUnits = inMaxBitRateBuffSizeInBlocks;
+ }
+ } //else the inNumBuffSizeUnits is explicitly defined so just use that value
+
+ if( inNumBuffSizeUnits < 1 )
+ inNumBuffSizeUnits = 1;
+
+ this->DeleteMap();
+ fBlockPool.DeleteBlockPool();
+
+ fNumBuffSizeUnits = inNumBuffSizeUnits;
+ fDataBufferSize = fBlockPool.GetBufferUnitSizeBytes() * inNumBuffSizeUnits;
+
+ fBlockPool.SetMaxBuffers(inBufferIncCount);
+ fBlockPool.SetBuffIncValue(inBufferIncCount);
+
+ fMapArraySize = (fileLen / fDataBufferSize) + 1;
+ fFileMapArray = NEW FileBlockBuffer *[ (SInt32) (fMapArraySize + 1) ];
+
+ this->Clean(); // required because fFileMapArray's array is used to store buffer pointers.
+#if FILE_SOURCE_DEBUG
+ qtss_printf("FileMap::AllocateBufferMap shared buffers fFileMapArray=%"_U32BITARG_" fDataBufferSize= %"_U32BITARG_" fMapArraySize=%"_U32BITARG_" fileLen=%qu \n",fFileMapArray, fDataBufferSize, fMapArraySize,fileLen);
+#endif
+
+}
+
+void FileMap::DeleteOldBuffs()
+{
+ while (fBlockPool.GetNumCurrentBuffers() > fBlockPool.GetMaxBuffers()) // delete any old buffers
+ {
+ FileBlockBuffer *theElem = fBlockPool.GetBufferElement(fDataBufferSize);
+ fFileMapArray[theElem->fArrayIndex] = NULL;
+ delete theElem;
+ fBlockPool.DecCurBuffers();
+ }
+}
+
+char *FileMap::GetBuffer(SInt64 buffIndex, Bool16 *outFillBuff)
+{
+ Assert(outFillBuff != NULL);
+ *outFillBuff = true; // we are re-using or just created a buff
+
+ this->DeleteOldBuffs();
+ Assert(buffIndex < (SInt32) fMapArraySize);
+
+ FileBlockBuffer *theElem = fFileMapArray[buffIndex];
+ if ( NULL == theElem)
+ {
+ #if FILE_SOURCE_DEBUG
+ qtss_printf("FileMap::GetBuffer call fBlockPool.GetBufferElement(); buffIndex=%"_S32BITARG_"\n",buffIndex);
+ #endif
+
+ theElem = fBlockPool.GetBufferElement(fDataBufferSize);
+ Assert(theElem);
+ }
+
+ fBlockPool.MarkUsed(theElem); // must happen here after getting a pre-allocated or used buffer.
+
+ if (theElem->fArrayIndex == buffIndex) // found a pre-allocated and filled buffer
+ {
+ #if FILE_SOURCE_DEBUG
+ //qtss_printf("FileMap::GetBuffer pre-allocated buff buffIndex=%"_S32BITARG_"\n",buffIndex);
+ #endif
+
+ *outFillBuff = false;
+ return theElem->fDataBuffer;
+ }
+
+ if (theElem->fArrayIndex >= 0)
+ {
+ fFileMapArray[theElem->fArrayIndex] = NULL; // reset the old map location
+ }
+ fFileMapArray[buffIndex] = theElem; // a new buffer
+ theElem->fArrayIndex = buffIndex; // record the index
+
+#if FILE_SOURCE_DEBUG
+ theElem->CleanBuffer();
+#endif
+
+ return theElem->fDataBuffer;
+
+}
+
+
+
+void FileMap::Clean(void)
+{
+ if (fFileMapArray != NULL)
+ ::memset( (char *)fFileMapArray,0, (SInt32) (sizeof(FileBlockBuffer *) * fMapArraySize) );
+}
+
+void FileMap::DeleteMap(void)
+{
+ if (NULL == fFileMapArray)
+ return;
+
+#if FILE_SOURCE_DEBUG
+ qtss_printf("FileMap::DeleteMap fFileMapArray=%"_U32BITARG_" fMapArraySize=%"_S32BITARG_" \n",fFileMapArray, fMapArraySize);
+ this->Clean();
+#endif
+
+ delete fFileMapArray;
+ fFileMapArray = NULL;
+
+}
+
+
+void OSFileSource::Set(const char *inPath)
+{
+ Close();
+
+#if __Win32__
+ fFile = open(inPath, O_RDONLY | O_BINARY);
+#elif __linux__
+ fFile = open(inPath, O_RDONLY | O_LARGEFILE);
+#else
+ fFile = open(inPath, O_RDONLY);
+#endif
+
+ if (fFile != -1)
+ {
+ struct stat buf;
+ ::memset(&buf,sizeof(buf),0);
+ if (::fstat(fFile, &buf) >= 0)
+ {
+ fLength = buf.st_size;
+ fModDate = buf.st_mtime;
+ if (fModDate < 0)
+ fModDate = 0;
+#ifdef __Win32__
+ fIsDir = buf.st_mode & _S_IFDIR;
+#else
+ fIsDir = S_ISDIR(buf.st_mode);
+#endif
+ this->SetLog(inPath);
+ }
+ else
+ this->Close();
+ }
+}
+
+
+
+void OSFileSource::Advise(UInt64 , UInt32 )
+{
+// does nothing on platforms other than MacOSXServer
+}
+
+
+OS_Error OSFileSource::FillBuffer(char* ioBuffer, char *buffStart, SInt32 buffIndex)
+{
+ UInt32 buffSize = fFileMap.GetMaxBufSize();
+ UInt64 startPos = (UInt64) buffIndex * (UInt64) buffSize;
+ UInt32 readLen = 0;
+
+ OS_Error theErr = this->ReadFromPos(startPos, buffStart, buffSize, &readLen);
+
+ fFileMap.SetIndexBuffFillSize(buffIndex, readLen);
+ fFileMap.TestBuffer(buffIndex);
+
+ return theErr;
+}
+
+#if FILE_SOURCE_BUFFTEST
+static SInt32 sBuffCount = 1;
+#endif
+
+OS_Error OSFileSource::Read(UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32* outRcvLen)
+{
+
+ if ( ( !fFileMap.Initialized() )
+ || ( !fCacheEnabled )
+ || ( fFileMap.GetBuffIndex(inPosition+inLength) > fFileMap.GetMaxBuffIndex() )
+ )
+ return this->ReadFromPos(inPosition, inBuffer, inLength, outRcvLen);
+
+ return this->ReadFromCache(inPosition, inBuffer, inLength, outRcvLen);
+}
+
+
+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;
+}
+
+OS_Error OSFileSource::ReadFromDisk(void* inBuffer, UInt32 inLength, UInt32* outRcvLen)
+{
+ #if FILE_SOURCE_BUFFTEST
+ qtss_printf("OSFileSource::Read inLength=%"_U32BITARG_" fFile=%d\n",inLength,fFile);
+ #endif
+
+#if __Win32__
+ if (_lseeki64(fFile, fPosition, SEEK_SET) == -1)
+ return OSThread::GetErrno();
+#else
+ if (lseek(fFile, fPosition, SEEK_SET) == -1)
+ return OSThread::GetErrno();
+#endif
+
+
+ int rcvLen = ::read(fFile, (char*)inBuffer, inLength);
+ if (rcvLen == -1)
+ return OSThread::GetErrno();
+
+ if (outRcvLen != NULL)
+ *outRcvLen = rcvLen;
+
+ fPosition += rcvLen;
+ fReadPos = fPosition;
+
+ return OS_NoErr;
+}
+
+OS_Error OSFileSource::ReadFromPos(UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32* outRcvLen)
+{
+#if TEST_TIME
+ {
+ startTime = OS::Milliseconds();
+ sReadCount++;
+ if (outRcvLen)
+ *outRcvLen = 0;
+ qtss_printf("OSFileSource::Read sReadCount = %"_S32BITARG_" totalbytes=%"_S32BITARG_" readsize=%"_U32BITARG_"\n",sReadCount,sByteCount,inLength);
+ }
+#endif
+
+ this->Seek(inPosition);
+ OS_Error err = this->ReadFromDisk(inBuffer,inLength,outRcvLen);
+
+#if READ_LOG
+ if (fFileLog)
+ { fFileLog = ::fopen(fFilePath,"a");
+ if (fFileLog)
+ { qtss_fprintf(fFileLog, "read: %qu %"_U32BITARG_" %"_U32BITARG_"\n",inPosition, *outRcvLen, xTrackID);
+ ::fclose(fFileLog);
+ }
+ }
+
+#endif
+#if TEST_TIME
+ {
+ durationTime += OS::Milliseconds() - startTime;
+ sByteCount += *outRcvLen;
+ }
+#endif
+
+ return err;
+}
+
+void OSFileSource::SetTrackID(UInt32 trackID)
+{
+#if READ_LOG
+ fTrackID = trackID;
+// qtss_printf("OSFileSource::SetTrackID = %"_U32BITARG_" this=%"_U32BITARG_"\n",fTrackID,(UInt32) this);
+#endif
+}
+
+
+void OSFileSource::Close()
+{
+ if ((fFile != -1) && (fShouldClose))
+ { ::close(fFile);
+
+ #if READ_LOG
+ if ( 0 && fFileLog != NULL )
+ { ::fclose(fFileLog);
+ fFileLog = NULL;
+ fFilePath[0] =0;
+ }
+ #endif
+ }
+
+ fFile = -1;
+ fModDate = 0;
+ fLength = 0;
+ fPosition = 0;
+ fReadPos = 0;
+
+#if TEST_TIME
+ if (fShouldClose)
+ { sMovie = 0;
+// qtss_printf("OSFileSource::Close sReadCount = %"_S32BITARG_" totalbytes=%"_S32BITARG_"\n",sReadCount,sByteCount);
+// qtss_printf("OSFileSource::Close durationTime = %qd\n",durationTime);
+ }
+#endif
+
+}
diff --git a/CommonUtilitiesLib/OSFileSource.h b/CommonUtilitiesLib/OSFileSource.h
new file mode 100644
index 0000000..d29d59b
--- /dev/null
+++ b/CommonUtilitiesLib/OSFileSource.h
@@ -0,0 +1,234 @@
+/*
+ *
+ * @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: osfilesource.h
+
+ Contains: simple file abstraction. This file abstraction is ONLY to be
+ used for files intended for serving
+
+
+*/
+
+#ifndef __OSFILE_H_
+#define __OSFILE_H_
+
+#include
+#include
+
+#include "OSHeaders.h"
+#include "StrPtrLen.h"
+#include "OSQueue.h"
+
+#define READ_LOG 0
+
+class FileBlockBuffer
+{
+
+ public:
+ FileBlockBuffer(): fArrayIndex(-1),fBufferSize(0),fBufferFillSize(0),fDataBuffer(NULL),fDummy(0){}
+ ~FileBlockBuffer(void);
+ void AllocateBuffer(UInt32 buffSize);
+ void TestBuffer(void);
+ void CleanBuffer() { ::memset(fDataBuffer,0, fBufferSize); }
+ void SetFillSize(UInt32 fillSize) {fBufferFillSize = fillSize;}
+ UInt32 GetFillSize(void) { return fBufferFillSize;}
+ OSQueueElem *GetQElem() { return &fQElem; }
+ SInt64 fArrayIndex;
+ UInt32 fBufferSize;
+ UInt32 fBufferFillSize;
+ char *fDataBuffer;
+ OSQueueElem fQElem;
+ UInt32 fDummy;
+};
+
+
+
+class FileBlockPool
+{
+ enum {
+ kDataBufferUnitSizeExp = 15,// base 2 exponent
+ kBufferUnitSize = (1 << kDataBufferUnitSizeExp ) // 32Kbytes
+ };
+
+ public:
+ FileBlockPool(void) : fMaxBuffers(1), fNumCurrentBuffers(0), fBufferUnitSizeBytes(kBufferUnitSize){}
+ ~FileBlockPool(void);
+
+ void SetMaxBuffers(UInt32 maxBuffers) { if (maxBuffers > 0) fMaxBuffers = maxBuffers; }
+
+ void SetBuffIncValue(UInt32 bufferInc) { if (bufferInc > 0) fBufferInc = bufferInc;}
+ void IncMaxBuffers(void) { fMaxBuffers += fBufferInc; }
+ void DecMaxBuffers(void) { if (fMaxBuffers > fBufferInc) fMaxBuffers-= fBufferInc; }
+ void DecCurBuffers(void) { if (fNumCurrentBuffers > 0) fNumCurrentBuffers--; }
+
+ void SetBufferUnitSize (UInt32 inUnitSizeInK) { fBufferUnitSizeBytes = inUnitSizeInK * 1024; }
+ UInt32 GetBufferUnitSizeBytes() { return fBufferUnitSizeBytes; }
+ UInt32 GetMaxBuffers(void) { return fMaxBuffers; }
+ UInt32 GetIncBuffers() { return fBufferInc; }
+ UInt32 GetNumCurrentBuffers(void) { return fNumCurrentBuffers; }
+ void DeleteBlockPool();
+ FileBlockBuffer* GetBufferElement(UInt32 bufferSizeBytes);
+ void MarkUsed(FileBlockBuffer* inBuffPtr);
+
+ private:
+ OSQueue fQueue;
+ UInt32 fMaxBuffers;
+ UInt32 fNumCurrentBuffers;
+ UInt32 fBufferInc;
+ UInt32 fBufferUnitSizeBytes;
+ UInt32 fBufferDataSizeBytes;
+
+};
+
+class FileMap
+{
+
+ public:
+ FileMap(void):fFileMapArray(NULL),fDataBufferSize(0),fMapArraySize(0),fNumBuffSizeUnits(0) {}
+ ~FileMap(void) {fFileMapArray = NULL;}
+ void AllocateBufferMap(UInt32 inUnitSizeInK, UInt32 inNumBuffSizeUnits, UInt32 inBufferIncCount, UInt32 inMaxBitRateBuffSizeInBlocks, UInt64 fileLen, UInt32 inBitRate);
+ char* GetBuffer(SInt64 bufIndex, Bool16 *outIsEmptyBuff);
+ void TestBuffer(SInt32 bufIndex) {Assert (bufIndex >= 0); fFileMapArray[bufIndex]->TestBuffer();};
+ void SetIndexBuffFillSize(SInt32 bufIndex, UInt32 fillSize) { Assert (bufIndex >= 0); fFileMapArray[bufIndex]->SetFillSize(fillSize);}
+ UInt32 GetMaxBufSize(void) {return fDataBufferSize;}
+ UInt32 GetBuffSize(SInt64 bufIndex) { Assert (bufIndex >= 0); return fFileMapArray[bufIndex]->GetFillSize(); }
+ UInt32 GetIncBuffers(void) { return fBlockPool.GetIncBuffers(); }
+ void IncMaxBuffers() {fBlockPool.IncMaxBuffers(); }
+ void DecMaxBuffers() {fBlockPool.DecMaxBuffers(); }
+ Bool16 Initialized() { return fFileMapArray == NULL ? false : true; }
+ void Clean(void);
+ void DeleteMap(void);
+ void DeleteOldBuffs(void);
+ SInt64 GetBuffIndex(UInt64 inPosition) { return inPosition / this->GetMaxBufSize(); }
+ SInt64 GetMaxBuffIndex() { Assert(fMapArraySize > 0); return fMapArraySize -1; }
+ UInt64 GetBuffOffset(SInt64 bufIndex) { return (UInt64) (bufIndex * this->GetMaxBufSize() ); }
+ FileBlockPool fBlockPool;
+
+ FileBlockBuffer** fFileMapArray;
+
+ private:
+
+ UInt32 fDataBufferSize;
+ SInt64 fMapArraySize;
+ UInt32 fNumBuffSizeUnits;
+
+};
+
+class OSFileSource
+{
+ public:
+
+ OSFileSource() : fFile(-1), fLength(0), fPosition(0), fReadPos(0), fShouldClose(true), fIsDir(false), fCacheEnabled(false)
+ {
+
+ #if READ_LOG
+ fFileLog = NULL;
+ fTrackID = 0;
+ fFilePath[0]=0;
+ #endif
+
+ }
+
+ OSFileSource(const char *inPath) : fFile(-1), fLength(0), fPosition(0), fReadPos(0), fShouldClose(true), fIsDir(false),fCacheEnabled(false)
+ {
+ Set(inPath);
+
+ #if READ_LOG
+ fFileLog = NULL;
+ fTrackID = 0;
+ fFilePath[0]=0;
+ #endif
+
+ }
+
+ ~OSFileSource() { Close(); fFileMap.DeleteMap();}
+
+ //Sets this object to reference this file
+ void Set(const char *inPath);
+
+ // Call this if you don't want Close or the destructor to close the fd
+ void DontCloseFD() { fShouldClose = false; }
+
+ //Advise: this advises the OS that we are going to be reading soon from the
+ //following position in the file
+ void Advise(UInt64 advisePos, UInt32 adviseAmt);
+
+ OS_Error Read(void* inBuffer, UInt32 inLength, UInt32* outRcvLen = NULL)
+ { return ReadFromDisk(inBuffer, inLength, outRcvLen);
+ }
+
+ OS_Error Read(UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32* outRcvLen = NULL);
+ OS_Error ReadFromDisk(void* inBuffer, UInt32 inLength, UInt32* outRcvLen = NULL);
+ OS_Error ReadFromCache(UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32* outRcvLen = NULL);
+ OS_Error ReadFromPos(UInt64 inPosition, void* inBuffer, UInt32 inLength, UInt32* outRcvLen = NULL);
+ void EnableFileCache(Bool16 enabled) {OSMutexLocker locker(&fMutex); fCacheEnabled = enabled; }
+ Bool16 GetCacheEnabled() { return fCacheEnabled; }
+ void AllocateFileCache(UInt32 inUnitSizeInK = 32, UInt32 bufferSizeUnits = 0, UInt32 incBuffers = 1, UInt32 inMaxBitRateBuffSizeInBlocks = 8, UInt32 inBitRate = 32768)
+ { fFileMap.AllocateBufferMap(inUnitSizeInK, bufferSizeUnits,incBuffers, inMaxBitRateBuffSizeInBlocks, fLength, inBitRate);
+ }
+ void IncMaxBuffers() {OSMutexLocker locker(&fMutex); fFileMap.IncMaxBuffers(); }
+ void DecMaxBuffers() {OSMutexLocker locker(&fMutex); fFileMap.DecMaxBuffers(); }
+
+ OS_Error FillBuffer(char* ioBuffer, char *buffStart, SInt32 bufIndex);
+
+ void Close();
+ time_t GetModDate() { return fModDate; }
+ UInt64 GetLength() { return fLength; }
+ UInt64 GetCurOffset() { return fPosition; }
+ void Seek(SInt64 newPosition) { fPosition = newPosition; }
+ Bool16 IsValid() { return fFile != -1; }
+ Bool16 IsDir() { return fIsDir; }
+
+ // For async I/O purposes
+ int GetFD() { return fFile; }
+ void SetTrackID(UInt32 trackID);
+ // So that close won't do anything
+ void ResetFD() { fFile=-1; }
+
+ void SetLog(const char *inPath);
+
+ private:
+
+ int fFile;
+ UInt64 fLength;
+ UInt64 fPosition;
+ UInt64 fReadPos;
+ Bool16 fShouldClose;
+ Bool16 fIsDir;
+ time_t fModDate;
+
+
+ OSMutex fMutex;
+ FileMap fFileMap;
+ Bool16 fCacheEnabled;
+#if READ_LOG
+ FILE* fFileLog;
+ char fFilePath[1024];
+ UInt32 fTrackID;
+#endif
+
+};
+
+#endif //__OSFILE_H_
diff --git a/CommonUtilitiesLib/OSHashTable.h b/CommonUtilitiesLib/OSHashTable.h
new file mode 100644
index 0000000..57e8341
--- /dev/null
+++ b/CommonUtilitiesLib/OSHashTable.h
@@ -0,0 +1,168 @@
+/*
+ *
+ * @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: OSHashTable.h
+
+ Contains: Defines a template class for hash tables.
+
+
+
+
+*/
+
+#ifndef _OSHASHTABLE_H_
+#define _OSHASHTABLE_H_
+
+#include "MyAssert.h"
+#include "OSHeaders.h"
+
+/*
+T must have a fNextHashEntry field, and key(T) must returns the key of type K.
+K must have a method GetHashKey() that returns an UInt32 bit hash value.
+Will the hash table can contain duplicate keys, the Map function will return only the first one.
+*/
+
+template
+class OSHashTable {
+public:
+ OSHashTable( UInt32 size )
+ {
+ fHashTable = new ( T*[size] );
+ Assert( fHashTable );
+ memset( fHashTable, 0, sizeof(T*) * size );
+ fSize = size;
+ // Determine whether the hash size is a power of 2
+ // if not set the mask to zero, otherwise we can
+ // use the mask which is faster for ComputeIndex
+ fMask = fSize - 1;
+ if ((fMask & fSize) != 0)
+ fMask = 0;
+ fNumEntries = 0;
+ }
+ ~OSHashTable()
+ {
+ delete [] fHashTable;
+ }
+ void Add( T* entry ) {
+ Assert( entry->fNextHashEntry == NULL );
+ K key( entry );
+ UInt32 theIndex = ComputeIndex( key.GetHashKey() );
+ entry->fNextHashEntry = fHashTable[ theIndex ];
+ fHashTable[ theIndex ] = entry;
+ fNumEntries++;
+ }
+ void Remove( T* entry )
+ {
+ K key( entry );
+ UInt32 theIndex = ComputeIndex( key.GetHashKey() );
+ T* elem = fHashTable[ theIndex ];
+ T* last = NULL;
+ while (elem && elem != entry) {
+ last = elem;
+ elem = elem->fNextHashEntry;
+ }
+
+ if ( elem ) // sometimes remove is called 2x ( swap, then un register )
+ {
+ Assert(elem);
+ if (last)
+ last->fNextHashEntry = elem->fNextHashEntry;
+ else
+ fHashTable[ theIndex ] = elem->fNextHashEntry;
+ elem->fNextHashEntry = NULL;
+ fNumEntries--;
+ }
+ }
+ T* Map( K* key )
+ {
+ UInt32 theIndex = ComputeIndex( key->GetHashKey() );
+ T* elem = fHashTable[ theIndex ];
+ while (elem) {
+ K elemKey( elem );
+ if (elemKey == *key)
+ break;
+ elem = elem->fNextHashEntry;
+ }
+ return elem;
+ }
+ UInt64 GetNumEntries() { return fNumEntries; }
+
+ UInt32 GetTableSize() { return fSize; }
+ T* GetTableEntry( int i ) { return fHashTable[i]; }
+
+private:
+ T** fHashTable;
+ UInt32 fSize;
+ UInt32 fMask;
+ UInt64 fNumEntries;
+
+ UInt32 ComputeIndex( UInt32 hashKey )
+ {
+ if (fMask)
+ return( hashKey & fMask );
+ else
+ return( hashKey % fSize );
+ }
+};
+
+template
+class OSHashTableIter {
+public:
+ OSHashTableIter( OSHashTable* table )
+ {
+ fHashTable = table;
+ First();
+ }
+ void First()
+ {
+ for (fIndex = 0; fIndex < fHashTable->GetTableSize(); fIndex++) {
+ fCurrent = fHashTable->GetTableEntry( fIndex );
+ if (fCurrent)
+ break;
+ }
+ }
+ void Next()
+ {
+ fCurrent = fCurrent->fNextHashEntry;
+ if (!fCurrent) {
+ for (fIndex = fIndex + 1; fIndex < fHashTable->GetTableSize(); fIndex++) {
+ fCurrent = fHashTable->GetTableEntry( fIndex );
+ if (fCurrent)
+ break;
+ }
+ }
+ }
+ Bool16 IsDone()
+ {
+ return( fCurrent == NULL );
+ }
+ T* GetCurrent() { return fCurrent; }
+
+private:
+ OSHashTable* fHashTable;
+ T* fCurrent;
+ UInt32 fIndex;
+};
+#endif //_OSHASHTABLE_H_
diff --git a/CommonUtilitiesLib/OSHeaders.c b/CommonUtilitiesLib/OSHeaders.c
new file mode 100644
index 0000000..3a39a2e
--- /dev/null
+++ b/CommonUtilitiesLib/OSHeaders.c
@@ -0,0 +1,35 @@
+#include "OSHeaders.h"
+
+#if __linux__
+ // OpenBSD strlcpy implementation to allow usage of it while on Linux.
+ /*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+ size_t
+ strlcpy(char *dst, const char *src, size_t siz)
+ {
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+ }
+#endif
\ No newline at end of file
diff --git a/CommonUtilitiesLib/OSHeaders.h b/CommonUtilitiesLib/OSHeaders.h
new file mode 100644
index 0000000..24b2b21
--- /dev/null
+++ b/CommonUtilitiesLib/OSHeaders.h
@@ -0,0 +1,562 @@
+/*
+ *
+ * @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@
+ *
+ */
+
+#ifndef OSHeaders_H
+#define OSHeaders_H
+#include
+
+
+
+#ifndef TRUE
+ #define TRUE 1
+#endif
+
+#ifndef FALSE
+ #define FALSE 0
+#endif
+
+
+
+/* Platform-specific components */
+#if __MacOSX__
+
+ /* Defines */
+ #define _64BITARG_ "ll"
+ #define _S64BITARG_ "lld"
+ #define _U64BITARG_ "llu"
+
+#if __LP64__
+ #define _S32BITARG_ "d"
+ #define _U32BITARG_ "u"
+ #define _SPOINTERSIZEARG_ _S64BITARG_
+ #define _UPOINTERSIZEARG_ _U64BITARG_
+#else
+ #define _S32BITARG_ "ld"
+ #define _U32BITARG_ "lu"
+ #define _SPOINTERSIZEARG_ _S32BITARG_
+ #define _UPOINTERSIZEARG_ _U32BITARG_
+#endif
+
+ /* paths */
+ #define kEOLString "\n"
+ #define kPathDelimiterString "/"
+ #define kPathDelimiterChar '/'
+ #define kPartialPathBeginsWithDelimiter 0
+
+ /* Includes */
+ #include
+
+ /* Constants */
+ #define QT_TIME_TO_LOCAL_TIME (-2082844800)
+ #define QT_PATH_SEPARATOR '/'
+
+
+#include "/System/Library/Frameworks/CoreServices.framework/Headers/../Frameworks/CarbonCore.framework/Headers/MacTypes.h"
+
+#define kSInt16_Max (SInt16) SHRT_MAX
+#define kUInt16_Max (UInt16) USHRT_MAX
+
+#define kSInt32_Max (SInt32) LONG_MAX
+#define kUInt32_Max (UInt32) ULONG_MAX
+
+#define kSInt64_Max (SInt64) LONG_LONG_MAX
+#define kUInt64_Max (UInt64) ULONG_LONG_MAX
+
+#if 0 // old defs we are now using MacTypes.h
+ /* Typedefs */
+ typedef unsigned char UInt8;
+ typedef signed char SInt8;
+ typedef unsigned short UInt16;
+ typedef signed short SInt16;
+ typedef unsigned int UInt32;
+ typedef signed int SInt32;
+ typedef signed long long SInt64;
+ typedef unsigned long long UInt64;
+ typedef float Float32;
+ typedef double Float64;
+ typedef UInt32 FourCharCode;
+ typedef FourCharCode OSType;
+#endif
+
+ typedef signed long PointerSizedInt;
+ typedef unsigned long PointerSizedUInt;
+ typedef UInt16 Bool16;
+ typedef UInt8 Bool8;
+
+
+ #ifdef FOUR_CHARS_TO_INT
+ #error Conflicting Macro "FOUR_CHARS_TO_INT"
+ #endif
+
+ #define FOUR_CHARS_TO_INT( c1, c2, c3, c4 ) ( c1 << 24 | c2 << 16 | c3 << 8 | c4 )
+
+ #ifdef TW0_CHARS_TO_INT
+ #error Conflicting Macro "TW0_CHARS_TO_INT"
+ #endif
+
+ #define TW0_CHARS_TO_INT( c1, c2 ) ( c1 << 8 | c2 )
+
+
+#elif __linux__ || __linuxppc__ || __FreeBSD__
+
+ /* Defines */
+ #define _64BITARG_ "q"
+ #define _S64BITARG_ "lld"
+ #define _U64BITARG_ "llu"
+
+#if __LP64__
+ #define _S32BITARG_ "d"
+ #define _U32BITARG_ "u"
+#else
+ #define _S32BITARG_ "ld"
+ #define _U32BITARG_ "lu"
+#endif
+ #define _SPOINTERSIZEARG_ _S64BITARG_
+ #define _UPOINTERSIZEARG_ _U64BITARG_
+
+ /* paths */
+ #define kEOLString "\n"
+ #define kPathDelimiterString "/"
+ #define kPathDelimiterChar '/'
+ #define kPartialPathBeginsWithDelimiter 0
+
+ /* Includes */
+ #include
+
+ /* Constants */
+ #define QT_TIME_TO_LOCAL_TIME (-2082844800)
+ #define QT_PATH_SEPARATOR '/'
+
+ /* Typedefs */
+ typedef signed long PointerSizedInt;
+ typedef unsigned long PointerSizedUInt;
+ typedef unsigned char UInt8;
+ typedef signed char SInt8;
+ typedef unsigned short UInt16;
+ typedef signed short SInt16;
+ typedef unsigned int UInt32;
+ typedef signed int SInt32;
+ typedef signed long SInt64;
+ typedef unsigned long UInt64;
+ typedef float Float32;
+ typedef double Float64;
+ typedef UInt16 Bool16;
+ typedef UInt8 Bool8;
+
+ /* Define max values of our custom typedefs */
+ #define kSInt16_Max (SInt16) SHRT_MAX
+ #define kUInt16_Max (UInt16) USHRT_MAX
+
+ #define kSInt32_Max (SInt32) INT_MAX
+ #define kUInt32_Max (UInt32) UINT_MAX
+
+ #define kSInt64_Max (SInt64) LONG_MAX
+ #define kUInt64_Max (UInt64) ULONG_MAX
+
+
+ typedef unsigned int FourCharCode;
+ typedef FourCharCode OSType;
+
+ #ifdef FOUR_CHARS_TO_INT
+ #error Conflicting Macro "FOUR_CHARS_TO_INT"
+ #endif
+
+ #define FOUR_CHARS_TO_INT( c1, c2, c3, c4 ) ( c1 << 24 | c2 << 16 | c3 << 8 | c4 )
+
+ #ifdef TW0_CHARS_TO_INT
+ #error Conflicting Macro "TW0_CHARS_TO_INT"
+ #endif
+
+ #define TW0_CHARS_TO_INT( c1, c2 ) ( c1 << 8 | c2 )
+
+#if __linux__
+ size_t strlcpy(char *dst, const char *src, size_t siz);
+#endif
+
+#elif __Win32__
+
+ /* Defines */
+ #define _64BITARG_ "I64"
+ #define _S64BITARG_ "I64d"
+ #define _U64BITARG_ "I64u"
+#if __LP64__
+ #define _S32BITARG_ "d"
+ #define _U32BITARG_ "u"
+#else
+ #define _S32BITARG_ "ld"
+ #define _U32BITARG_ "lu"
+#endif
+
+ /* paths */
+ #define kEOLString "\r\n"
+ #define kPathDelimiterString "\\"
+ #define kPathDelimiterChar '\\'
+ #define kPartialPathBeginsWithDelimiter 0
+
+ #define crypt(buf, salt) ((char*)buf)
+
+ /* Includes */
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+
+
+ #define R_OK 0
+ #define W_OK 1
+
+ // POSIX errorcodes
+ #define ENOTCONN 1002
+ #define EADDRINUSE 1004
+ #define EINPROGRESS 1007
+ #define ENOBUFS 1008
+ #define EADDRNOTAVAIL 1009
+
+ // Winsock does not use iovecs
+ struct iovec {
+ u_long iov_len; // this is not the POSIX definition, it is rather defined to be
+ char FAR* iov_base; // equivalent to a WSABUF for easy integration into Win32
+ };
+
+ /* Constants */
+ #define QT_TIME_TO_LOCAL_TIME (-2082844800)
+ #define QT_PATH_SEPARATOR '/'
+
+ /* Typedefs */
+ typedef signed long PointerSizedInt;
+ typedef unsigned long PointerSizedUInt;
+ typedef unsigned char UInt8;
+ typedef signed char SInt8;
+ typedef unsigned short UInt16;
+ typedef signed short SInt16;
+ typedef unsigned long UInt32;
+ typedef signed long SInt32;
+ typedef LONGLONG SInt64;
+ typedef ULONGLONG UInt64;
+ typedef float Float32;
+ typedef double Float64;
+ typedef UInt16 Bool16;
+ typedef UInt8 Bool8;
+
+ typedef unsigned long FourCharCode;
+ typedef FourCharCode OSType;
+
+ #ifdef FOUR_CHARS_TO_INT
+ #error Conflicting Macro "FOUR_CHARS_TO_INT"
+ #endif
+
+ #define FOUR_CHARS_TO_INT( c1, c2, c3, c4 ) ( c1 << 24 | c2 << 16 | c3 << 8 | c4 )
+
+ #ifdef TW0_CHARS_TO_INT
+ #error Conflicting Macro "TW0_CHARS_TO_INT"
+ #endif
+
+ #define TW0_CHARS_TO_INT( c1, c2 ) ( c1 << 8 | c2 )
+
+ #define kSInt16_Max USHRT_MAX
+ #define kUInt16_Max USHRT_MAX
+
+ #define kSInt32_Max LONG_MAX
+ #define kUInt32_Max ULONG_MAX
+
+ #undef kSInt64_Max
+ #define kSInt64_Max 9223372036854775807i64
+
+ #undef kUInt64_Max
+ #define kUInt64_Max (kSInt64_Max * 2ULL + 1)
+
+#elif __sgi__
+ /* Defines */
+ #define _64BITARG_ "ll"
+ #define _S64BITARG_ "lld"
+ #define _U64BITARG_ "llu"
+#if __LP64__
+ #define _S32BITARG_ "d"
+ #define _U32BITARG_ "u"
+#else
+ #define _S32BITARG_ "ld"
+ #define _U32BITARG_ "lu"
+#endif
+
+ /* paths */
+ #define kPathDelimiterString "/"
+ #define kPathDelimiterChar '/'
+ #define kPartialPathBeginsWithDelimiter 0
+ #define kEOLString "\n"
+
+ /* Includes */
+ #include
+ #include
+ #include
+
+ /* Constants */
+ #define QT_TIME_TO_LOCAL_TIME (-2082844800)
+ #define QT_PATH_SEPARATOR '/'
+
+ /* Typedefs */
+ typedef unsigned char boolean;
+ #define true 1
+ #define false 0
+
+ typedef signed long PointerSizedInt;
+ typedef unsigned long PointerSizedUInt;
+ typedef unsigned char UInt8;
+ typedef signed char SInt8;
+ typedef unsigned short UInt16;
+ typedef signed short SInt16;
+ typedef unsigned long UInt32;
+ typedef signed long SInt32;
+ typedef signed long long SInt64;
+ typedef unsigned long long UInt64;
+ typedef float Float32;
+ typedef double Float64;
+
+ typedef UInt16 Bool16;
+
+ typedef unsigned long FourCharCode;
+ typedef FourCharCode OSType;
+
+ /* Nulled-out new() for use without memory debugging */
+ /* #define NEW(t,c,v) new c v
+ #define NEW_ARRAY(t,c,n) new c[n] */
+
+ #define thread_t pthread_t
+ #define cthread_errno() errno
+
+ #ifdef FOUR_CHARS_TO_INT
+ #error Conflicting Macro "FOUR_CHARS_TO_INT"
+ #endif
+
+ #define FOUR_CHARS_TO_INT( c1, c2, c3, c4 ) ( c1 << 24 | c2 << 16 | c3 << 8 | c4 )
+
+ #ifdef TW0_CHARS_TO_INT
+ #error Conflicting Macro "TW0_CHARS_TO_INT"
+ #endif
+
+ #define TW0_CHARS_TO_INT( c1, c2 ) ( c1 << 8 | c2 )
+
+#elif defined(sun) // && defined(sparc)
+
+ /* Defines */
+ #define _64BITARG_ "ll"
+ #define _S64BITARG_ "Ild"
+ #define _U64BITARG_ "llu"
+#if __LP64__
+ #define _S32BITARG_ "d"
+ #define _U32BITARG_ "u"
+#else
+ #define _S32BITARG_ "ld"
+ #define _U32BITARG_ "lu"
+#endif
+
+ /* paths */
+ #define kPathDelimiterString "/"
+ #define kPathDelimiterChar '/'
+ #define kPartialPathBeginsWithDelimiter 0
+ #define kEOLString "\n"
+
+ /* Includes */
+ #include
+ #include
+
+ /* Constants */
+ #define QT_TIME_TO_LOCAL_TIME (-2082844800)
+ #define QT_PATH_SEPARATOR '/'
+
+ /* Typedefs */
+ //typedef unsigned char Bool16;
+ //#define true 1
+ //#define false 0
+
+ typedef signed long PointerSizedInt;
+ typedef unsigned long PointerSizedUInt;
+ typedef unsigned char UInt8;
+ typedef signed char SInt8;
+ typedef unsigned short UInt16;
+ typedef signed short SInt16;
+ typedef unsigned long UInt32;
+ typedef signed long SInt32;
+ typedef signed long long SInt64;
+ typedef unsigned long long UInt64;
+ typedef float Float32;
+ typedef double Float64;
+ typedef UInt16 Bool16;
+ typedef UInt8 Bool8;
+
+ typedef unsigned long FourCharCode;
+ typedef FourCharCode OSType;
+
+ #ifdef FOUR_CHARS_TO_INT
+ #error Conflicting Macro "FOUR_CHARS_TO_INT"
+ #endif
+
+ #define FOUR_CHARS_TO_INT( c1, c2, c3, c4 ) ( c1 << 24 | c2 << 16 | c3 << 8 | c4 )
+
+ #ifdef TW0_CHARS_TO_INT
+ #error Conflicting Macro "TW0_CHARS_TO_INT"
+ #endif
+
+ #define TW0_CHARS_TO_INT( c1, c2 ) ( c1 << 8 | c2 )
+
+#elif defined(__hpux__)
+
+ /* Defines */
+ #define _64BITARG_ "ll"
+ #define _S64BITARG_ "Ild"
+ #define _U64BITARG_ "llu"
+#if __LP64__
+ #define _S32BITARG_ "d"
+ #define _U32BITARG_ "u"
+#else
+ #define _S32BITARG_ "ld"
+ #define _U32BITARG_ "lu"
+#endif
+
+ /* paths */
+ #define kPathDelimiterString "/"
+ #define kPathDelimiterChar '/'
+ #define kPartialPathBeginsWithDelimiter 0
+ #define kEOLString "\n"
+
+ /* Includes */
+ #include
+ #include
+
+ /* Constants */
+ #define QT_TIME_TO_LOCAL_TIME (-2082844800)
+ #define QT_PATH_SEPARATOR '/'
+
+ /* Typedefs */
+ //typedef unsigned char Bool16;
+ //#define true 1
+ //#define false 0
+
+ typedef signed long PointerSizedInt;
+ typedef unsigned long PointerSizedUInt;
+ typedef unsigned char UInt8;
+ typedef signed char SInt8;
+ typedef unsigned short UInt16;
+ typedef signed short SInt16;
+ typedef unsigned long UInt32;
+ typedef signed long SInt32;
+ typedef signed long long SInt64;
+ typedef unsigned long long UInt64;
+ typedef float Float32;
+ typedef double Float64;
+ typedef UInt16 Bool16;
+ typedef UInt8 Bool8;
+
+ typedef unsigned long FourCharCode;
+ typedef FourCharCode OSType;
+
+ #ifdef FOUR_CHARS_TO_INT
+ #error Conflicting Macro "FOUR_CHARS_TO_INT"
+ #endif
+
+ #define FOUR_CHARS_TO_INT( c1, c2, c3, c4 ) ( c1 << 24 | c2 << 16 | c3 << 8 | c4 )
+
+ #ifdef TW0_CHARS_TO_INT
+ #error Conflicting Macro "TW0_CHARS_TO_INT"
+ #endif
+
+ #define TW0_CHARS_TO_INT( c1, c2 ) ( c1 << 8 | c2 )
+
+#elif defined(__osf__)
+
+ /* Defines */
+ #define _64BITARG_ "l"
+ #define _S64BITARG_ "ld"
+ #define _U64BITARG_ "lu"
+#if __LP64__
+ #define _S32BITARG_ "ld"
+ #define _U32BITARG_ "lu"
+#else
+ #define _S32BITARG_ "d"
+ #define _U32BITARG_ "u"
+#endif
+
+ /* paths */
+ #define kEOLString "\n"
+ #define kPathDelimiterString "/"
+ #define kPathDelimiterChar '/'
+ #define kPartialPathBeginsWithDelimiter 0
+
+ /* Includes */
+ #include
+ #include
+
+ /* Constants */
+ #define QT_TIME_TO_LOCAL_TIME (-2082844800)
+ #define QT_PATH_SEPARATOR '/'
+
+ /* Typedefs */
+ typedef unsigned long PointerSizedInt;
+ typedef unsigned char UInt8;
+ typedef signed char SInt8;
+ typedef unsigned short UInt16;
+ typedef signed short SInt16;
+ typedef unsigned int UInt32;
+ typedef signed int SInt32;
+ typedef signed long SInt64;
+ typedef unsigned long UInt64;
+ typedef float Float32;
+ typedef double Float64;
+ typedef UInt16 Bool16;
+ typedef UInt8 Bool8;
+
+ typedef unsigned int FourCharCode;
+ typedef FourCharCode OSType;
+
+ #ifdef FOUR_CHARS_TO_INT
+ #error Conflicting Macro "FOUR_CHARS_TO_INT"
+ #endif
+
+ #define FOUR_CHARS_TO_INT( c1, c2, c3, c4 ) ( c1 << 24 | c2 << 16 | c3 << 8 | c4 )
+
+ #ifdef TW0_CHARS_TO_INT
+ #error Conflicting Macro "TW0_CHARS_TO_INT"
+ #endif
+
+ #define TW0_CHARS_TO_INT( c1, c2 ) ( c1 << 8 | c2 )
+
+
+#endif
+
+typedef SInt32 OS_Error;
+
+enum
+{
+ OS_NoErr = (OS_Error) 0,
+ OS_BadURLFormat = (OS_Error) -100,
+ OS_NotEnoughSpace = (OS_Error) -101
+};
+
+#include "../PlatformHeader.h"
+
+#endif /* OSHeaders_H */
diff --git a/CommonUtilitiesLib/OSHeap.cpp b/CommonUtilitiesLib/OSHeap.cpp
new file mode 100644
index 0000000..7da51bf
--- /dev/null
+++ b/CommonUtilitiesLib/OSHeap.cpp
@@ -0,0 +1,421 @@
+/*
+ *
+ * @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: OSHeap.cpp
+
+ Contains: Implements a heap
+
+
+
+*/
+
+#include
+
+#include "OSHeap.h"
+#include "OSMemory.h"
+
+OSHeap::OSHeap(UInt32 inStartSize)
+: fFreeIndex(1)
+{
+ if (inStartSize < 2)
+ fArraySize = 2;
+ else
+ fArraySize = inStartSize;
+
+ fHeap = NEW OSHeapElem*[fArraySize];
+}
+
+void OSHeap::Insert(OSHeapElem* inElem)
+{
+ Assert(inElem != NULL);
+
+ if ((fHeap == NULL) || (fFreeIndex == fArraySize))
+ {
+ fArraySize *= 2;
+ OSHeapElem** tempArray = NEW OSHeapElem*[fArraySize];
+ if ((fHeap != NULL) && (fFreeIndex > 1))
+ memcpy(tempArray, fHeap, sizeof(OSHeapElem*) * fFreeIndex);
+
+ delete [] fHeap;
+ fHeap = tempArray;
+ }
+
+ Assert(fHeap != NULL);
+ Assert(inElem->fCurrentHeap == NULL);
+ Assert(fArraySize > fFreeIndex);
+
+#if _OSHEAP_TESTING_
+ SanityCheck(1);
+#endif
+
+ //insert the element into the last leaf of the tree
+ fHeap[fFreeIndex] = inElem;
+
+ //bubble the new element up to its proper place in the heap
+
+ //start at the last leaf of the tree
+ UInt32 swapPos = fFreeIndex;
+ while (swapPos > 1)
+ {
+ //move up the chain until we get to the root, bubbling this new element
+ //to its proper place in the tree
+ UInt32 nextSwapPos = swapPos >> 1;
+
+ //if this child is greater than it's parent, we need to do the old
+ //switcheroo
+ if (fHeap[swapPos]->fValue < fHeap[nextSwapPos]->fValue)
+ {
+ OSHeapElem* temp = fHeap[swapPos];
+ fHeap[swapPos] = fHeap[nextSwapPos];
+ fHeap[nextSwapPos] = temp;
+ swapPos = nextSwapPos;
+ }
+ else
+ //if not, we are done!
+ break;
+ }
+ inElem->fCurrentHeap = this;
+ fFreeIndex++;
+}
+
+
+OSHeapElem* OSHeap::Extract(UInt32 inIndex)
+{
+ if ((fHeap == NULL) || (fFreeIndex <= inIndex))
+ return NULL;
+
+#if _OSHEAP_TESTING_
+ SanityCheck(1);
+#endif
+
+ //store a reference to the element we want to extract
+ OSHeapElem* victim = fHeap[inIndex];
+ Assert(victim->fCurrentHeap == this);
+ victim->fCurrentHeap = NULL;
+
+ //but now we need to preserve this heuristic. We do this by taking
+ //the last leaf, putting it at the empty position, then heapifying that chain
+ fHeap[inIndex] = fHeap[fFreeIndex - 1];
+ fFreeIndex--;
+
+ //The following is an implementation of the Heapify algorithm (CLR 7.1 pp 143)
+ //The gist is that this new item at the top of the heap needs to be bubbled down
+ //until it is bigger than its two children, therefore maintaining the heap property.
+
+ UInt32 parent = inIndex;
+ while (parent < fFreeIndex)
+ {
+ //which is bigger? parent or left child?
+ UInt32 greatest = parent;
+ UInt32 leftChild = parent * 2;
+ if ((leftChild < fFreeIndex) && (fHeap[leftChild]->fValue < fHeap[parent]->fValue))
+ greatest = leftChild;
+
+ //which is bigger? the biggest so far or the right child?
+ UInt32 rightChild = (parent * 2) + 1;
+ if ((rightChild < fFreeIndex) && (fHeap[rightChild]->fValue < fHeap[greatest]->fValue))
+ greatest = rightChild;
+
+ //if the parent is in fact bigger than its two children, we have bubbled
+ //this element down far enough
+ if (greatest == parent)
+ break;
+
+ //parent is not bigger than at least one of its two children, so swap the parent
+ //with the largest item.
+ OSHeapElem* temp = fHeap[parent];
+ fHeap[parent] = fHeap[greatest];
+ fHeap[greatest] = temp;
+
+ //now heapify the remaining chain
+ parent = greatest;
+ }
+
+ return victim;
+}
+
+OSHeapElem* OSHeap::Remove(OSHeapElem* elem)
+{
+ if ((fHeap == NULL) || (fFreeIndex == 1))
+ return NULL;
+
+#if _OSHEAP_TESTING_
+ SanityCheck(1);
+#endif
+
+ //first attempt to locate this element in the heap
+ UInt32 theIndex = 1;
+ for ( ; theIndex < fFreeIndex; theIndex++)
+ if (elem == fHeap[theIndex])
+ break;
+
+ //either we've found it, or this is a bogus element
+ if (theIndex == fFreeIndex)
+ return NULL;
+
+ return Extract(theIndex);
+}
+
+
+#if _OSHEAP_TESTING_
+
+void OSHeap::SanityCheck(UInt32 root)
+{
+ //make sure root is greater than both its children. Do so recursively
+ if (root < fFreeIndex)
+ {
+ if ((root * 2) < fFreeIndex)
+ {
+ Assert(fHeap[root]->fValue <= fHeap[root * 2]->fValue);
+ SanityCheck(root * 2);
+ }
+ if (((root * 2) + 1) < fFreeIndex)
+ {
+ Assert(fHeap[root]->fValue <= fHeap[(root * 2) + 1]->fValue);
+ SanityCheck((root * 2) + 1);
+ }
+ }
+}
+
+
+Bool16 OSHeap::Test()
+{
+ OSHeap victim(2);
+ OSHeapElem elem1;
+ OSHeapElem elem2;
+ OSHeapElem elem3;
+ OSHeapElem elem4;
+ OSHeapElem elem5;
+ OSHeapElem elem6;
+ OSHeapElem elem7;
+ OSHeapElem elem8;
+ OSHeapElem elem9;
+
+ OSHeapElem* max = victim.ExtractMin();
+ if (max != NULL)
+ return false;
+
+ elem1.SetValue(100);
+ victim.Insert(&elem1);
+
+ max = victim.ExtractMin();
+ if (max != &elem1)
+ return false;
+ max = victim.ExtractMin();
+ if (max != NULL)
+ return false;
+
+ elem1.SetValue(100);
+ elem2.SetValue(80);
+
+ victim.Insert(&elem1);
+ victim.Insert(&elem2);
+
+ max = victim.ExtractMin();
+ if (max != &elem2)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem1)
+ return false;
+ max = victim.ExtractMin();
+ if (max != NULL)
+ return false;
+
+ victim.Insert(&elem2);
+ victim.Insert(&elem1);
+
+ max = victim.ExtractMin();
+ if (max != &elem2)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem1)
+ return false;
+
+ elem3.SetValue(70);
+ elem4.SetValue(60);
+
+ victim.Insert(&elem3);
+ victim.Insert(&elem1);
+ victim.Insert(&elem2);
+ victim.Insert(&elem4);
+
+ max = victim.ExtractMin();
+ if (max != &elem4)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem3)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem2)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem1)
+ return false;
+
+ elem5.SetValue(50);
+ elem6.SetValue(40);
+ elem7.SetValue(30);
+ elem8.SetValue(20);
+ elem9.SetValue(10);
+
+ victim.Insert(&elem5);
+ victim.Insert(&elem3);
+ victim.Insert(&elem1);
+
+ max = victim.ExtractMin();
+ if (max != &elem5)
+ return false;
+
+ victim.Insert(&elem4);
+ victim.Insert(&elem2);
+
+ max = victim.ExtractMin();
+ if (max != &elem4)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem3)
+ return false;
+
+ victim.Insert(&elem2);
+
+ max = victim.ExtractMin();
+ if (max != &elem2)
+ return false;
+
+ victim.Insert(&elem2);
+ victim.Insert(&elem6);
+
+ max = victim.ExtractMin();
+ if (max != &elem6)
+ return false;
+
+ victim.Insert(&elem6);
+ victim.Insert(&elem3);
+ victim.Insert(&elem4);
+ victim.Insert(&elem5);
+
+ max = victim.ExtractMin();
+ if (max != &elem6)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem5)
+ return false;
+
+ victim.Insert(&elem8);
+ max = victim.ExtractMin();
+ if (max != &elem8)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem4)
+ return false;
+
+ victim.Insert(&elem5);
+ victim.Insert(&elem4);
+ victim.Insert(&elem9);
+ victim.Insert(&elem7);
+ victim.Insert(&elem8);
+ victim.Insert(&elem6);
+
+ max = victim.ExtractMin();
+ if (max != &elem9)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem8)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem7)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem6)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem5)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem4)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem3)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem2)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem2)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem1)
+ return false;
+ max = victim.ExtractMin();
+ if (max != NULL)
+ return false;
+
+ victim.Insert(&elem1);
+ victim.Insert(&elem2);
+ victim.Insert(&elem3);
+ victim.Insert(&elem4);
+ victim.Insert(&elem5);
+ victim.Insert(&elem6);
+ victim.Insert(&elem7);
+ victim.Insert(&elem8);
+ victim.Insert(&elem9);
+
+ max = victim.Remove(&elem7);
+ if (max != &elem7)
+ return false;
+ max = victim.Remove(&elem9);
+ if (max != &elem9)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem8)
+ return false;
+ max = victim.Remove(&elem2);
+ if (max != &elem2)
+ return false;
+ max = victim.Remove(&elem2);
+ if (max != NULL)
+ return false;
+ max = victim.Remove(&elem8);
+ if (max != NULL)
+ return false;
+ max = victim.Remove(&elem5);
+ if (max != &elem5)
+ return false;
+ max = victim.Remove(&elem6);
+ if (max != &elem6)
+ return false;
+ max = victim.Remove(&elem1);
+ if (max != &elem1)
+ return false;
+ max = victim.ExtractMin();
+ if (max != &elem4)
+ return false;
+ max = victim.Remove(&elem1);
+ if (max != NULL)
+ return false;
+
+ return true;
+}
+#endif
diff --git a/CommonUtilitiesLib/OSHeap.h b/CommonUtilitiesLib/OSHeap.h
new file mode 100644
index 0000000..073f812
--- /dev/null
+++ b/CommonUtilitiesLib/OSHeap.h
@@ -0,0 +1,112 @@
+/*
+ *
+ * @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: OSHeap.h
+
+ Contains: Implements a heap
+
+
+*/
+
+#ifndef _OSHEAP_H_
+#define _OSHEAP_H_
+
+#define _OSHEAP_TESTING_ 0
+
+#include "OSCond.h"
+
+class OSHeapElem;
+
+class OSHeap
+{
+ public:
+
+ enum
+ {
+ kDefaultStartSize = 1024 //UInt32
+ };
+
+ OSHeap(UInt32 inStartSize = kDefaultStartSize);
+ ~OSHeap() { if (fHeap != NULL) delete fHeap; }
+
+ //ACCESSORS
+ UInt32 CurrentHeapSize() { return fFreeIndex - 1; }
+ OSHeapElem* PeekMin() { if (CurrentHeapSize() > 0) return fHeap[1]; return NULL; }
+
+ //MODIFIERS
+
+ //These are the two primary operations supported by the heap
+ //abstract data type. both run in log(n) time.
+ void Insert(OSHeapElem* inElem);
+ OSHeapElem* ExtractMin() { return Extract(1); }
+ //removes specified element from the heap
+ OSHeapElem* Remove(OSHeapElem* elem);
+
+#if _OSHEAP_TESTING_
+ //returns true if it passed the test, false otherwise
+ static Bool16 Test();
+#endif
+
+ private:
+
+ OSHeapElem* Extract(UInt32 index);
+
+#if _OSHEAP_TESTING_
+ //verifies that the heap is in fact a heap
+ void SanityCheck(UInt32 root);
+#endif
+
+ OSHeapElem** fHeap;
+ UInt32 fFreeIndex;
+ UInt32 fArraySize;
+};
+
+class OSHeapElem
+{
+ public:
+ OSHeapElem(void* enclosingObject = NULL)
+ : fValue(0), fEnclosingObject(enclosingObject), fCurrentHeap(NULL) {}
+ ~OSHeapElem() {}
+
+ //This data structure emphasizes performance over extensibility
+ //If it were properly object-oriented, the compare routine would
+ //be virtual. However, to avoid the use of v-functions in this data
+ //structure, I am assuming that the objects are compared using a 64 bit number.
+ //
+ void SetValue(SInt64 newValue) { fValue = newValue; }
+ SInt64 GetValue() { return fValue; }
+ void* GetEnclosingObject() { return fEnclosingObject; }
+ void SetEnclosingObject(void* obj) { fEnclosingObject = obj; }
+ Bool16 IsMemberOfAnyHeap() { return fCurrentHeap != NULL; }
+
+ private:
+
+ SInt64 fValue;
+ void* fEnclosingObject;
+ OSHeap* fCurrentHeap;
+
+ friend class OSHeap;
+};
+#endif //_OSHEAP_H_
diff --git a/CommonUtilitiesLib/OSMemory.h b/CommonUtilitiesLib/OSMemory.h
new file mode 100644
index 0000000..40e47a8
--- /dev/null
+++ b/CommonUtilitiesLib/OSMemory.h
@@ -0,0 +1,146 @@
+/*
+ *
+ * @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: OSMemory.h
+
+ Contains: Prototypes for overridden new & delete, definition of OSMemory
+ class which implements some memory leak debugging features.
+
+
+*/
+
+#ifndef __OS_MEMORY_H__
+#define __OS_MEMORY_H__
+
+#include "OSHeaders.h"
+#include "OSQueue.h"
+#include "OSMutex.h"
+
+class OSMemory
+{
+ public:
+
+#if MEMORY_DEBUGGING
+ //If memory debugging is on, clients can get access to data structures that give
+ //memory status.
+ static OSQueue* GetTagQueue() { return &sTagQueue; }
+ static OSMutex* GetTagQueueMutex() { return &sMutex; }
+ static UInt32 GetAllocatedMemory() { return sAllocatedBytes; }
+
+ static void* DebugNew(size_t size, char* inFile, int inLine, Bool16 sizeCheck);
+ static void DebugDelete(void *mem);
+ static Bool16 MemoryDebuggingTest();
+ static void ValidateMemoryQueue();
+
+ enum
+ {
+ kMaxFileNameSize = 48
+ };
+
+ struct TagElem
+ {
+ OSQueueElem elem;
+ char fileName[kMaxFileNameSize];
+ int line;
+ UInt32 tagSize; //how big are objects of this type?
+ UInt32 totMemory; //how much do they currently occupy
+ UInt32 numObjects;//how many are there currently?
+ };
+#endif
+
+ // Provides non-debugging behaviour for new and delete
+ static void* New(size_t inSize);
+ static void Delete(void* inMemory);
+
+ //When memory allocation fails, the server just exits. This sets the code
+ //the server exits with
+ static void SetMemoryError(SInt32 inErr);
+
+#if MEMORY_DEBUGGING
+ private:
+
+ struct MemoryDebugging
+ {
+ OSQueueElem elem;
+ TagElem* tagElem;
+ UInt32 size;
+ };
+ static OSQueue sMemoryQueue;
+ static OSQueue sTagQueue;
+ static UInt32 sAllocatedBytes;
+ static OSMutex sMutex;
+
+#endif
+};
+
+
+// NEW MACRO
+// When memory debugging is on, this macro transparently uses the memory debugging
+// overridden version of the new operator. When memory debugging is off, it just compiles
+// down to the standard new.
+
+#if MEMORY_DEBUGGING
+
+#ifdef NEW
+#error Conflicting Macro "NEW"
+#endif
+
+#define NEW new (__FILE__, __LINE__)
+
+#else
+
+#ifdef NEW
+#error Conflicting Macro "NEW"
+#endif
+
+#define NEW new
+
+#endif
+
+
+//
+// PLACEMENT NEW OPERATOR
+inline void* operator new(size_t, void* ptr) { return ptr;}
+
+#if MEMORY_DEBUGGING
+
+// These versions of the new operator with extra arguments provide memory debugging
+// features.
+
+void* operator new(size_t s, char* inFile, int inLine);
+void* operator new[](size_t s, char* inFile, int inLine);
+
+#endif
+
+// When memory debugging is not on, these are overridden so that if new fails,
+// the process will exit.
+void* operator new (size_t s);
+void* operator new[](size_t s);
+
+void operator delete(void* mem);
+void operator delete[](void* mem);
+
+
+#endif //__OS_MEMORY_H__
diff --git a/CommonUtilitiesLib/OSMutex.cpp b/CommonUtilitiesLib/OSMutex.cpp
new file mode 100644
index 0000000..db1cb4e
--- /dev/null
+++ b/CommonUtilitiesLib/OSMutex.cpp
@@ -0,0 +1,158 @@
+/*
+ *
+ * @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: OSMutex.cpp
+
+ Contains: Platform - independent mutex header. The implementation of this object
+ is platform - specific. Each platform must define an independent
+ QTSSMutex.h & QTSSMutex.cpp file.
+
+ This file is for Mac OS X Server only
+
+
+
+*/
+
+#include "OSMutex.h"
+#include
+#include "SafeStdLib.h"
+#include
+
+// Private globals
+#if __PTHREADS_MUTEXES__
+ static pthread_mutexattr_t *sMutexAttr=NULL;
+ static void MutexAttrInit();
+
+ #if __solaris__
+ static pthread_once_t sMutexAttrInit = {PTHREAD_ONCE_INIT};
+ #else
+ static pthread_once_t sMutexAttrInit = PTHREAD_ONCE_INIT;
+ #endif
+
+#endif
+
+OSMutex::OSMutex()
+{
+#ifdef __Win32__
+ ::InitializeCriticalSection(&fMutex);
+ fHolder = 0;
+ fHolderCount = 0;
+#elif __PTHREADS_MUTEXES__
+ (void)pthread_once(&sMutexAttrInit, MutexAttrInit);
+ (void)pthread_mutex_init(&fMutex, sMutexAttr);
+
+ fHolder = 0;
+ fHolderCount = 0;
+#else
+ fMutex = mymutex_alloc();
+#endif
+}
+
+#if __PTHREADS_MUTEXES__
+void MutexAttrInit()
+{
+ sMutexAttr = (pthread_mutexattr_t*)malloc(sizeof(pthread_mutexattr_t));
+ ::memset(sMutexAttr, 0, sizeof(pthread_mutexattr_t));
+ pthread_mutexattr_init(sMutexAttr);
+}
+#endif
+
+OSMutex::~OSMutex()
+{
+#ifdef __Win32__
+ ::DeleteCriticalSection(&fMutex);
+#elif __PTHREADS_MUTEXES__
+ pthread_mutex_destroy(&fMutex);
+#else
+ mymutex_free(fMutex);
+#endif
+}
+
+#if __PTHREADS_MUTEXES__ || __Win32__
+void OSMutex::RecursiveLock()
+{
+ // We already have this mutex. Just refcount and return
+ if (OSThread::GetCurrentThreadID() == fHolder)
+ {
+ fHolderCount++;
+ return;
+ }
+#ifdef __Win32__
+ ::EnterCriticalSection(&fMutex);
+#else
+ (void)pthread_mutex_lock(&fMutex);
+#endif
+ Assert(fHolder == 0);
+ fHolder = OSThread::GetCurrentThreadID();
+ fHolderCount++;
+ Assert(fHolderCount == 1);
+}
+
+void OSMutex::RecursiveUnlock()
+{
+ if (OSThread::GetCurrentThreadID() != fHolder)
+ return;
+
+ Assert(fHolderCount > 0);
+ fHolderCount--;
+ if (fHolderCount == 0)
+ {
+ fHolder = 0;
+#ifdef __Win32__
+ ::LeaveCriticalSection(&fMutex);
+#else
+ pthread_mutex_unlock(&fMutex);
+#endif
+ }
+}
+
+Bool16 OSMutex::RecursiveTryLock()
+{
+ // We already have this mutex. Just refcount and return
+ if (OSThread::GetCurrentThreadID() == fHolder)
+ {
+ fHolderCount++;
+ return true;
+ }
+
+#ifdef __Win32__
+ Bool16 theErr = (Bool16)::TryEnterCriticalSection(&fMutex); // Return values of this function match our API
+ if (!theErr)
+ return theErr;
+#else
+ int theErr = pthread_mutex_trylock(&fMutex);
+ if (theErr != 0)
+ {
+ Assert(theErr == EBUSY);
+ return false;
+ }
+#endif
+ Assert(fHolder == 0);
+ fHolder = OSThread::GetCurrentThreadID();
+ fHolderCount++;
+ Assert(fHolderCount == 1);
+ return true;
+}
+#endif //__PTHREADS_MUTEXES__ || __Win32__
diff --git a/CommonUtilitiesLib/OSMutex.h b/CommonUtilitiesLib/OSMutex.h
new file mode 100644
index 0000000..6e21319
--- /dev/null
+++ b/CommonUtilitiesLib/OSMutex.h
@@ -0,0 +1,147 @@
+/*
+ *
+ * @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: OSMutex.h
+
+ Contains: Platform - independent mutex header. The implementation of this object
+ is platform - specific. Each platform must define an independent
+ OSMutex.h & OSMutex.cpp file.
+
+ This file is for Mac OS X Server only
+
+
+
+*/
+
+#ifndef _OSMUTEX_H_
+#define _OSMUTEX_H_
+
+#include
+#include "SafeStdLib.h"
+#ifndef __Win32__
+#include
+ #if __PTHREADS_MUTEXES__
+ #if __MacOSX__
+ #ifndef _POSIX_PTHREAD_H
+ #include
+ #endif
+ #else
+ #include
+ #endif
+ #include
+
+ #else
+ #include "mymutex.h"
+ #endif
+#endif
+
+#include "OSHeaders.h"
+#include "OSThread.h"
+#include "MyAssert.h"
+
+class OSCond;
+
+class OSMutex
+{
+ public:
+
+ OSMutex();
+ ~OSMutex();
+
+ inline void Lock();
+ inline void Unlock();
+
+ // Returns true on successful grab of the lock, false on failure
+ inline Bool16 TryLock();
+
+ private:
+
+#ifdef __Win32__
+ CRITICAL_SECTION fMutex;
+
+ DWORD fHolder;
+ UInt32 fHolderCount;
+
+#elif !__PTHREADS_MUTEXES__
+ mymutex_t fMutex;
+#else
+ pthread_mutex_t fMutex;
+ // These two platforms don't implement pthreads recursive mutexes, so
+ // we have to do it manually
+ pthread_t fHolder;
+ UInt32 fHolderCount;
+#endif
+
+#if __PTHREADS_MUTEXES__ || __Win32__
+ void RecursiveLock();
+ void RecursiveUnlock();
+ Bool16 RecursiveTryLock();
+#endif
+ friend class OSCond;
+};
+
+class OSMutexLocker
+{
+ public:
+
+ OSMutexLocker(OSMutex *inMutexP) : fMutex(inMutexP) { if (fMutex != NULL) fMutex->Lock(); }
+ ~OSMutexLocker() { if (fMutex != NULL) fMutex->Unlock(); }
+
+ void Lock() { if (fMutex != NULL) fMutex->Lock(); }
+ void Unlock() { if (fMutex != NULL) fMutex->Unlock(); }
+
+ private:
+
+ OSMutex* fMutex;
+};
+
+void OSMutex::Lock()
+{
+#if __PTHREADS_MUTEXES__ || __Win32__
+ this->RecursiveLock();
+#else
+ mymutex_lock(fMutex);
+#endif //!__PTHREADS__
+}
+
+void OSMutex::Unlock()
+{
+#if __PTHREADS_MUTEXES__ || __Win32__
+ this->RecursiveUnlock();
+#else
+ mymutex_unlock(fMutex);
+#endif //!__PTHREADS__
+}
+
+Bool16 OSMutex::TryLock()
+{
+#if __PTHREADS_MUTEXES__ || __Win32__
+ return this->RecursiveTryLock();
+#else
+ return (Bool16)mymutex_try_lock(fMutex);
+#endif //!__PTHREADS__
+}
+
+#endif //_OSMUTEX_H_
diff --git a/CommonUtilitiesLib/OSMutexRW.cpp b/CommonUtilitiesLib/OSMutexRW.cpp
new file mode 100644
index 0000000..71e18ed
--- /dev/null
+++ b/CommonUtilitiesLib/OSMutexRW.cpp
@@ -0,0 +1,182 @@
+/*
+ *
+ * @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: OSMutex.cpp
+
+ Contains:
+
+
+
+*/
+
+#include "OSMutexRW.h"
+#include "OSMutex.h"
+#include "OSCond.h"
+
+#include
+#include "SafeStdLib.h"
+#include
+
+#if DEBUGMUTEXRW
+ int OSMutexRW::fCount = 0;
+ int OSMutexRW::fMaxCount =0;
+#endif
+
+
+#if DEBUGMUTEXRW
+void OSMutexRW::CountConflict(int i)
+{
+ fCount += i;
+ if (i == -1) qtss_printf("Num Conflicts: %d\n", fMaxCount);
+ if (fCount > fMaxCount)
+ fMaxCount = fCount;
+
+}
+#endif
+
+void OSMutexRW::LockRead()
+{
+ OSMutexLocker locker(&fInternalLock);
+#if DEBUGMUTEXRW
+ if (fState != 0)
+ { qtss_printf("LockRead(conflict) fState = %d active readers = %d, waiting writers = %d, waiting readers=%d\n",fState, fActiveReaders, fWriteWaiters, fReadWaiters);
+ CountConflict(1);
+ }
+
+#endif
+
+ AddReadWaiter();
+ while ( ActiveWriter() // active writer so wait
+ || WaitingWriters() // reader must wait for write waiters
+ )
+ {
+ fReadersCond.Wait(&fInternalLock,OSMutexRW::eMaxWait);
+ }
+
+ RemoveReadWaiter();
+ AddActiveReader(); // add 1 to active readers
+ fActiveReaders = fState;
+
+#if DEBUGMUTEXRW
+// qtss_printf("LockRead(conflict) fState = %d active readers = %d, waiting writers = %d, waiting readers=%d\n",fState, fActiveReaders, fWriteWaiters, fReadWaiters);
+
+#endif
+}
+
+void OSMutexRW::LockWrite()
+{
+ OSMutexLocker locker(&fInternalLock);
+ AddWriteWaiter(); // 1 writer queued
+#if DEBUGMUTEXRW
+
+ if (Active())
+ { qtss_printf("LockWrite(conflict) state = %d active readers = %d, waiting writers = %d, waiting readers=%d\n", fState, fActiveReaders, fWriteWaiters, fReadWaiters);
+ CountConflict(1);
+ }
+
+ qtss_printf("LockWrite 'waiting' fState = %d locked active readers = %d, waiting writers = %d, waiting readers=%d\n",fState, fActiveReaders, fReadWaiters, fWriteWaiters);
+#endif
+
+ while (ActiveReaders()) // active readers
+ {
+ fWritersCond.Wait(&fInternalLock,OSMutexRW::eMaxWait);
+ }
+
+ RemoveWriteWaiter(); // remove from waiting writers
+ SetState(OSMutexRW::eActiveWriterState); // this is the active writer
+ fActiveReaders = fState;
+#if DEBUGMUTEXRW
+// qtss_printf("LockWrite 'locked' fState = %d locked active readers = %d, waiting writers = %d, waiting readers=%d\n",fState, fActiveReaders, fReadWaiters, fWriteWaiters);
+#endif
+
+}
+
+
+void OSMutexRW::Unlock()
+{
+ OSMutexLocker locker(&fInternalLock);
+#if DEBUGMUTEXRW
+// qtss_printf("Unlock active readers = %d, waiting writers = %d, waiting readers=%d\n", fActiveReaders, fReadWaiters, fWriteWaiters);
+
+#endif
+
+ if (ActiveWriter())
+ {
+ SetState(OSMutexRW::eNoWriterState); // this was the active writer
+ if (WaitingWriters()) // there are waiting writers
+ { fWritersCond.Signal();
+ }
+ else
+ { fReadersCond.Broadcast();
+ }
+#if DEBUGMUTEXRW
+ qtss_printf("Unlock(writer) active readers = %d, waiting writers = %d, waiting readers=%d\n", fActiveReaders, fReadWaiters, fWriteWaiters);
+#endif
+ }
+ else
+ {
+ RemoveActiveReader(); // this was a reader
+ if (!ActiveReaders()) // no active readers
+ { SetState(OSMutexRW::eNoWriterState); // this was the active writer now no actives threads
+ fWritersCond.Signal();
+ }
+ }
+ fActiveReaders = fState;
+
+}
+
+
+
+// Returns true on successful grab of the lock, false on failure
+int OSMutexRW::TryLockWrite()
+{
+ int status = EBUSY;
+ OSMutexLocker locker(&fInternalLock);
+
+ if ( !Active() && !WaitingWriters()) // no writers, no readers, no waiting writers
+ {
+ this->LockWrite();
+ status = 0;
+ }
+
+ return status;
+}
+
+int OSMutexRW::TryLockRead()
+{
+ int status = EBUSY;
+ OSMutexLocker locker(&fInternalLock);
+
+ if ( !ActiveWriter() && !WaitingWriters() ) // no current writers but other readers ok
+ {
+ this->LockRead();
+ status = 0;
+ }
+
+ return status;
+}
+
+
+
diff --git a/CommonUtilitiesLib/OSMutexRW.h b/CommonUtilitiesLib/OSMutexRW.h
new file mode 100644
index 0000000..d8fc576
--- /dev/null
+++ b/CommonUtilitiesLib/OSMutexRW.h
@@ -0,0 +1,135 @@
+/*
+ *
+ * @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: OSMutexRW.h
+
+ Contains:
+
+
+
+*/
+
+#ifndef _OSMUTEXRW_H_
+#define _OSMUTEXRW_H_
+
+#include
+#include "SafeStdLib.h"
+#include "OSHeaders.h"
+#include "OSThread.h"
+#include "MyAssert.h"
+#include "OSMutex.h"
+#include "OSQueue.h"
+
+#define DEBUGMUTEXRW 0
+
+class OSMutexRW
+{
+ public:
+
+ OSMutexRW(): fState(0), fWriteWaiters(0),fReadWaiters(0),fActiveReaders(0) {} ;
+
+ void LockRead();
+ void LockWrite();
+ void Unlock();
+
+ // Returns 0 on success, EBUSY on failure
+ int TryLockWrite();
+ int TryLockRead();
+
+ private:
+ enum {eMaxWait = 0x0FFFFFFF, eMultiThreadCondition = true, };
+ enum {eActiveWriterState = -1, eNoWriterState = 0 };
+
+ OSMutex fInternalLock; // the internal lock
+ OSCond fReadersCond; // the waiting readers
+ OSCond fWritersCond; // the waiting writers
+ int fState; // -1:writer,0:free,>0:readers
+ int fWriteWaiters; // number of waiting writers
+ int fReadWaiters; // number of waiting readers
+ int fActiveReaders; // number of active readers = fState >= 0;
+
+ inline void AdjustState(int i) { fState += i; };
+ inline void AdjustWriteWaiters(int i) { fWriteWaiters += i; };
+ inline void AdjustReadWaiters(int i) { fReadWaiters += i; };
+ inline void SetState(int i) { fState = i; };
+ inline void SetWriteWaiters(int i) { fWriteWaiters = i; };
+ inline void SetReadWaiters(int i) { fReadWaiters = i; };
+
+ inline void AddWriteWaiter() { AdjustWriteWaiters(1); };
+ inline void RemoveWriteWaiter() { AdjustWriteWaiters(-1); };
+
+ inline void AddReadWaiter() { AdjustReadWaiters(1); };
+ inline void RemoveReadWaiter() { AdjustReadWaiters(-1); };
+
+ inline void AddActiveReader() { AdjustState(1); };
+ inline void RemoveActiveReader() { AdjustState(-1); };
+
+
+ inline Bool16 WaitingWriters() {return (Bool16) (fWriteWaiters > 0) ; }
+ inline Bool16 WaitingReaders() {return (Bool16) (fReadWaiters > 0) ;}
+ inline Bool16 Active() {return (Bool16) (fState != 0) ;}
+ inline Bool16 ActiveReaders() {return (Bool16) (fState > 0) ;}
+ inline Bool16 ActiveWriter() {return (Bool16) (fState < 0) ;} // only one
+
+ #if DEBUGMUTEXRW
+ static int fCount, fMaxCount;
+ static OSMutex sCountMutex;
+ void CountConflict(int i);
+ #endif
+
+};
+
+class OSMutexReadWriteLocker
+{
+ public:
+ OSMutexReadWriteLocker(OSMutexRW *inMutexPtr): fRWMutexPtr(inMutexPtr) {};
+ ~OSMutexReadWriteLocker() { if (fRWMutexPtr != NULL) fRWMutexPtr->Unlock(); }
+
+
+ void UnLock() { if (fRWMutexPtr != NULL) fRWMutexPtr->Unlock(); }
+ void SetMutex(OSMutexRW *mutexPtr) {fRWMutexPtr = mutexPtr;}
+ OSMutexRW* fRWMutexPtr;
+};
+
+class OSMutexReadLocker: public OSMutexReadWriteLocker
+{
+ public:
+
+ OSMutexReadLocker(OSMutexRW *inMutexPtr) : OSMutexReadWriteLocker(inMutexPtr) { if (OSMutexReadWriteLocker::fRWMutexPtr != NULL) OSMutexReadWriteLocker::fRWMutexPtr->LockRead(); }
+ void Lock() { if (OSMutexReadWriteLocker::fRWMutexPtr != NULL) OSMutexReadWriteLocker::fRWMutexPtr->LockRead(); }
+};
+
+class OSMutexWriteLocker: public OSMutexReadWriteLocker
+{
+ public:
+
+ OSMutexWriteLocker(OSMutexRW *inMutexPtr) : OSMutexReadWriteLocker(inMutexPtr) { if (OSMutexReadWriteLocker::fRWMutexPtr != NULL) OSMutexReadWriteLocker::fRWMutexPtr->LockWrite(); }
+ void Lock() { if (OSMutexReadWriteLocker::fRWMutexPtr != NULL) OSMutexReadWriteLocker::fRWMutexPtr->LockWrite(); }
+
+};
+
+
+
+#endif //_OSMUTEX_H_
diff --git a/CommonUtilitiesLib/OSQueue.cpp b/CommonUtilitiesLib/OSQueue.cpp
new file mode 100644
index 0000000..11800e1
--- /dev/null
+++ b/CommonUtilitiesLib/OSQueue.cpp
@@ -0,0 +1,262 @@
+/*
+ *
+ * @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: OSQueue.cpp
+
+ Contains: implements OSQueue class
+
+
+*/
+
+#include "OSQueue.h"
+
+
+OSQueue::OSQueue() : fLength(0)
+{
+ fSentinel.fNext = &fSentinel;
+ fSentinel.fPrev = &fSentinel;
+}
+
+void OSQueue::EnQueue(OSQueueElem* elem)
+{
+ Assert(elem != NULL);
+ if (elem->fQueue == this)
+ return;
+ Assert(elem->fQueue == NULL);
+ elem->fNext = fSentinel.fNext;
+ elem->fPrev = &fSentinel;
+ elem->fQueue = this;
+ fSentinel.fNext->fPrev = elem;
+ fSentinel.fNext = elem;
+ fLength++;
+}
+
+OSQueueElem* OSQueue::DeQueue()
+{
+ if (fLength > 0)
+ {
+ OSQueueElem* elem = fSentinel.fPrev;
+ Assert(fSentinel.fPrev != &fSentinel);
+ elem->fPrev->fNext = &fSentinel;
+ fSentinel.fPrev = elem->fPrev;
+ elem->fQueue = NULL;
+ fLength--;
+ return elem;
+ }
+ else
+ return NULL;
+}
+
+void OSQueue::Remove(OSQueueElem* elem)
+{
+ Assert(elem != NULL);
+ Assert(elem != &fSentinel);
+
+ if (elem->fQueue == this)
+ {
+ elem->fNext->fPrev = elem->fPrev;
+ elem->fPrev->fNext = elem->fNext;
+ elem->fQueue = NULL;
+ fLength--;
+ }
+}
+
+#if OSQUEUETESTING
+Bool16 OSQueue::Test()
+{
+ OSQueue theVictim;
+ void *x = (void*)1;
+ OSQueueElem theElem1(x);
+ x = (void*)2;
+ OSQueueElem theElem2(x);
+ x = (void*)3;
+ OSQueueElem theElem3(x);
+
+ if (theVictim.GetHead() != NULL)
+ return false;
+ if (theVictim.GetTail() != NULL)
+ return false;
+
+ theVictim.EnQueue(&theElem1);
+ if (theVictim.GetHead() != &theElem1)
+ return false;
+ if (theVictim.GetTail() != &theElem1)
+ return false;
+
+ OSQueueElem* theElem = theVictim.DeQueue();
+ if (theElem != &theElem1)
+ return false;
+
+ if (theVictim.GetHead() != NULL)
+ return false;
+ if (theVictim.GetTail() != NULL)
+ return false;
+
+ theVictim.EnQueue(&theElem1);
+ theVictim.EnQueue(&theElem2);
+
+ if (theVictim.GetHead() != &theElem1)
+ return false;
+ if (theVictim.GetTail() != &theElem2)
+ return false;
+
+ theElem = theVictim.DeQueue();
+ if (theElem != &theElem1)
+ return false;
+
+ if (theVictim.GetHead() != &theElem2)
+ return false;
+ if (theVictim.GetTail() != &theElem2)
+ return false;
+
+ theElem = theVictim.DeQueue();
+ if (theElem != &theElem2)
+ return false;
+
+ theVictim.EnQueue(&theElem1);
+ theVictim.EnQueue(&theElem2);
+ theVictim.EnQueue(&theElem3);
+
+ if (theVictim.GetHead() != &theElem1)
+ return false;
+ if (theVictim.GetTail() != &theElem3)
+ return false;
+
+ theElem = theVictim.DeQueue();
+ if (theElem != &theElem1)
+ return false;
+
+ if (theVictim.GetHead() != &theElem2)
+ return false;
+ if (theVictim.GetTail() != &theElem3)
+ return false;
+
+ theElem = theVictim.DeQueue();
+ if (theElem != &theElem2)
+ return false;
+
+ if (theVictim.GetHead() != &theElem3)
+ return false;
+ if (theVictim.GetTail() != &theElem3)
+ return false;
+
+ theElem = theVictim.DeQueue();
+ if (theElem != &theElem3)
+ return false;
+
+ theVictim.EnQueue(&theElem1);
+ theVictim.EnQueue(&theElem2);
+ theVictim.EnQueue(&theElem3);
+
+ OSQueueIter theIterVictim(&theVictim);
+ if (theIterVictim.IsDone())
+ return false;
+ if (theIterVictim.GetCurrent() != &theElem3)
+ return false;
+ theIterVictim.Next();
+ if (theIterVictim.IsDone())
+ return false;
+ if (theIterVictim.GetCurrent() != &theElem2)
+ return false;
+ theIterVictim.Next();
+ if (theIterVictim.IsDone())
+ return false;
+ if (theIterVictim.GetCurrent() != &theElem1)
+ return false;
+ theIterVictim.Next();
+ if (!theIterVictim.IsDone())
+ return false;
+ if (theIterVictim.GetCurrent() != NULL)
+ return false;
+
+ theVictim.Remove(&theElem1);
+
+ if (theVictim.GetHead() != &theElem2)
+ return false;
+ if (theVictim.GetTail() != &theElem3)
+ return false;
+
+ theVictim.Remove(&theElem1);
+
+ if (theVictim.GetHead() != &theElem2)
+ return false;
+ if (theVictim.GetTail() != &theElem3)
+ return false;
+
+ theVictim.Remove(&theElem3);
+
+ if (theVictim.GetHead() != &theElem2)
+ return false;
+ if (theVictim.GetTail() != &theElem2)
+ return false;
+
+ return true;
+}
+#endif
+
+
+
+void OSQueueIter::Next()
+{
+ if (fCurrentElemP == fQueueP->GetTail())
+ fCurrentElemP = NULL;
+ else
+ fCurrentElemP = fCurrentElemP->Prev();
+}
+
+
+OSQueueElem* OSQueue_Blocking::DeQueueBlocking(OSThread* inCurThread, SInt32 inTimeoutInMilSecs)
+{
+ OSMutexLocker theLocker(&fMutex);
+#ifdef __Win32_
+ if (fQueue.GetLength() == 0)
+ { fCond.Wait(&fMutex, inTimeoutInMilSecs);
+ return NULL;
+ }
+#else
+ if (fQueue.GetLength() == 0)
+ fCond.Wait(&fMutex, inTimeoutInMilSecs);
+#endif
+
+ OSQueueElem* retval = fQueue.DeQueue();
+ return retval;
+}
+
+OSQueueElem* OSQueue_Blocking::DeQueue()
+{
+ OSMutexLocker theLocker(&fMutex);
+ OSQueueElem* retval = fQueue.DeQueue();
+ return retval;
+}
+
+
+void OSQueue_Blocking::EnQueue(OSQueueElem* obj)
+{
+ {
+ OSMutexLocker theLocker(&fMutex);
+ fQueue.EnQueue(obj);
+ }
+ fCond.Signal();
+}
diff --git a/CommonUtilitiesLib/OSQueue.h b/CommonUtilitiesLib/OSQueue.h
new file mode 100644
index 0000000..80c167e
--- /dev/null
+++ b/CommonUtilitiesLib/OSQueue.h
@@ -0,0 +1,151 @@
+/*
+ *
+ * @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: OSQueue.h
+
+ Contains: implements OSQueue class
+
+
+*/
+
+#ifndef _OSQUEUE_H_
+#define _OSQUEUE_H_
+
+#include "MyAssert.h"
+#include "OSHeaders.h"
+#include "OSMutex.h"
+#include "OSCond.h"
+#include "OSThread.h"
+
+#define OSQUEUETESTING 0
+
+class OSQueue;
+
+class OSQueueElem {
+ public:
+ OSQueueElem(void* enclosingObject = NULL) : fNext(NULL), fPrev(NULL), fQueue(NULL),
+ fEnclosingObject(enclosingObject) {}
+ virtual ~OSQueueElem() { Assert(fQueue == NULL); }
+
+ Bool16 IsMember(const OSQueue& queue) { return (&queue == fQueue); }
+ Bool16 IsMemberOfAnyQueue() { return fQueue != NULL; }
+ void* GetEnclosingObject() { return fEnclosingObject; }
+ void SetEnclosingObject(void* obj) { fEnclosingObject = obj; }
+
+ OSQueueElem* Next() { return fNext; }
+ OSQueueElem* Prev() { return fPrev; }
+ OSQueue* InQueue() { return fQueue; }
+ inline void Remove();
+
+ private:
+
+ OSQueueElem* fNext;
+ OSQueueElem* fPrev;
+ OSQueue * fQueue;
+ void* fEnclosingObject;
+
+ friend class OSQueue;
+};
+
+class OSQueue {
+ public:
+ OSQueue();
+ ~OSQueue() {}
+
+ void EnQueue(OSQueueElem* object);
+ OSQueueElem* DeQueue();
+
+ OSQueueElem* GetHead() { if (fLength > 0) return fSentinel.fPrev; return NULL; }
+ OSQueueElem* GetTail() { if (fLength > 0) return fSentinel.fNext; return NULL; }
+ UInt32 GetLength() { return fLength; }
+
+ void Remove(OSQueueElem* object);
+
+#if OSQUEUETESTING
+ static Bool16 Test();
+#endif
+
+ protected:
+
+ OSQueueElem fSentinel;
+ UInt32 fLength;
+};
+
+class OSQueueIter
+{
+ public:
+ OSQueueIter(OSQueue* inQueue) : fQueueP(inQueue), fCurrentElemP(inQueue->GetHead()) {}
+ OSQueueIter(OSQueue* inQueue, OSQueueElem* startElemP ) : fQueueP(inQueue)
+ {
+ if ( startElemP )
+ { Assert( startElemP->IsMember(*inQueue ) );
+ fCurrentElemP = startElemP;
+
+ }
+ else
+ fCurrentElemP = NULL;
+ }
+ ~OSQueueIter() {}
+
+ void Reset() { fCurrentElemP = fQueueP->GetHead(); }
+
+ OSQueueElem* GetCurrent() { return fCurrentElemP; }
+ void Next();
+
+ Bool16 IsDone() { return fCurrentElemP == NULL; }
+
+ private:
+
+ OSQueue* fQueueP;
+ OSQueueElem* fCurrentElemP;
+};
+
+class OSQueue_Blocking
+{
+ public:
+ OSQueue_Blocking() {}
+ ~OSQueue_Blocking() {}
+
+ OSQueueElem* DeQueueBlocking(OSThread* inCurThread, SInt32 inTimeoutInMilSecs);
+ OSQueueElem* DeQueue();//will not block
+ void EnQueue(OSQueueElem* obj);
+
+ OSCond* GetCond() { return &fCond; }
+ OSQueue* GetQueue() { return &fQueue; }
+
+ private:
+
+ OSCond fCond;
+ OSMutex fMutex;
+ OSQueue fQueue;
+};
+
+
+void OSQueueElem::Remove()
+{
+ if (fQueue != NULL)
+ fQueue->Remove(this);
+}
+#endif //_OSQUEUE_H_
diff --git a/CommonUtilitiesLib/OSRef.cpp b/CommonUtilitiesLib/OSRef.cpp
new file mode 100644
index 0000000..35d0d92
--- /dev/null
+++ b/CommonUtilitiesLib/OSRef.cpp
@@ -0,0 +1,197 @@
+/*
+ *
+ * @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: OSRef.cpp
+
+ Contains: Implementation of OSRef class.
+
+
+*/
+
+#include "OSRef.h"
+
+#include
+
+UInt32 OSRefTableUtils::HashString(StrPtrLen* inString)
+{
+ Assert(inString != NULL);
+ Assert(inString->Ptr != NULL);
+ Assert(inString->Len > 0);
+ if (inString == NULL || inString->Len == 0 || inString->Ptr == NULL)
+ return 0;
+
+ //make sure to convert to unsigned here, as there may be binary
+ //data in this string
+ UInt8* theData = (UInt8*)inString->Ptr;
+
+ //divide by 4 and take the characters at quarter points in the string,
+ //use those as the basis for the hash value
+ UInt32 quarterLen = inString->Len >> 2;
+ return (inString->Len * (theData[0] + theData[quarterLen] +
+ theData[quarterLen * 2] + theData[quarterLen * 3] +
+ theData[inString->Len - 1]));
+}
+
+OS_Error OSRefTable::Register(OSRef* inRef)
+{
+ Assert(inRef != NULL);
+ if (inRef == NULL)
+ return EPERM;
+#if DEBUG
+ Assert(!inRef->fInATable);
+#endif
+ Assert(inRef->fRefCount == 0);
+ Assert(inRef->fString.Ptr != NULL);
+ Assert(inRef->fString.Len != 0);
+
+ OSMutexLocker locker(&fMutex);
+ if (inRef->fString.Ptr == NULL || inRef->fString.Len == 0)
+ { //printf("OSRefTable::Register inRef is invalid \n");
+ return EPERM;
+ }
+
+ // Check for a duplicate. In this function, if there is a duplicate,
+ // return an error, don't resolve the duplicate
+ OSRefKey key(&inRef->fString);
+ OSRef* duplicateRef = fTable.Map(&key);
+ if (duplicateRef != NULL)
+ return EPERM;
+
+ // There is no duplicate, so add this ref into the table
+#if DEBUG
+ inRef->fInATable = true;
+#endif
+ fTable.Add(inRef);
+ return OS_NoErr;
+}
+
+
+OSRef* OSRefTable::RegisterOrResolve(OSRef* inRef)
+{
+ Assert(inRef != NULL);
+#if DEBUG
+ Assert(!inRef->fInATable);
+#endif
+ Assert(inRef->fRefCount == 0);
+
+ OSMutexLocker locker(&fMutex);
+
+ // Check for a duplicate. If there is one, resolve it and return it to the caller
+ OSRef* duplicateRef = this->Resolve(&inRef->fString);
+ if (duplicateRef != NULL)
+ return duplicateRef;
+
+ // There is no duplicate, so add this ref into the table
+#if DEBUG
+ inRef->fInATable = true;
+#endif
+ fTable.Add(inRef);
+ return NULL;
+}
+
+void OSRefTable::UnRegister(OSRef* ref, UInt32 refCount)
+{
+ Assert(ref != NULL);
+ OSMutexLocker locker(&fMutex);
+
+ //make sure that no one else is using the object
+ while (ref->fRefCount > refCount)
+ ref->fCond.Wait(&fMutex);
+
+#if DEBUG
+ OSRefKey key(&ref->fString);
+ if (ref->fInATable)
+ Assert(fTable.Map(&key) != NULL);
+ ref->fInATable = false;
+#endif
+
+ //ok, we now definitely have no one else using this object, so
+ //remove it from the table
+ fTable.Remove(ref);
+}
+
+Bool16 OSRefTable::TryUnRegister(OSRef* ref, UInt32 refCount)
+{
+ OSMutexLocker locker(&fMutex);
+ if (ref->fRefCount > refCount)
+ return false;
+
+ // At this point, this is guarenteed not to block, because
+ // we've already checked that the refCount is low.
+ this->UnRegister(ref, refCount);
+ return true;
+}
+
+
+OSRef* OSRefTable::Resolve(StrPtrLen* inUniqueID)
+{
+ Assert(inUniqueID != NULL);
+ OSRefKey key(inUniqueID);
+
+ //this must be done atomically wrt the table
+ OSMutexLocker locker(&fMutex);
+ OSRef* ref = fTable.Map(&key);
+ if (ref != NULL)
+ {
+ ref->fRefCount++;
+ Assert(ref->fRefCount > 0);
+ }
+ return ref;
+}
+
+void OSRefTable::Release(OSRef* ref)
+{
+ Assert(ref != NULL);
+ OSMutexLocker locker(&fMutex);
+ ref->fRefCount--;
+ // fRefCount is a UInt32 and QTSS should never run into
+ // a ref greater than 16 * 64K, so this assert just checks to
+ // be sure that we have not decremented the ref less than zero.
+ Assert( ref->fRefCount < 1048576L );
+ //make sure to wakeup anyone who may be waiting for this resource to be released
+ ref->fCond.Signal();
+}
+
+void OSRefTable::Swap(OSRef* newRef)
+{
+ Assert(newRef != NULL);
+ OSMutexLocker locker(&fMutex);
+
+ OSRefKey key(&newRef->fString);
+ OSRef* oldRef = fTable.Map(&key);
+ if (oldRef != NULL)
+ {
+ fTable.Remove(oldRef);
+ fTable.Add(newRef);
+#if DEBUG
+ newRef->fInATable = true;
+ oldRef->fInATable = false;
+ oldRef->fSwapCalled = true;
+#endif
+ }
+ else
+ Assert(0);
+}
+
diff --git a/CommonUtilitiesLib/OSRef.h b/CommonUtilitiesLib/OSRef.h
new file mode 100644
index 0000000..f7524eb
--- /dev/null
+++ b/CommonUtilitiesLib/OSRef.h
@@ -0,0 +1,258 @@
+/*
+ *
+ * @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: OSRef.h
+
+ Contains: Class supports creating unique string IDs to object pointers. A grouping
+ of an object and its string ID may be stored in an OSRefTable, and the
+ associated object pointer can be looked up by string ID.
+
+ Refs can only be removed from the table when no one is using the ref,
+ therefore allowing clients to arbitrate access to objects in a preemptive,
+ multithreaded environment.
+
+
+
+
+*/
+
+#ifndef _OSREF_H_
+#define _OSREF_H_
+
+
+#include "StrPtrLen.h"
+#include "OSHashTable.h"
+#include "OSCond.h"
+
+class OSRefKey;
+
+class OSRefTableUtils
+{
+ private:
+
+ static UInt32 HashString(StrPtrLen* inString);
+
+ friend class OSRef;
+ friend class OSRefKey;
+};
+
+class OSRef
+{
+ public:
+
+ OSRef() : fObjectP(NULL), fRefCount(0), fNextHashEntry(NULL)
+ {
+#if DEBUG
+ fInATable = false;
+ fSwapCalled = false;
+#endif
+ }
+ OSRef(const StrPtrLen &inString, void* inObjectP)
+ : fRefCount(0), fNextHashEntry(NULL)
+ { Set(inString, inObjectP); }
+ ~OSRef() {}
+
+ void Set(const StrPtrLen& inString, void* inObjectP)
+ {
+#if DEBUG
+ fInATable = false;
+ fSwapCalled = false;
+#endif
+ fString = inString; fObjectP = inObjectP;
+ fHashValue = OSRefTableUtils::HashString(&fString);
+ }
+
+#if DEBUG
+ Bool16 IsInTable() { return fInATable; }
+#endif
+ void** GetObjectPtr() { return &fObjectP; }
+ void* GetObject() { return fObjectP; }
+ UInt32 GetRefCount() { return fRefCount; }
+ StrPtrLen *GetString() { return &fString; }
+ private:
+
+ //value
+ void* fObjectP;
+ //key
+ StrPtrLen fString;
+
+ //refcounting
+ UInt32 fRefCount;
+#if DEBUG
+ Bool16 fInATable;
+ Bool16 fSwapCalled;
+#endif
+ OSCond fCond;//to block threads waiting for this ref.
+
+ UInt32 fHashValue;
+ OSRef* fNextHashEntry;
+
+ friend class OSRefKey;
+ friend class OSHashTable;
+ friend class OSHashTableIter;
+ friend class OSRefTable;
+
+};
+
+
+class OSRefKey
+{
+public:
+
+ //CONSTRUCTOR / DESTRUCTOR:
+ OSRefKey(StrPtrLen* inStringP)
+ : fStringP(inStringP)
+ { fHashValue = OSRefTableUtils::HashString(inStringP); }
+
+ ~OSRefKey() {}
+
+
+ //ACCESSORS:
+ StrPtrLen* GetString() { return fStringP; }
+
+
+private:
+
+ //PRIVATE ACCESSORS:
+ SInt32 GetHashKey() { return fHashValue; }
+
+ //these functions are only used by the hash table itself. This constructor
+ //will break the "Set" functions.
+ OSRefKey(OSRef *elem) : fStringP(&elem->fString),
+ fHashValue(elem->fHashValue) {}
+
+ friend int operator ==(const OSRefKey &key1, const OSRefKey &key2)
+ {
+ if (key1.fStringP->Equal(*key2.fStringP))
+ return true;
+ return false;
+ }
+
+ //data:
+ StrPtrLen *fStringP;
+ UInt32 fHashValue;
+
+ friend class OSHashTable;
+};
+
+typedef OSHashTable OSRefHashTable;
+typedef OSHashTableIter OSRefHashTableIter;
+
+class OSRefTable
+{
+ public:
+
+ enum
+ {
+ kDefaultTableSize = 1193 //UInt32
+ };
+
+ //tableSize doesn't indicate the max number of Refs that can be added
+ //(it's unlimited), but is rather just how big to make the hash table
+ OSRefTable(UInt32 tableSize = kDefaultTableSize) : fTable(tableSize), fMutex() {}
+ ~OSRefTable() {}
+
+ //Allows access to the mutex in case you need to lock the table down
+ //between operations
+ OSMutex* GetMutex() { return &fMutex; }
+ OSRefHashTable* GetHashTable() { return &fTable; }
+
+ //Registers a Ref in the table. Once the Ref is in, clients may resolve
+ //the ref by using its string ID. You must setup the Ref before passing it
+ //in here, ie., setup the string and object pointers
+ //This function will succeed unless the string identifier is not unique,
+ //in which case it will return QTSS_DupName
+ //This function is atomic wrt this ref table.
+ OS_Error Register(OSRef* ref);
+
+ // RegisterOrResolve
+ // If the ID of the input ref is unique, this function is equivalent to
+ // Register, and returns NULL.
+ // If there is a duplicate ID already in the map, this funcion
+ // leave it, resolves it, and returns it.
+ OSRef* RegisterOrResolve(OSRef* inRef);
+
+ //This function may block. You can only remove a Ref from the table
+ //when the refCount drops to the level specified. If several threads have
+ //the ref currently, the calling thread will wait until the other threads
+ //stop using the ref (by calling Release, below)
+ //This function is atomic wrt this ref table.
+ void UnRegister(OSRef* ref, UInt32 refCount = 0);
+
+ // Same as UnRegister, but guarenteed not to block. Will return
+ // true if ref was sucessfully unregistered, false otherwise
+ Bool16 TryUnRegister(OSRef* ref, UInt32 refCount = 0);
+
+ //Resolve. This function uses the provided key string to identify and grab
+ //the Ref keyed by that string. Once the Ref is resolved, it is safe to use
+ //(it cannot be removed from the Ref table) until you call Release. Because
+ //of that, you MUST call release in a timely manner, and be aware of potential
+ //deadlocks because you now own a resource being contended over.
+ //This function is atomic wrt this ref table.
+ OSRef* Resolve(StrPtrLen* inString);
+
+ //Release. Release a Ref, and drops its refCount. After calling this, the
+ //Ref is no longer safe to use, as it may be removed from the ref table.
+ void Release(OSRef* inRef);
+
+ // Swap. This atomically removes any existing Ref in the table with the new
+ // ref's ID, and replaces it with this new Ref. If there is no matching Ref
+ // already in the table, this function does nothing.
+ //
+ // Be aware that this creates a situation where clients may have a Ref resolved
+ // that is no longer in the table. The old Ref must STILL be UnRegistered normally.
+ // Once Swap completes sucessfully, clients that call resolve on the ID will get
+ // the new OSRef object.
+ void Swap(OSRef* newRef);
+
+ UInt32 GetNumRefsInTable() { UInt64 result = fTable.GetNumEntries(); Assert(result < kUInt32_Max); return (UInt32) result; }
+
+ private:
+
+
+ //all this object needs to do its job is an atomic hashtable
+ OSRefHashTable fTable;
+ OSMutex fMutex;
+};
+
+
+class OSRefReleaser
+{
+ public:
+
+ OSRefReleaser(OSRefTable* inTable, OSRef* inRef) : fOSRefTable(inTable), fOSRef(inRef) {}
+ ~OSRefReleaser() { fOSRefTable->Release(fOSRef); }
+
+ OSRef* GetRef() { return fOSRef; }
+
+ private:
+
+ OSRefTable* fOSRefTable;
+ OSRef* fOSRef;
+};
+
+
+
+#endif //_OSREF_H_
diff --git a/CommonUtilitiesLib/OSThread.cpp b/CommonUtilitiesLib/OSThread.cpp
new file mode 100644
index 0000000..10c7e23
--- /dev/null
+++ b/CommonUtilitiesLib/OSThread.cpp
@@ -0,0 +1,347 @@
+/*
+ *
+ * @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: OSThread.cpp
+
+ Contains: Thread abstraction implementation
+
+
+
+*/
+
+#include
+#include
+#include "SafeStdLib.h"
+#include
+#include
+
+#ifdef __MacOSX__
+#include
+#include
+#endif
+
+#ifndef __Win32__
+ #if __PTHREADS__
+ #include
+ #if USE_THR_YIELD
+ #include
+ #endif
+ #else
+ #include
+ #include
+ #endif
+ #include
+ #include
+ #include
+#endif
+
+#include "OSThread.h"
+#include "MyAssert.h"
+
+#ifdef __sgi__
+#include
+#endif
+
+
+//
+// OSThread.cp
+//
+void* OSThread::sMainThreadData = NULL;
+
+#ifdef __Win32__
+DWORD OSThread::sThreadStorageIndex = 0;
+#elif __PTHREADS__
+pthread_key_t OSThread::gMainKey = 0;
+#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
+pthread_attr_t OSThread::sThreadAttr;
+#endif
+#endif
+
+char OSThread::sUser[128]= "";
+char OSThread::sGroup[128]= "";
+
+#if __linux__ || __MacOSX__
+Bool16 OSThread::sWrapSleep = true;
+#endif
+
+void OSThread::Initialize()
+{
+
+
+#ifdef __Win32__
+ sThreadStorageIndex = ::TlsAlloc();
+ Assert(sThreadStorageIndex >= 0);
+#elif __PTHREADS__
+ pthread_key_create(&OSThread::gMainKey, NULL);
+#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
+
+ //
+ // Added for Solaris...
+
+ pthread_attr_init(&sThreadAttr);
+ /* Indicate we want system scheduling contention scope. This
+ thread is permanently "bound" to an LWP */
+ pthread_attr_setscope(&sThreadAttr, PTHREAD_SCOPE_SYSTEM);
+#endif
+
+#endif
+}
+
+OSThread::OSThread()
+: fStopRequested(false),
+ fJoined(false),
+ fThreadData(NULL)
+{
+}
+
+OSThread::~OSThread()
+{
+ this->StopAndWaitForThread();
+}
+
+
+
+void OSThread::Start()
+{
+#ifdef __Win32__
+ unsigned int theId = 0; // We don't care about the identifier
+ fThreadID = (HANDLE)_beginthreadex( NULL, // Inherit security
+ 0, // Inherit stack size
+ _Entry, // Entry function
+ (void*)this, // Entry arg
+ 0, // Begin executing immediately
+ &theId );
+ Assert(fThreadID != NULL);
+#elif __PTHREADS__
+ pthread_attr_t* theAttrP;
+#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
+ //theAttrP = &sThreadAttr;
+ theAttrP = 0;
+#else
+ theAttrP = NULL;
+#endif
+ int err = pthread_create((pthread_t*)&fThreadID, theAttrP, _Entry, (void*)this);
+ Assert(err == 0);
+#else
+ fThreadID = (UInt32)cthread_fork((cthread_fn_t)_Entry, (any_t)this);
+#endif
+}
+
+void OSThread::StopAndWaitForThread()
+{
+ fStopRequested = true;
+ if (!fJoined)
+ Join();
+}
+
+void OSThread::Join()
+{
+ // What we're trying to do is allow the thread we want to delete to complete
+ // running. So we wait for it to stop.
+ Assert(!fJoined);
+ fJoined = true;
+#ifdef __Win32__
+ DWORD theErr = ::WaitForSingleObject(fThreadID, INFINITE);
+ Assert(theErr == WAIT_OBJECT_0);
+#elif __PTHREADS__
+ void *retVal;
+ pthread_join((pthread_t)fThreadID, &retVal);
+#else
+ cthread_join((cthread_t)fThreadID);
+#endif
+}
+
+void OSThread::ThreadYield()
+{
+ // on platforms who's threading is not pre-emptive yield
+ // to another thread
+#if THREADING_IS_COOPERATIVE
+ #if __PTHREADS__
+ #if USE_THR_YIELD
+ thr_yield();
+ #else
+ sched_yield();
+ #endif
+ #endif
+#endif
+}
+
+#include "OS.h"
+void OSThread::Sleep(UInt32 inMsec)
+{
+
+#ifdef __Win32__
+ ::Sleep(inMsec);
+#elif __linux__ || __MacOSX__
+
+ if (inMsec == 0)
+ return;
+
+ SInt64 startTime = OS::Milliseconds();
+ SInt64 timeLeft = inMsec;
+ SInt64 timeSlept = 0;
+ UInt64 utimeLeft = 0;
+
+ do { // loop in case we leave the sleep early
+ //qtss_printf("OSThread::Sleep time slept= %qd request sleep=%qd\n",timeSlept, timeLeft);
+ timeLeft = inMsec - timeSlept;
+ if (timeLeft < 1)
+ break;
+
+ utimeLeft = timeLeft * 1000;
+ //qtss_printf("OSThread::Sleep usleep=%qd\n", utimeLeft);
+ ::usleep(utimeLeft);
+
+ timeSlept = (OS::Milliseconds() - startTime);
+ if (timeSlept < 0) // system time set backwards
+ break;
+
+ } while (timeSlept < inMsec);
+
+ //qtss_printf("total sleep = %qd request sleep=%"_U32BITARG_"\n", timeSlept,inMsec);
+
+#elif defined(__osf__) || defined(__hpux__)
+ if (inMsec < 1000)
+ ::usleep(inMsec * 1000); // useconds must be less than 1,000,000
+ else
+ ::sleep((inMsec + 500) / 1000); // round to the nearest whole second
+#elif defined(__sgi__)
+ struct timespec ts;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = inMsec * 1000000;
+
+ nanosleep(&ts, 0);
+#else
+ ::usleep(inMsec * 1000);
+#endif
+}
+
+#ifdef __Win32__
+unsigned int WINAPI OSThread::_Entry(LPVOID inThread)
+#else
+void* OSThread::_Entry(void *inThread) //static
+#endif
+{
+ OSThread* theThread = (OSThread*)inThread;
+#ifdef __Win32__
+ BOOL theErr = ::TlsSetValue(sThreadStorageIndex, theThread);
+ Assert(theErr == TRUE);
+#elif __PTHREADS__
+ theThread->fThreadID = (pthread_t)pthread_self();
+ pthread_setspecific(OSThread::gMainKey, theThread);
+#else
+ theThread->fThreadID = (UInt32)cthread_self();
+ cthread_set_data(cthread_self(), (any_t)theThread);
+#endif
+ theThread->SwitchPersonality();
+ //
+ // Run the thread
+ theThread->Entry();
+ return NULL;
+}
+
+
+Bool16 OSThread::SwitchPersonality()
+{
+#if __linux__
+ if (::strlen(sGroup) > 0)
+ {
+ struct group* gr = ::getgrnam(sGroup);
+ if (gr == NULL || ::setgid(gr->gr_gid) == -1)
+ {
+ //qtss_printf("thread %"_U32BITARG_" setgid to group=%s FAILED \n", (UInt32) this, sGroup);
+ return false;
+ }
+
+ //qtss_printf("thread %"_U32BITARG_" setgid to group=%s \n", (UInt32) this, sGroup);
+ }
+
+
+ if (::strlen(sUser) > 0)
+ {
+ struct passwd* pw = ::getpwnam(sUser);
+ if (pw == NULL || ::setuid(pw->pw_uid) == -1)
+ {
+ //qtss_printf("thread %"_U32BITARG_" setuid to user=%s FAILED \n", (UInt32) this, sUser);
+ return false;
+ }
+
+ //qtss_printf("thread %"_U32BITARG_" setuid to user=%s \n", (UInt32) this, sUser);
+ }
+#endif
+
+ return true;
+}
+
+
+OSThread* OSThread::GetCurrent()
+{
+#ifdef __Win32__
+ return (OSThread *)::TlsGetValue(sThreadStorageIndex);
+#elif __PTHREADS__
+ return (OSThread *)pthread_getspecific(OSThread::gMainKey);
+#else
+ return (OSThread*)cthread_data(cthread_self());
+#endif
+}
+
+#ifdef __Win32__
+int OSThread::GetErrno()
+{
+ int winErr = ::GetLastError();
+
+
+ // Convert to a POSIX errorcode. The *major* assumption is that
+ // the meaning of these codes is 1-1 and each Winsock, etc, etc
+ // function is equivalent in errors to the POSIX standard. This is
+ // a big assumption, but the server only checks for a small subset of
+ // the real errors, on only a small number of functions, so this is probably ok.
+ switch (winErr)
+ {
+
+ case ERROR_FILE_NOT_FOUND: return ENOENT;
+
+ case ERROR_PATH_NOT_FOUND: return ENOENT;
+
+
+
+
+ case WSAEINTR: return EINTR;
+ case WSAENETRESET: return EPIPE;
+ case WSAENOTCONN: return ENOTCONN;
+ case WSAEWOULDBLOCK:return EAGAIN;
+ case WSAECONNRESET: return EPIPE;
+ case WSAEADDRINUSE: return EADDRINUSE;
+ case WSAEMFILE: return EMFILE;
+ case WSAEINPROGRESS:return EINPROGRESS;
+ case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL;
+ case WSAECONNABORTED: return EPIPE;
+ case 0: return 0;
+
+ default: return ENOTCONN;
+ }
+}
+#endif
diff --git a/CommonUtilitiesLib/OSThread.h b/CommonUtilitiesLib/OSThread.h
new file mode 100644
index 0000000..2b3e900
--- /dev/null
+++ b/CommonUtilitiesLib/OSThread.h
@@ -0,0 +1,172 @@
+/*
+ *
+ * @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: OSThread.h
+
+ Contains: A thread abstraction
+
+
+
+*/
+
+// OSThread.h
+#ifndef __OSTHREAD__
+#define __OSTHREAD__
+
+#ifndef __Win32__
+#if __PTHREADS__
+#if __solaris__ || __sgi__ || __hpux__
+ #include
+#else
+ #include
+#endif
+ #include