/* * * @APPLE_LICENSE_HEADER_START@ * * 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. * * @APPLE_LICENSE_HEADER_END@ * *//* File: QTSSDictionary.h Contains: Definitions of two classes: QTSSDictionary and QTSSDictionaryMap. Collectively, these classes implement the "dictionary" APIs in QTSS API. A QTSSDictionary corresponds to a QTSS_Object, a QTSSDictionaryMap corresponds to a QTSS_ObjectType. Created: Tue, Mar 2, 1999 @ 4:23 PM*/#ifndef _QTSSDICTIONARY_H_#define _QTSSDICTIONARY_H_#include <stdlib.h>#include "SafeStdLib.h"#include "QTSS.h"#include "OSHeaders.h"#include "OSMutex.h"#include "StrPtrLen.h"#include "MyAssert.h"#include "QTSSStream.h"class QTSSDictionary;class QTSSDictionaryMap;class QTSSAttrInfoDict;#define __DICTIONARY_TESTING__ 0//// Function prototype for attr functionstypedef void* (*QTSS_AttrFunctionPtr)(QTSSDictionary* , UInt32* );class QTSSDictionary : public QTSSStream{ public: // // CONSTRUCTOR / DESTRUCTOR QTSSDictionary(QTSSDictionaryMap* inMap, OSMutex* inMutex = NULL); virtual ~QTSSDictionary(); // // QTSS API CALLS // Flags used by internal callers of these routines enum { kNoFlags = 0, kDontObeyReadOnly = 1, kDontCallCompletionRoutine = 2 }; // This version of GetValue copies the element into a buffer provided by the caller // Returns: QTSS_BadArgument, QTSS_NotPreemptiveSafe (if attribute is not preemptive safe), // QTSS_BadIndex (if inIndex is bad) QTSS_Error GetValue(QTSS_AttributeID inAttrID, UInt32 inIndex, void* ioValueBuffer, UInt32* ioValueLen); //This version of GetValue returns a pointer to the internal buffer for the attribute. //Only usable if the attribute is preemptive safe. // // Returns: Same as above, but also QTSS_NotEnoughSpace, if value is too big for buffer. QTSS_Error GetValuePtr(QTSS_AttributeID inAttrID, UInt32 inIndex, void** outValueBuffer, UInt32* outValueLen) { return GetValuePtr(inAttrID, inIndex, outValueBuffer, outValueLen, false); } // This version of GetValue converts the value to a string before returning it. Memory for // the string is allocated internally. // // Returns: QTSS_BadArgument, QTSS_BadIndex, QTSS_ValueNotFound QTSS_Error GetValueAsString(QTSS_AttributeID inAttrID, UInt32 inIndex, char** outString); // Returns: QTSS_BadArgument, QTSS_ReadOnly (if attribute is read only), // QTSS_BadIndex (attempt to set indexed parameter with param retrieval) QTSS_Error SetValue(QTSS_AttributeID inAttrID, UInt32 inIndex, const void* inBuffer, UInt32 inLen, UInt32 inFlags = kNoFlags); // Returns: QTSS_BadArgument, QTSS_ReadOnly (if attribute is read only), QTSS_Error SetValuePtr(QTSS_AttributeID inAttrID, const void* inBuffer, UInt32 inLen, UInt32 inFlags = kNoFlags); // Returns: QTSS_BadArgument, QTSS_ReadOnly (if attribute is read only), QTSS_Error CreateObjectValue(QTSS_AttributeID inAttrID, UInt32* outIndex, QTSSDictionary** newObject, QTSSDictionaryMap* inMap = NULL, UInt32 inFlags = kNoFlags); // Returns: QTSS_BadArgument, QTSS_ReadOnly, QTSS_BadIndex QTSS_Error RemoveValue(QTSS_AttributeID inAttrID, UInt32 inIndex, UInt32 inFlags = kNoFlags); // Utility routine used by the two external flavors of GetValue QTSS_Error GetValuePtr(QTSS_AttributeID inAttrID, UInt32 inIndex, void** outValueBuffer, UInt32* outValueLen, Bool16 isInternal); // // accessORS QTSSDictionaryMap* GetDictionaryMap() { return fMap; } // Returns the Instance dictionary map for this dictionary. This may return NULL // if there are no instance attributes in this dictionary QTSSDictionaryMap* GetInstanceDictMap() { return fInstanceMap; } // Returns the number of values associated with a given attribute UInt32 GetNumValues(QTSS_AttributeID inAttrID); void SetNumValues(QTSS_AttributeID inAttrID, UInt32 inNumValues); // Meant only for internal server use. Does no error checking, // doesn't invoke the param retrieval function. StrPtrLen* GetValue(QTSS_AttributeID inAttrID) { return &fAttributes[inAttrID].fAttributeData; } OSMutex* GetMutex() { return fMutexP; } void SetLocked(Bool16 inLocked) { fLocked = inLocked; } Bool16 IsLocked() { return fLocked; } // // GETTING ATTRIBUTE INFO QTSS_Error GetAttrInfoByIndex(UInt32 inIndex, QTSSAttrInfoDict** outAttrInfoDict); QTSS_Error GetAttrInfoByName(const char* inAttrName, QTSSAttrInfoDict** outAttrInfoDict); QTSS_Error GetAttrInfoByID(QTSS_AttributeID inAttrID, QTSSAttrInfoDict** outAttrInfoDict); // // INSTANCE ATTRIBUTES QTSS_Error AddInstanceAttribute( const char* inAttrName, QTSS_AttrFunctionPtr inFuncPtr, QTSS_AttrDataType inDataType, QTSS_AttrPermission inPermission ); QTSS_Error RemoveInstanceAttribute(QTSS_AttributeID inAttr); // // MODIFIERS // These functions are meant to be used by the server when it is setting up the // dictionary attributes. They do no error checking. // They don't set fNumAttributes & fAllocatedInternally. void SetVal(QTSS_AttributeID inAttrID, void* inValueBuffer, UInt32 inBufferLen); void SetVal(QTSS_AttributeID inAttrID, StrPtrLen* inNewValue) { this->SetVal(inAttrID, inNewValue->Ptr, inNewValue->Len); } // Call this if you want to assign empty storage to an attribute void SetEmptyVal(QTSS_AttributeID inAttrID, void* inBuf, UInt32 inBufLen); #if __DICTIONARY_TESTING__ static void Test(); // API test for these objects#endif protected: // Derived classes can provide a completion routine for some dictionary functions virtual void RemoveValueComplete(UInt32 /*inAttrIndex*/, QTSSDictionaryMap* /*inMap*/, UInt32 /*inValueIndex*/) {} virtual void SetValueComplete(UInt32 /*inAttrIndex*/, QTSSDictionaryMap* /*inMap*/, UInt32 /*inValueIndex*/, void* /*inNewValue*/, UInt32 /*inNewValueLen*/) {} virtual void RemoveInstanceAttrComplete(UInt32 /*inAttrindex*/, QTSSDictionaryMap* /*inMap*/) {} virtual QTSSDictionary* CreateNewDictionary(QTSSDictionaryMap* inMap, OSMutex* inMutex); private: struct DictValueElement { // This stores all necessary information for each attribute value. DictValueElement() : fAllocatedLen(0), fNumAttributes(0), fAllocatedInternally(false), fIsDynamicDictionary(false) {} // Does not delete! You Must call DeleteAttributeData for that ~DictValueElement() {} StrPtrLen fAttributeData; // The data UInt32 fAllocatedLen; // How much space do we have allocated? UInt32 fNumAttributes; // If this is an iterated attribute, how many? Bool16 fAllocatedInternally; //Should we delete this memory? Bool16 fIsDynamicDictionary; //is this a dictionary object? }; DictValueElement* fAttributes; DictValueElement* fInstanceAttrs; UInt32 fInstanceArraySize; QTSSDictionaryMap* fMap; QTSSDictionaryMap* fInstanceMap; OSMutex* fMutexP; Bool16 fMyMutex; Bool16 fLocked; void DeleteAttributeData(DictValueElement* inDictValues, UInt32 inNumValues);};class QTSSAttrInfoDict : public QTSSDictionary{ public: struct AttrInfo { // This is all the relevent information for each dictionary // attribute. char fAttrName[QTSS_MAX_ATTRIBUTE_NAME_SIZE + 1]; QTSS_AttrFunctionPtr fFuncPtr; QTSS_AttrDataType fAttrDataType; QTSS_AttrPermission fAttrPermission; }; QTSSAttrInfoDict(); virtual ~QTSSAttrInfoDict(); private: AttrInfo fAttrInfo; QTSS_AttributeID fID; static AttrInfo sAttributes[]; friend class QTSSDictionaryMap;};class QTSSDictionaryMap{ public: // // This must be called before using any QTSSDictionary or QTSSDictionaryMap functionality static void Initialize(); // Stores all meta-information for attributes // CONSTRUCTOR FLAGS enum { kNoFlags = 0, kAllowRemoval = 1, //是否允许删除元素 kIsInstanceMap = 2, kInstanceAttrsAllowed = 4, kCompleteFunctionsAllowed = 8 }; // // CONSTRUCTOR / DESTRUCTOR QTSSDictionaryMap(UInt32 inNumReservedAttrs, UInt32 inFlags = kNoFlags); ~QTSSDictionaryMap(){ delete fAttrArray; } // // QTSS API CALLS //添加属性,从头遍历如果发现有 QTSS_Error AddAttribute( const char* inAttrName, QTSS_AttrFunctionPtr inFuncPtr, QTSS_AttrDataType inDataType, QTSS_AttrPermission inPermission ); // // Marks this attribute as removed QTSS_Error RemoveAttribute(QTSS_AttributeID inAttrID); QTSS_Error UnRemoveAttribute(QTSS_AttributeID inAttrID); QTSS_Error CheckRemovePermission(QTSS_AttributeID inAttrID); // // Searching / Iteration. These never return removed attributes QTSS_Error GetAttrInfoByName(const char* inAttrName, QTSSAttrInfoDict** outAttrInfoDict, Bool16 returnRemovedAttr = false); QTSS_Error GetAttrInfoByID(QTSS_AttributeID inID, QTSSAttrInfoDict** outAttrInfoDict); QTSS_Error GetAttrInfoByIndex(UInt32 inIndex, QTSSAttrInfoDict** outAttrInfoDict); QTSS_Error GetAttrID(const char* inAttrName, QTSS_AttributeID* outID); // // PRIVATE ATTR PERMISSIONS //是否已经删除属性 enum { qtssPrivateAttrModeRemoved = 0x80000000 }; // // CONVERTING attribute IDs to array indexes. Returns -1 if inAttrID doesn't exist inline SInt32 ConvertAttrIDToArrayIndex(QTSS_AttributeID inAttrID); static Bool16 IsInstanceAttrID(QTSS_AttributeID inAttrID) { return (inAttrID & 0x80000000) != 0; } // ACCESSORS // These functions do no error checking. Be careful. // Includes removed attributes UInt32 GetNumAttrs() { return fNextAvailableID; } UInt32 GetNumNonRemovedAttrs() { return fNumValidAttrs; } Bool16 IsPreemptiveSafe(UInt32 inIndex) { Assert(inIndex < fNextAvailableID); return (Bool16) (fAttrArray[inIndex]->fAttrInfo.fAttrPermission & qtssAttrModePreempSafe); } Bool16 IsWriteable(UInt32 inIndex) { Assert(inIndex < fNextAvailableID); return (Bool16) (fAttrArray[inIndex]->fAttrInfo.fAttrPermission & qtssAttrModeWrite); } Bool16 IsCacheable(UInt32 inIndex) { Assert(inIndex < fNextAvailableID); return (Bool16) (fAttrArray[inIndex]->fAttrInfo.fAttrPermission & qtssAttrModeCacheable); } Bool16 IsRemoved(UInt32 inIndex) { Assert(inIndex < fNextAvailableID); return (Bool16) (fAttrArray[inIndex]->fAttrInfo.fAttrPermission & qtssPrivateAttrModeRemoved) ; } QTSS_AttrFunctionPtr GetAttrFunction(UInt32 inIndex) { Assert(inIndex < fNextAvailableID); return fAttrArray[inIndex]->fAttrInfo.fFuncPtr; } char* GetAttrName(UInt32 inIndex) { Assert(inIndex < fNextAvailableID); return fAttrArray[inIndex]->fAttrInfo.fAttrName; } QTSS_AttributeID GetAttrID(UInt32 inIndex) { Assert(inIndex < fNextAvailableID); return fAttrArray[inIndex]->fID; } QTSS_AttrDataType GetAttrType(UInt32 inIndex) { Assert(inIndex < fNextAvailableID); return fAttrArray[inIndex]->fAttrInfo.fAttrDataType; } Bool16 InstanceAttrsAllowed() { return (Bool16) (fFlags & kInstanceAttrsAllowed); } Bool16 CompleteFunctionsAllowed() { return (Bool16) (fFlags & kCompleteFunctionsAllowed) ; } // MODIFIERS // Sets this attribute ID to have this information void SetAttribute( QTSS_AttributeID inID, const char* inAttrName, QTSS_AttrFunctionPtr inFuncPtr, QTSS_AttrDataType inDataType, QTSS_AttrPermission inPermission ); // // DICTIONARY MAPS // All dictionary maps are stored here, and are accessable // through these routines // This enum allows all QTSSDictionaryMaps to be stored in an array enum { kServerDictIndex = 0, kPrefsDictIndex = 1, kTextMessagesDictIndex = 2, kServiceDictIndex = 3, kRTPStreamDictIndex = 4, kClientsessionDictIndex = 5, kRTSPSessionDictIndex = 6, kRTSPRequestDictIndex = 7, kRTSPHeaderDictIndex = 8, kFileDictIndex = 9, kModuleDictIndex = 10, kModulePrefsDictIndex = 11, kAttrInfoDictIndex = 12, kQTSSUserProfileDictIndex = 13, kQTSSConnectedUserDictIndex = 14, kNumDictionaries = 15, kNumDynamicDictionaryTypes = 500, kIllegalDictionary = kNumDynamicDictionaryTypes + kNumDictionaries }; // This function converts a QTSS_ObjectType to an index static UInt32 GetMapIndex(QTSS_ObjectType inType); // Using one of the above predefined indexes, this returns the corresponding map static QTSSDictionaryMap* GetMap(UInt32 inIndex) { Assert(inIndex < kNumDynamicDictionaryTypes + kNumDictionaries); return sDictionaryMaps[inIndex]; } static QTSS_ObjectType CreateNewMap(); private: // // Repository for dictionary maps static QTSSDictionaryMap* sDictionaryMaps[kNumDictionaries + kNumDynamicDictionaryTypes]; static UInt32 sNextDynamicMap; enum { kMinArraySize = 20 }; UInt32 fNextAvailableID;//所有的分配过的属性,只要该属性有fid标识就算(包含qtssPrivateAttrModeRemoved标识) UInt32 fNumValidAttrs; //所有没有qtssPrivateAttrModeRemoved标识的属性 UInt32 fAttrArraySize; //整个数组的大小。如果添加元素超过该大小,会自动增加成之前的两倍。fAttrArraySize>=fNextAvailableID QTSSAttrInfoDict** fAttrArray; UInt32 fFlags; friend class QTSSDictionary;};inline SInt32 QTSSDictionaryMap::ConvertAttrIDToArrayIndex(QTSS_AttributeID inAttrID){ SInt32 theIndex = inAttrID & 0x7FFFFFFF; if ((theIndex < 0) || (theIndex >= (SInt32)fNextAvailableID)) return -1; else return theIndex;}#endif/* * * @APPLE_LICENSE_HEADER_START@ * * 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. * * @APPLE_LICENSE_HEADER_END@ * *//* File: QTSSDictionary.cpp Contains: Implementation of object defined in QTSSDictionary.h */#include "QTSSDictionary.h"#include <string.h>#include <stdio.h>#include "MyAssert.h"#include "OSMemory.h"#include "QTSSDataConverter.h"#include <errno.h>QTSSDictionary::QTSSDictionary(QTSSDictionaryMap* inMap, OSMutex* inMutex) : fAttributes(NULL), fInstanceAttrs(NULL), fInstanceArraySize(0), fMap(inMap), fInstanceMap(NULL), fMutexP(inMutex), fMyMutex(false), fLocked(false){ if (fMap != NULL) fAttributes = NEW DictValueElement[inMap->GetNumAttrs()]; if (fMutexP == NULL) { fMyMutex = true; fMutexP = NEW OSMutex(); }}QTSSDictionary::~QTSSDictionary(){ if (fMap != NULL) this->DeleteAttributeData(fAttributes, fMap->GetNumAttrs()); if (fAttributes != NULL) delete [] fAttributes; delete fInstanceMap; this->DeleteAttributeData(fInstanceAttrs, fInstanceArraySize); delete [] fInstanceAttrs; if (fMyMutex) delete fMutexP;}QTSSDictionary* QTSSDictionary::CreateNewDictionary(QTSSDictionaryMap* inMap, OSMutex* inMutex){ return NEW QTSSDictionary(inMap, inMutex);}QTSS_Error QTSSDictionary::GetValuePtr(QTSS_AttributeID inAttrID, UInt32 inIndex, void** outValueBuffer, UInt32* outValueLen, Bool16 isInternal){ // Check first to see if this is a static attribute or an instance attribute QTSSDictionaryMap* theMap = fMap; DictValueElement* theAttrs = fAttributes; if (QTSSDictionaryMap::IsInstanceAttrID(inAttrID)) { theMap = fInstanceMap; theAttrs = fInstanceAttrs; } if (theMap == NULL) return QTSS_AttrDoesntExist; SInt32 theMapIndex = theMap->ConvertAttrIDToArrayIndex(inAttrID); if (theMapIndex < 0) return QTSS_AttrDoesntExist; if (theMap->IsRemoved(theMapIndex)) return QTSS_AttrDoesntExist; if ((!isInternal) && (!theMap->IsPreemptiveSafe(theMapIndex)) && !this->IsLocked()) return QTSS_NotPreemptiveSafe; // An iterated attribute cannot have a param retrieval function if ((inIndex > 0) && (theMap->GetAttrFunction(theMapIndex) != NULL)) return QTSS_BadIndex; // Check to make sure the index parameter is legal if ((inIndex > 0) && (inIndex >= theAttrs[theMapIndex].fNumAttributes)) return QTSS_BadIndex; // Retrieve the parameter char* theBuffer = theAttrs[theMapIndex].fAttributeData.Ptr; *outValueLen = theAttrs[theMapIndex].fAttributeData.Len; Bool16 cacheable = theMap->IsCacheable(theMapIndex); if ( (theMap->GetAttrFunction(theMapIndex) != NULL) && ((cacheable && (*outValueLen == 0)) || !cacheable) ) { // If function is cacheable: // If the parameter doesn't have a value assigned yet, and there is an attribute // retrieval function provided, invoke that function now. // If function is *not* cacheable: // always call the function theBuffer = (char*)theMap->GetAttrFunction(theMapIndex)(this, outValueLen); //If the param retrieval function didn't return an explicit value for this attribute, //refetch the parameter out of the array, in case the function modified it. if (theBuffer == NULL) { theBuffer = theAttrs[theMapIndex].fAttributeData.Ptr; *outValueLen = theAttrs[theMapIndex].fAttributeData.Len; } }#if DEBUG else // Make sure we aren't outside the bounds of attribute memory Assert(theAttrs[theMapIndex].fAllocatedLen >= (theAttrs[theMapIndex].fAttributeData.Len * (theAttrs[theMapIndex].fNumAttributes)));#endif // Return an error if there is no data for this attribute if (*outValueLen == 0) return QTSS_ValueNotFound; theBuffer += theAttrs[theMapIndex].fAttributeData.Len * inIndex; *outValueBuffer = theBuffer; // strings need an extra dereference - moved it up if ((theMap->GetAttrType(theMapIndex) == qtssAttrDataTypeCharArray) && (theAttrs[theMapIndex].fNumAttributes > 1)) { char** string = (char**)theBuffer; *outValueBuffer = *string; //*outValueLen = strlen(*string) + 1; *outValueLen = strlen(*string); } return QTSS_NoErr;}QTSS_Error QTSSDictionary::GetValue(QTSS_AttributeID inAttrID, UInt32 inIndex, void* ioValueBuffer, UInt32* ioValueLen){ // If there is a mutex, lock it and get a pointer to the proper attribute OSMutexLocker locker(fMutexP); void* tempValueBuffer = NULL; UInt32 tempValueLen = 0; QTSS_Error theErr = this->GetValuePtr(inAttrID, inIndex, &tempValueBuffer, &tempValueLen, true); if (theErr != QTSS_NoErr) return theErr; if (theErr == QTSS_NoErr) { // If caller provided a buffer that's too small for this attribute, report that error if (tempValueLen > *ioValueLen) theErr = QTSS_NotEnoughSpace; // Only copy out the attribute if the buffer is big enough if ((ioValueBuffer != NULL) && (theErr == QTSS_NoErr)) ::memcpy(ioValueBuffer, tempValueBuffer, tempValueLen); // Always set the ioValueLen to be the actual length of the attribute. *ioValueLen = tempValueLen; } return QTSS_NoErr;}QTSS_Error QTSSDictionary::GetValueAsString(QTSS_AttributeID inAttrID, UInt32 inIndex, char** outString){ void* tempValueBuffer; UInt32 tempValueLen = 0; if (outString == NULL) return QTSS_BadArgument; OSMutexLocker locker(fMutexP); QTSS_Error theErr = this->GetValuePtr(inAttrID, inIndex, &tempValueBuffer, &tempValueLen, true); if (theErr != QTSS_NoErr) return theErr; QTSSDictionaryMap* theMap = fMap; if (QTSSDictionaryMap::IsInstanceAttrID(inAttrID)) theMap = fInstanceMap; if (theMap == NULL) return QTSS_AttrDoesntExist; SInt32 theMapIndex = theMap->ConvertAttrIDToArrayIndex(inAttrID); Assert(theMapIndex >= 0); *outString = QTSSDataConverter::ValueToString(tempValueBuffer, tempValueLen, theMap->GetAttrType(theMapIndex)); return QTSS_NoErr;}QTSS_Error QTSSDictionary::CreateObjectValue(QTSS_AttributeID inAttrID, UInt32* outIndex, QTSSDictionary** newObject, QTSSDictionaryMap* inMap, UInt32 inFlags){ // Check first to see if this is a static attribute or an instance attribute QTSSDictionaryMap* theMap = fMap; DictValueElement* theAttrs = fAttributes; if (QTSSDictionaryMap::IsInstanceAttrID(inAttrID)) { theMap = fInstanceMap; theAttrs = fInstanceAttrs; } if (theMap == NULL) return QTSS_AttrDoesntExist; SInt32 theMapIndex = theMap->ConvertAttrIDToArrayIndex(inAttrID); // If there is a mutex, make this action atomic. OSMutexLocker locker(fMutexP); if (theMapIndex < 0) return QTSS_AttrDoesntExist; if ((!(inFlags & kDontObeyReadOnly)) && (!theMap->IsWriteable(theMapIndex))) return QTSS_ReadOnly; if (theMap->IsRemoved(theMapIndex)) return QTSS_AttrDoesntExist; if (theMap->GetAttrType(theMapIndex) != qtssAttrDataTypeQTSS_Object) return QTSS_BadArgument; UInt32 numValues = theAttrs[theMapIndex].fNumAttributes; // if normal QTSSObjects have been added, then we can't add a dynamic one if (!theAttrs[theMapIndex].fIsDynamicDictionary && (numValues > 0)) return QTSS_ReadOnly; QTSSDictionary* oldDict = NULL; *outIndex = numValues; // add the object into the next spot UInt32 len = sizeof(QTSSDictionary*); QTSSDictionary* dict = CreateNewDictionary(inMap, fMutexP); // kind of a hack to avoid the check in SetValue theAttrs[theMapIndex].fIsDynamicDictionary = false; QTSS_Error err = SetValue(inAttrID, *outIndex, &dict, len, inFlags); if (err != QTSS_NoErr) { delete dict; return err; } if (oldDict != NULL) { delete oldDict; } theAttrs[theMapIndex].fIsDynamicDictionary = true; *newObject = dict; return QTSS_NoErr;}QTSS_Error QTSSDictionary::SetValue(QTSS_AttributeID inAttrID, UInt32 inIndex, const void* inBuffer, UInt32 inLen, UInt32 inFlags){ // Check first to see if this is a static attribute or an instance attribute QTSSDictionaryMap* theMap = fMap; DictValueElement* theAttrs = fAttributes; if (QTSSDictionaryMap::IsInstanceAttrID(inAttrID)) { theMap = fInstanceMap; theAttrs = fInstanceAttrs; } if (theMap == NULL) return QTSS_AttrDoesntExist; SInt32 theMapIndex = theMap->ConvertAttrIDToArrayIndex(inAttrID); // If there is a mutex, make this action atomic. OSMutexLocker locker(fMutexP); if (theMapIndex < 0) return QTSS_AttrDoesntExist; if ((!(inFlags & kDontObeyReadOnly)) && (!theMap->IsWriteable(theMapIndex))) return QTSS_ReadOnly; if (theMap->IsRemoved(theMapIndex)) return QTSS_AttrDoesntExist; if (theAttrs[theMapIndex].fIsDynamicDictionary) return QTSS_ReadOnly; UInt32 numValues = theAttrs[theMapIndex].fNumAttributes; QTSS_AttrDataType dataType = theMap->GetAttrType(theMapIndex); UInt32 attrLen = inLen; if (dataType == qtssAttrDataTypeCharArray) { if (inIndex > 0) attrLen = sizeof(char*); // value just contains a pointer if ((numValues == 1) && (inIndex == 1)) { // we're adding a second value, so we need to change the storage from directly // storing the string to an array of string pointers // creating new memory here just to create a null terminated string // instead of directly using the old storage as the old storage didn't // have its string null terminated UInt32 tempStringLen = theAttrs[theMapIndex].fAttributeData.Len; char* temp = NEW char[tempStringLen + 1]; ::memcpy(temp, theAttrs[theMapIndex].fAttributeData.Ptr, tempStringLen); temp[tempStringLen] = '/0'; delete [] theAttrs[theMapIndex].fAttributeData.Ptr; //char* temp = theAttrs[theMapIndex].fAttributeData.Ptr; theAttrs[theMapIndex].fAllocatedLen = 16 * sizeof(char*); theAttrs[theMapIndex].fAttributeData.Ptr = NEW char[theAttrs[theMapIndex].fAllocatedLen]; theAttrs[theMapIndex].fAttributeData.Len = sizeof(char*); // store off original string as first value in array *(char**)theAttrs[theMapIndex].fAttributeData.Ptr = temp; // question: why isn't theAttrs[theMapIndex].fAllocatedInternally set to true? } } else { // If this attribute is iterated, this new value // must be the same size as all the others. if (((inIndex > 0) || (numValues > 1)) &&(theAttrs[theMapIndex].fAttributeData.Len != 0) && (inLen != theAttrs[theMapIndex].fAttributeData.Len)) return QTSS_BadArgument; } // // Can't put empty space into the array of values if (inIndex > numValues) return QTSS_BadIndex; if ((attrLen * (inIndex + 1)) > theAttrs[theMapIndex].fAllocatedLen) { // We need to reallocate this buffer. UInt32 theLen; if (inIndex == 0) theLen = attrLen; // most attributes are single valued, so allocate just enough space else theLen = 2 * (attrLen * (inIndex + 1));// Allocate twice as much as we need char* theNewBuffer = NEW char[theLen]; if (inIndex > 0) { // Copy out the old attribute data ::memcpy(theNewBuffer, theAttrs[theMapIndex].fAttributeData.Ptr, theAttrs[theMapIndex].fAllocatedLen); } // Now get rid of the old stuff. Delete the buffer // if it was already allocated internally if (theAttrs[theMapIndex].fAllocatedInternally) delete [] theAttrs[theMapIndex].fAttributeData.Ptr; // Finally, update this attribute structure with all the new values. theAttrs[theMapIndex].fAttributeData.Ptr = theNewBuffer; theAttrs[theMapIndex].fAllocatedLen = theLen; theAttrs[theMapIndex].fAllocatedInternally = true; } // At this point, we should always have enough space to write what we want Assert(theAttrs[theMapIndex].fAllocatedLen >= (attrLen * (inIndex + 1))); // Copy the new data to the right place in our data buffer void *attributeBufferPtr; if ((dataType != qtssAttrDataTypeCharArray) || ((numValues < 2) && (inIndex == 0))) { attributeBufferPtr = theAttrs[theMapIndex].fAttributeData.Ptr + (inLen * inIndex); theAttrs[theMapIndex].fAttributeData.Len = inLen; } else { //attributeBufferPtr = NEW char[inLen]; // allocating one extra so that we can null terminate the string attributeBufferPtr = NEW char[inLen + 1]; char* tempBuffer = (char*)attributeBufferPtr; tempBuffer[inLen] = '/0'; //char** valuePtr = (char**)theAttrs[theMapIndex].fAttributeData.Ptr + (inLen * inIndex); // The offset should be (attrLen * inIndex) and not (inLen * inIndex) char** valuePtr = (char**)(theAttrs[theMapIndex].fAttributeData.Ptr + (attrLen * inIndex)); if (inIndex < numValues) // we're replacing an existing string delete *valuePtr; *valuePtr = (char*)attributeBufferPtr; } ::memcpy(attributeBufferPtr, inBuffer, inLen); // Set the number of attributes to be proper if (inIndex >= theAttrs[theMapIndex].fNumAttributes) { // // We should never have to increment num attributes by more than 1 Assert(theAttrs[theMapIndex].fNumAttributes == inIndex); theAttrs[theMapIndex].fNumAttributes++; } // // Call the completion routine if (((fMap == NULL) || fMap->CompleteFunctionsAllowed()) && !(inFlags & kDontCallCompletionRoutine)) this->SetValueComplete(theMapIndex, theMap, inIndex, attributeBufferPtr, inLen); return QTSS_NoErr;}QTSS_Error QTSSDictionary::SetValuePtr(QTSS_AttributeID inAttrID, const void* inBuffer, UInt32 inLen, UInt32 inFlags){ // Check first to see if this is a static attribute or an instance attribute QTSSDictionaryMap* theMap = fMap; DictValueElement* theAttrs = fAttributes; if (QTSSDictionaryMap::IsInstanceAttrID(inAttrID)) { theMap = fInstanceMap; theAttrs = fInstanceAttrs; } if (theMap == NULL) return QTSS_AttrDoesntExist; SInt32 theMapIndex = theMap->ConvertAttrIDToArrayIndex(inAttrID); // If there is a mutex, make this action atomic. OSMutexLocker locker(fMutexP); if (theMapIndex < 0) return QTSS_AttrDoesntExist; if ((!(inFlags & kDontObeyReadOnly)) && (!theMap->IsWriteable(theMapIndex))) return QTSS_ReadOnly; if (theMap->IsRemoved(theMapIndex)) return QTSS_AttrDoesntExist; if (theAttrs[theMapIndex].fIsDynamicDictionary) return QTSS_ReadOnly; UInt32 numValues = theAttrs[theMapIndex].fNumAttributes; if ((numValues > 0) || (theAttrs[theMapIndex].fAttributeData.Ptr != NULL)) return QTSS_BadArgument; // you can only set the pointer if you haven't done set value theAttrs[theMapIndex].fAttributeData.Ptr = (char*) inBuffer; theAttrs[theMapIndex].fAttributeData.Len = inLen; theAttrs[theMapIndex].fAllocatedLen = inLen; // This function assumes there is only one value and that it isn't allocated internally theAttrs[theMapIndex].fNumAttributes = 1; return QTSS_NoErr;}QTSS_Error QTSSDictionary::RemoveValue(QTSS_AttributeID inAttrID, UInt32 inIndex, UInt32 inFlags){ // Check first to see if this is a static attribute or an instance attribute QTSSDictionaryMap* theMap = fMap; DictValueElement* theAttrs = fAttributes; if (QTSSDictionaryMap::IsInstanceAttrID(inAttrID)) { theMap = fInstanceMap; theAttrs = fInstanceAttrs; } if (theMap == NULL) return QTSS_AttrDoesntExist; SInt32 theMapIndex = theMap->ConvertAttrIDToArrayIndex(inAttrID); // If there is a mutex, make this action atomic. OSMutexLocker locker(fMutexP); if (theMapIndex < 0) return QTSS_AttrDoesntExist; if ((!(inFlags & kDontObeyReadOnly)) && (!theMap->IsWriteable(theMapIndex))) return QTSS_ReadOnly; if (theMap->IsRemoved(theMapIndex)) return QTSS_AttrDoesntExist; if ((theMap->GetAttrFunction(theMapIndex) != NULL) && (inIndex > 0)) return QTSS_BadIndex; UInt32 numValues = theAttrs[theMapIndex].fNumAttributes; UInt32 theValueLen = theAttrs[theMapIndex].fAttributeData.Len; if (theAttrs[theMapIndex].fIsDynamicDictionary) { // this is an internally allocated dictionary, so we need to desctruct it Assert(theMap->GetAttrType(theMapIndex) == qtssAttrDataTypeQTSS_Object); Assert(theValueLen == sizeof(QTSSDictionary*)); QTSSDictionary* dict = *(QTSSDictionary**)(theAttrs[theMapIndex].fAttributeData.Ptr + (theValueLen * inIndex)); delete dict; } QTSS_AttrDataType dataType = theMap->GetAttrType(theMapIndex); if ((dataType == qtssAttrDataTypeCharArray) && (numValues > 1)) { // we need to delete the string char* str = *(char**)(theAttrs[theMapIndex].fAttributeData.Ptr + (theValueLen * inIndex)); delete str; } // // If there are values after this one in the array, move them. if (inIndex + 1 < theAttrs[theMapIndex].fNumAttributes) { ::memmove( theAttrs[theMapIndex].fAttributeData.Ptr + (theValueLen * inIndex), theAttrs[theMapIndex].fAttributeData.Ptr + (theValueLen * (inIndex + 1)), theValueLen * ( (theAttrs[theMapIndex].fNumAttributes) - inIndex - 1)); } // else this is the last in the array so just truncate. // // Update our number of values theAttrs[theMapIndex].fNumAttributes--; if (theAttrs[theMapIndex].fNumAttributes == 0) theAttrs[theMapIndex].fAttributeData.Len = 0; if ((dataType == qtssAttrDataTypeCharArray) && (theAttrs[theMapIndex].fNumAttributes == 1)) { // we only have one string left, so we don't need the extra pointer char* str = *(char**)(theAttrs[theMapIndex].fAttributeData.Ptr); delete theAttrs[theMapIndex].fAttributeData.Ptr; theAttrs[theMapIndex].fAttributeData.Ptr = str; theAttrs[theMapIndex].fAttributeData.Len = strlen(str); theAttrs[theMapIndex].fAllocatedLen = strlen(str); } // // Call the completion routine if (((fMap == NULL) || fMap->CompleteFunctionsAllowed()) && !(inFlags & kDontCallCompletionRoutine)) this->RemoveValueComplete(theMapIndex, theMap, inIndex); return QTSS_NoErr;}UInt32 QTSSDictionary::GetNumValues(QTSS_AttributeID inAttrID){ // Check first to see if this is a static attribute or an instance attribute QTSSDictionaryMap* theMap = fMap; DictValueElement* theAttrs = fAttributes; if (QTSSDictionaryMap::IsInstanceAttrID(inAttrID)) { theMap = fInstanceMap; theAttrs = fInstanceAttrs; } if (theMap == NULL) return 0; SInt32 theMapIndex = theMap->ConvertAttrIDToArrayIndex(inAttrID); if (theMapIndex < 0) return 0; return theAttrs[theMapIndex].fNumAttributes;}void QTSSDictionary::SetNumValues(QTSS_AttributeID inAttrID, UInt32 inNumValues){ // Check first to see if this is a static attribute or an instance attribute QTSSDictionaryMap* theMap = fMap; DictValueElement* theAttrs = fAttributes; if (QTSSDictionaryMap::IsInstanceAttrID(inAttrID)) { theMap = fInstanceMap; theAttrs = fInstanceAttrs; } if (theMap == NULL) return; SInt32 theMapIndex = theMap->ConvertAttrIDToArrayIndex(inAttrID); if (theMapIndex < 0) return; UInt32 numAttributes = theAttrs[theMapIndex].fNumAttributes; // this routine can only be ever used to reduce the number of values if (inNumValues >= numAttributes || numAttributes == 0) return; QTSS_AttrDataType dataType = theMap->GetAttrType(theMapIndex); if (theAttrs[theMapIndex].fIsDynamicDictionary || (dataType == qtssAttrDataTypeCharArray)) { // getting rid of dictionaries or strings is tricky, so it's easier to call remove value for (UInt32 removeCount = numAttributes - inNumValues; removeCount > 0; removeCount--) { // the delete index passed to RemoveValue is always the last in the array. this->RemoveValue(inAttrID, theAttrs[theMapIndex].fNumAttributes - 1, kDontObeyReadOnly); } } else { theAttrs[theMapIndex].fNumAttributes = inNumValues; if (inNumValues == 0) theAttrs[theMapIndex].fAttributeData.Len = 0; }}void QTSSDictionary::SetVal( QTSS_AttributeID inAttrID, void* inValueBuffer, UInt32 inBufferLen){ Assert(inAttrID >= 0); Assert(fMap); Assert((UInt32)inAttrID < fMap->GetNumAttrs()); fAttributes[inAttrID].fAttributeData.Ptr = (char*)inValueBuffer; fAttributes[inAttrID].fAttributeData.Len = inBufferLen; fAttributes[inAttrID].fAllocatedLen = inBufferLen; // This function assumes there is only one value and that it isn't allocated internally fAttributes[inAttrID].fNumAttributes = 1;}void QTSSDictionary::SetEmptyVal(QTSS_AttributeID inAttrID, void* inBuf, UInt32 inBufLen){ Assert(inAttrID >= 0); Assert(fMap); Assert((UInt32)inAttrID < fMap->GetNumAttrs()); fAttributes[inAttrID].fAttributeData.Ptr = (char*)inBuf; fAttributes[inAttrID].fAllocatedLen = inBufLen;#if !ALLOW_NON_Word_ALIGN_ACCESS //if (((UInt32) inBuf % 4) > 0) // qtss_printf("bad align by %d/n",((UInt32) inBuf % 4) ); Assert( ((PointerSizedInt) inBuf % 4) == 0 );#endif}QTSS_Error QTSSDictionary::AddInstanceAttribute( const char* inAttrName, QTSS_AttrFunctionPtr inFuncPtr, QTSS_AttrDataType inDataType, QTSS_AttrPermission inPermission ){ if ((fMap != NULL) && !fMap->InstanceAttrsAllowed()) return QTSS_InstanceAttrsNotAllowed; OSMutexLocker locker(fMutexP); // // Check to see if this attribute exists in the static map. If it does, // we can't add it as an instance attribute, so return an error QTSSAttrInfoDict* throwAway = NULL; QTSS_Error theErr; if (fMap != NULL) { theErr = fMap->GetAttrInfoByName(inAttrName, &throwAway); if (theErr == QTSS_NoErr) return QTSS_AttrNameExists; } if (fInstanceMap == NULL) { UInt32 theFlags = QTSSDictionaryMap::kAllowRemoval | QTSSDictionaryMap::kIsInstanceMap; if ((fMap == NULL) || fMap->CompleteFunctionsAllowed()) theFlags |= QTSSDictionaryMap::kCompleteFunctionsAllowed; fInstanceMap = new QTSSDictionaryMap( 0, theFlags ); } // // Add the attribute into the Dictionary Map. theErr = fInstanceMap->AddAttribute(inAttrName, inFuncPtr, inDataType, inPermission); if (theErr != QTSS_NoErr) return theErr; // // Check to see if our DictValueElement array needs to be reallocated if (fInstanceMap->GetNumAttrs() >= fInstanceArraySize) { UInt32 theNewArraySize = fInstanceArraySize * 2; if (theNewArraySize == 0) theNewArraySize = QTSSDictionaryMap::kMinArraySize; Assert(theNewArraySize > fInstanceMap->GetNumAttrs()); DictValueElement* theNewArray = NEW DictValueElement[theNewArraySize]; if (fInstanceAttrs != NULL) { ::memcpy(theNewArray, fInstanceAttrs, sizeof(DictValueElement) * fInstanceArraySize); // // Delete the old instance attr structs, this does not delete the actual attribute memory delete [] fInstanceAttrs; } fInstanceAttrs = theNewArray; fInstanceArraySize = theNewArraySize; } return QTSS_NoErr;}QTSS_Error QTSSDictionary::RemoveInstanceAttribute(QTSS_AttributeID inAttr){ OSMutexLocker locker(fMutexP); if (fInstanceMap != NULL) { QTSS_Error theErr = fInstanceMap->CheckRemovePermission(inAttr); if (theErr != QTSS_NoErr) return theErr; this->SetNumValues(inAttr,(UInt32) 0); // make sure to set num values to 0 since it is a deleted attribute fInstanceMap->RemoveAttribute(inAttr); } else return QTSS_BadArgument; // // Call the completion routine SInt32 theMapIndex = fInstanceMap->ConvertAttrIDToArrayIndex(inAttr); this->RemoveInstanceAttrComplete(theMapIndex, fInstanceMap); return QTSS_NoErr;}QTSS_Error QTSSDictionary::GetAttrInfoByIndex(UInt32 inIndex, QTSSAttrInfoDict** outAttrInfoDict){ if (outAttrInfoDict == NULL) return QTSS_BadArgument; OSMutexLocker locker(fMutexP); UInt32 numInstanceValues = 0; UInt32 numStaticValues = 0; if (fMap != NULL) numStaticValues = fMap->GetNumNonRemovedAttrs(); if (fInstanceMap != NULL) numInstanceValues = fInstanceMap->GetNumNonRemovedAttrs(); if (inIndex >= (numStaticValues + numInstanceValues)) return QTSS_AttrDoesntExist; if ( (numStaticValues > 0) && (inIndex < numStaticValues) ) return fMap->GetAttrInfoByIndex(inIndex, outAttrInfoDict); else { Assert(fInstanceMap != NULL); return fInstanceMap->GetAttrInfoByIndex(inIndex - numStaticValues, outAttrInfoDict); }}QTSS_Error QTSSDictionary::GetAttrInfoByID(QTSS_AttributeID inAttrID, QTSSAttrInfoDict** outAttrInfoDict){ if (outAttrInfoDict == NULL) return QTSS_BadArgument; if (QTSSDictionaryMap::IsInstanceAttrID(inAttrID)) { OSMutexLocker locker(fMutexP); if (fInstanceMap != NULL) return fInstanceMap->GetAttrInfoByID(inAttrID, outAttrInfoDict); } else if (fMap != NULL) return fMap->GetAttrInfoByID(inAttrID, outAttrInfoDict); return QTSS_AttrDoesntExist;}QTSS_Error QTSSDictionary::GetAttrInfoByName(const char* inAttrName, QTSSAttrInfoDict** outAttrInfoDict){ QTSS_Error theErr = QTSS_AttrDoesntExist; if (outAttrInfoDict == NULL) return QTSS_BadArgument; // Retrieve the Dictionary Map for this object type if (fMap != NULL) theErr = fMap->GetAttrInfoByName(inAttrName, outAttrInfoDict); if (theErr == QTSS_AttrDoesntExist) { OSMutexLocker locker(fMutexP); if (fInstanceMap != NULL) theErr = fInstanceMap->GetAttrInfoByName(inAttrName, outAttrInfoDict); } return theErr;}void QTSSDictionary::DeleteAttributeData(DictValueElement* inDictValues, UInt32 inNumValues){ for (UInt32 x = 0; x < inNumValues; x++) { if (inDictValues[x].fAllocatedInternally) delete [] inDictValues[x].fAttributeData.Ptr; }}QTSSAttrInfoDict::AttrInfo QTSSAttrInfoDict::sAttributes[] ={ /* 0 */ { "qtssAttrName", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe }, /* 1 */ { "qtssAttrID", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe }, /* 2 */ { "qtssAttrDataType", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe }, /* 3 */ { "qtssAttrPermissions",NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe }};QTSSAttrInfoDict::QTSSAttrInfoDict(): QTSSDictionary(QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kAttrInfoDictIndex)), fID(qtssIllegalAttrID){}QTSSAttrInfoDict::~QTSSAttrInfoDict() {}QTSSDictionaryMap* QTSSDictionaryMap::sDictionaryMaps[kNumDictionaries + kNumDynamicDictionaryTypes];UInt32 QTSSDictionaryMap::sNextDynamicMap = kNumDictionaries;void QTSSDictionaryMap::Initialize(){ // // Have to do this one first because this dict map is used by all the other // dict maps. sDictionaryMaps[kAttrInfoDictIndex] = new QTSSDictionaryMap(qtssAttrInfoNumParams); // Setup the Attr Info attributes before constructing any other dictionaries for (UInt32 x = 0; x < qtssAttrInfoNumParams; x++) sDictionaryMaps[kAttrInfoDictIndex]->SetAttribute(x, QTSSAttrInfoDict::sAttributes[x].fAttrName, QTSSAttrInfoDict::sAttributes[x].fFuncPtr, QTSSAttrInfoDict::sAttributes[x].fAttrDataType, QTSSAttrInfoDict::sAttributes[x].fAttrPermission); sDictionaryMaps[kServerDictIndex] = new QTSSDictionaryMap(qtssSvrNumParams, QTSSDictionaryMap::kCompleteFunctionsAllowed); sDictionaryMaps[kPrefsDictIndex] = new QTSSDictionaryMap(qtssPrefsNumParams, QTSSDictionaryMap::kInstanceAttrsAllowed | QTSSDictionaryMap::kCompleteFunctionsAllowed); sDictionaryMaps[kTextMessagesDictIndex] = new QTSSDictionaryMap(qtssMsgNumParams); sDictionaryMaps[kServiceDictIndex] = new QTSSDictionaryMap(0); sDictionaryMaps[kRTPStreamDictIndex] = new QTSSDictionaryMap(qtssRTPStrNumParams); sDictionaryMaps[kClientSessionDictIndex]= new QTSSDictionaryMap(qtssCliSesNumParams, QTSSDictionaryMap::kCompleteFunctionsAllowed); sDictionaryMaps[kRTSPSessionDictIndex] = new QTSSDictionaryMap(qtssRTSPSesNumParams); sDictionaryMaps[kRTSPRequestDictIndex] = new QTSSDictionaryMap(qtssRTSPReqNumParams); sDictionaryMaps[kRTSPHeaderDictIndex] = new QTSSDictionaryMap(qtssNumHeaders); sDictionaryMaps[kFileDictIndex] = new QTSSDictionaryMap(qtssFlObjNumParams); sDictionaryMaps[kModuleDictIndex] = new QTSSDictionaryMap(qtssModNumParams); sDictionaryMaps[kModulePrefsDictIndex] = new QTSSDictionaryMap(0, QTSSDictionaryMap::kInstanceAttrsAllowed | QTSSDictionaryMap::kCompleteFunctionsAllowed); sDictionaryMaps[kQTSSUserProfileDictIndex] = new QTSSDictionaryMap(qtssUserNumParams); sDictionaryMaps[kQTSSConnectedUserDictIndex] = new QTSSDictionaryMap(qtssConnectionNumParams);}QTSSDictionaryMap::QTSSDictionaryMap(UInt32 inNumReservedAttrs, UInt32 inFlags): fNextAvailableID(inNumReservedAttrs), fNumValidAttrs(inNumReservedAttrs),fAttrArraySize(inNumReservedAttrs), fFlags(inFlags){ if (fAttrArraySize < kMinArraySize) fAttrArraySize = kMinArraySize; fAttrArray = NEW QTSSAttrInfoDict*[fAttrArraySize]; ::memset(fAttrArray, 0, sizeof(QTSSAttrInfoDict*) * fAttrArraySize);}QTSS_Error QTSSDictionaryMap::AddAttribute( const char* inAttrName, QTSS_AttrFunctionPtr inFuncPtr, QTSS_AttrDataType inDataType, QTSS_AttrPermission inPermission){ if (inAttrName == NULL || ::strlen(inAttrName) > QTSS_MAX_ATTRIBUTE_NAME_SIZE) return QTSS_BadArgument; for (UInt32 count = 0; count < fNextAvailableID; count++) { if (::strcmp(&fAttrArray[count]->fAttrInfo.fAttrName[0], inAttrName) == 0) { // found the name in the dictionary if (fAttrArray[count]->fAttrInfo.fAttrPermission & qtssPrivateAttrModeRemoved ) { // it is a previously removed attribute if (fAttrArray[count]->fAttrInfo.fAttrDataType == inDataType) { //same type so reuse the attribute QTSS_AttributeID attrID = fAttrArray[count]->fID; this->UnRemoveAttribute(attrID); fAttrArray[count]->fAttrInfo.fFuncPtr = inFuncPtr; // reset fAttrArray[count]->fAttrInfo.fAttrPermission = inPermission;// reset return QTSS_NoErr; // nothing left to do. It is re-added. } // a removed attribute with the same name but different type--so keep checking continue; } // an error, an active attribute with this name exists return QTSS_AttrNameExists; } } if (fAttrArraySize == fNextAvailableID) { // If there currently isn't an attribute array, or if the current array // is full, allocate a new array and copy all the old stuff over to the new array. UInt32 theNewArraySize = fAttrArraySize * 2; if (theNewArraySize == 0) theNewArraySize = kMinArraySize; QTSSAttrInfoDict** theNewArray = NEW QTSSAttrInfoDict*[theNewArraySize]; ::memset(theNewArray, 0, sizeof(QTSSAttrInfoDict*) * theNewArraySize); if (fAttrArray != NULL) { ::memcpy(theNewArray, fAttrArray, sizeof(QTSSAttrInfoDict*) * fAttrArraySize); delete [] fAttrArray; } fAttrArray = theNewArray; fAttrArraySize = theNewArraySize; } QTSS_AttributeID theID = fNextAvailableID; fNextAvailableID++; fNumValidAttrs++; if (fFlags & kIsInstanceMap) theID |= 0x80000000; // Set the high order bit to indicate this is an instance attr // Copy the information into the first available element // Currently, all attributes added in this fashion are always writeable this->SetAttribute(theID, inAttrName, inFuncPtr, inDataType, inPermission); return QTSS_NoErr;}void QTSSDictionaryMap::SetAttribute( QTSS_AttributeID inID, const char* inAttrName, QTSS_AttrFunctionPtr inFuncPtr, QTSS_AttrDataType inDataType, QTSS_AttrPermission inPermission ){ UInt32 theIndex = QTSSDictionaryMap::ConvertAttrIDToArrayIndex(inID); UInt32 theNameLen = ::strlen(inAttrName); Assert(theNameLen < QTSS_MAX_ATTRIBUTE_NAME_SIZE); Assert(fAttrArray[theIndex] == NULL); fAttrArray[theIndex] = NEW QTSSAttrInfoDict; //Copy the information into the first available element fAttrArray[theIndex]->fID = inID; ::strcpy(&fAttrArray[theIndex]->fAttrInfo.fAttrName[0], inAttrName); fAttrArray[theIndex]->fAttrInfo.fFuncPtr = inFuncPtr; fAttrArray[theIndex]->fAttrInfo.fAttrDataType = inDataType; fAttrArray[theIndex]->fAttrInfo.fAttrPermission = inPermission; fAttrArray[theIndex]->SetVal(qtssAttrName, &fAttrArray[theIndex]->fAttrInfo.fAttrName[0], theNameLen); fAttrArray[theIndex]->SetVal(qtssAttrID, &fAttrArray[theIndex]->fID, sizeof(fAttrArray[theIndex]->fID)); fAttrArray[theIndex]->SetVal(qtssAttrDataType, &fAttrArray[theIndex]->fAttrInfo.fAttrDataType, sizeof(fAttrArray[theIndex]->fAttrInfo.fAttrDataType)); fAttrArray[theIndex]->SetVal(qtssAttrPermissions, &fAttrArray[theIndex]->fAttrInfo.fAttrPermission, sizeof(fAttrArray[theIndex]->fAttrInfo.fAttrPermission));}QTSS_Error QTSSDictionaryMap::CheckRemovePermission(QTSS_AttributeID inAttrID){ SInt32 theIndex = this->ConvertAttrIDToArrayIndex(inAttrID); if (theIndex < 0) return QTSS_AttrDoesntExist; if (0 == (fAttrArray[theIndex]->fAttrInfo.fAttrPermission & qtssAttrModeDelete)) return QTSS_BadArgument; if (!(fFlags & kAllowRemoval)) return QTSS_BadArgument; return QTSS_NoErr;}QTSS_Error QTSSDictionaryMap::RemoveAttribute(QTSS_AttributeID inAttrID){ SInt32 theIndex = this->ConvertAttrIDToArrayIndex(inAttrID); if (theIndex < 0) return QTSS_AttrDoesntExist; Assert(fFlags & kAllowRemoval); if (!(fFlags & kAllowRemoval)) return QTSS_BadArgument; //qtss_printf("QTSSDictionaryMap::RemoveAttribute arraySize=%lu numNonRemove= %lu fAttrArray[%lu]->fAttrInfo.fAttrName=%s/n",this->GetNumAttrs(), this->GetNumNonRemovedAttrs(), theIndex,fAttrArray[theIndex]->fAttrInfo.fAttrName); // // Don't actually touch the attribute or anything. Just flag the // it as removed. fAttrArray[theIndex]->fAttrInfo.fAttrPermission |= qtssPrivateAttrModeRemoved; fNumValidAttrs--;//有效元素减1 Assert(fNumValidAttrs < 1000000); return QTSS_NoErr;}QTSS_Error QTSSDictionaryMap::UnRemoveAttribute(QTSS_AttributeID inAttrID){ if (this->ConvertAttrIDToArrayIndex(inAttrID) == -1) return QTSS_AttrDoesntExist; SInt32 theIndex = this->ConvertAttrIDToArrayIndex(inAttrID); if (theIndex < 0) return QTSS_AttrDoesntExist; fAttrArray[theIndex]->fAttrInfo.fAttrPermission &= ~qtssPrivateAttrModeRemoved; fNumValidAttrs++; return QTSS_NoErr;}QTSS_Error QTSSDictionaryMap::GetAttrInfoByName(const char* inAttrName, QTSSAttrInfoDict** outAttrInfoObject, Bool16 returnRemovedAttr){ if (outAttrInfoObject == NULL) return QTSS_BadArgument; for (UInt32 count = 0; count < fNextAvailableID; count++) { if (::strcmp(&fAttrArray[count]->fAttrInfo.fAttrName[0], inAttrName) == 0) { if ((fAttrArray[count]->fAttrInfo.fAttrPermission & qtssPrivateAttrModeRemoved) && (!returnRemovedAttr)) continue; *outAttrInfoObject = fAttrArray[count]; return QTSS_NoErr; } } return QTSS_AttrDoesntExist;}QTSS_Error QTSSDictionaryMap::GetAttrInfoByID(QTSS_AttributeID inID, QTSSAttrInfoDict** outAttrInfoObject){ if (outAttrInfoObject == NULL) return QTSS_BadArgument; SInt32 theIndex = this->ConvertAttrIDToArrayIndex(inID); if (theIndex < 0) return QTSS_AttrDoesntExist; if (fAttrArray[theIndex]->fAttrInfo.fAttrPermission & qtssPrivateAttrModeRemoved) return QTSS_AttrDoesntExist; *outAttrInfoObject = fAttrArray[theIndex]; return QTSS_NoErr;}QTSS_Error QTSSDictionaryMap::GetAttrInfoByIndex(UInt32 inIndex, QTSSAttrInfoDict** outAttrInfoObject){ if (outAttrInfoObject == NULL) return QTSS_BadArgument; if (inIndex >= this->GetNumNonRemovedAttrs()) return QTSS_AttrDoesntExist; UInt32 actualIndex = inIndex; UInt32 max = this->GetNumAttrs(); if (fFlags & kAllowRemoval) { // If this dictionary map allows attributes to be removed, then // the iteration index and array indexes won't line up exactly, so // we have to iterate over the whole map all the time actualIndex = 0; for (UInt32 x = 0; x < max; x++) { if (fAttrArray[x] && (fAttrArray[x]->fAttrInfo.fAttrPermission & qtssPrivateAttrModeRemoved) ) { continue; } if (actualIndex == inIndex) { actualIndex = x; break; } actualIndex++; } } //qtss_printf("QTSSDictionaryMap::GetAttrInfoByIndex arraySize=%lu numNonRemove= %lu fAttrArray[%lu]->fAttrInfo.fAttrName=%s/n",this->GetNumAttrs(), this->GetNumNonRemovedAttrs(), actualIndex,fAttrArray[actualIndex]->fAttrInfo.fAttrName); Assert(actualIndex < fNextAvailableID); Assert(!(fAttrArray[actualIndex]->fAttrInfo.fAttrPermission & qtssPrivateAttrModeRemoved)); *outAttrInfoObject = fAttrArray[actualIndex]; return QTSS_NoErr;}QTSS_Error QTSSDictionaryMap::GetAttrID(const char* inAttrName, QTSS_AttributeID* outID){ if (outID == NULL) return QTSS_BadArgument; QTSSAttrInfoDict* theAttrInfo = NULL; QTSS_Error theErr = this->GetAttrInfoByName(inAttrName, &theAttrInfo); if (theErr == QTSS_NoErr) *outID = theAttrInfo->fID; return theErr;}UInt32 QTSSDictionaryMap::GetMapIndex(QTSS_ObjectType inType){ if (inType < sNextDynamicMap) return inType; switch (inType) { case qtssRTPStreamObjectType: return kRTPStreamDictIndex; case qtssClientSessionObjectType: return kClientSessionDictIndex; case qtssRTSPSessionObjectType: return kRTSPSessionDictIndex; case qtssRTSPRequestObjectType: return kRTSPRequestDictIndex; case qtssRTSPHeaderObjectType: return kRTSPHeaderDictIndex; case qtssServerObjectType: return kServerDictIndex; case qtssPrefsobjectType: return kPrefsDictIndex; case qtssTextMessagesObjectType: return kTextMessagesDictIndex; case qtssFileObjectType: return kFileDictIndex; case qtssModuleObjectType: return kModuleDictIndex; case qtssModulePrefsObjectType: return kModulePrefsDictIndex; case qtssAttrInfoObjectType: return kAttrInfoDictIndex; case qtssUserProfileObjectType: return kQTSSUserProfileDictIndex; case qtssConnectedUserObjectType: return kQTSSConnectedUserDictIndex; default: return kIllegalDictionary; } return kIllegalDictionary;}QTSS_ObjectType QTSSDictionaryMap::CreateNewMap(){ if (sNextDynamicMap == kNumDictionaries + kNumDynamicDictionaryTypes) return 0; sDictionaryMaps[sNextDynamicMap] = new QTSSDictionaryMap(0); QTSS_ObjectType result = (QTSS_ObjectType)sNextDynamicMap; sNextDynamicMap++; return result;}/* * * @APPLE_LICENSE_HEADER_START@ * * 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. * * @APPLE_LICENSE_HEADER_END@ * *//* File: QTSServerInterface.h Contains: This object defines an interface for getting and setting server-wide attributes, and storing global server resources. There can be only one of these objects per process, so there is a static accessor. */#ifndef __QTSSERVERINTERFACE_H__#define __QTSSERVERINTERFACE_H__#include "QTSS.h"#include "QTSSDictionary.h"#include "QTSServerPrefs.h"#include "QTSSMessages.h"#include "QTSSModule.h"#include "atomic.h"#include "OSMutex.h"#include "Task.h"#include "TCPListenerSocket.h"#include "ResizeableStringFormatter.h"// OSRefTable;class UDPSocketPool;class QTSServerPrefs;class QTSSMessages;//class RTPStatsUpdaterTask;class RTPSessionInterface;// This object also functions as our assert loggerclass QTSSErrorLogStream : public QTSSStream, public AssertLogger{ public: // This QTSSStream is used by modules to write to the error log QTSSErrorLogStream() {} virtual ~QTSSErrorLogStream() {} virtual QTSS_Error Write(void* inBuffer, UInt32 inLen, UInt32* outLenWritten, UInt32 inFlags); virtual void LogAssert(char* inMessage);};class QTSServerInterface : public QTSSDictionary{ public: //Initialize must be called right off the bat to initialize dictionary resources static void Initialize(); // // CONSTRUCTOR / DESTRUCTOR QTSServerInterface(); virtual ~QTSServerInterface() {} // // // STATISTICS MANipULATION // These functions are how the server keeps its statistics current void AlterCurrentRTSPSessionCount(SInt32 inDifference) { OSMutexLocker locker(&fMutex); fNumRTSPSessions += inDifference; } void AlterCurrentRTSPHTTPSessionCount(SInt32 inDifference) { OSMutexLocker locker(&fMutex); fNumRTSPHTTPSessions += inDifference; } void SwapFromRTSPToHTTP() { OSMutexLocker locker(&fMutex); fNumRTSPSessions--; fNumRTSPHTTPSessions++; } //total rtp bytes sent by the server void IncrementTotalRTPBytes(UInt32 bytes) { (void)atomic_add(&fPeriodicRTPBytes, bytes); } //total rtp packets sent by the server void IncrementTotalPackets() { (void)atomic_add(&fPeriodicRTPPackets, 1); } //total rtp bytes reported as lost by the clients void IncrementTotalRTPPacketsLost(UInt32 packets) { (void)atomic_add(&fPeriodicRTPPacketsLost, packets); } // Also increments current RTP session count void IncrementTotalRTPSessions() { OSMutexLocker locker(&fMutex); fNumRTPSessions++; fTotalRTPSessions++; } void AlterCurrentRTPSessionCount(SInt32 inDifference) { OSMutexLocker locker(&fMutex); fNumRTPSessions += inDifference; } //track how many sessions are playing void AlterRTPPlayingSessions(SInt32 inDifference) { OSMutexLocker locker(&fMutex); fNumRTPPlayingSessions += inDifference; } void IncrementTotalLate(SInt64 milliseconds) { OSMutexLocker locker(&fMutex); fTotalLate += milliseconds; if (milliseconds > fCurrentMaxLate) fCurrentMaxLate = milliseconds; if (milliseconds > fMaxLate) fMaxLate = milliseconds; } void IncrementTotalQuality(SInt32 level) { OSMutexLocker locker(&fMutex); fTotalQuality += level; } void IncrementNumThinned(SInt32 inDifference) { OSMutexLocker locker(&fMutex); fNumThinned += inDifference; } void ClearTotalLate() { OSMutexLocker locker(&fMutex); fTotalLate = 0; } void ClearCurrentMaxLate() { OSMutexLocker locker(&fMutex); fCurrentMaxLate = 0; } void ClearTotalQuality() { OSMutexLocker locker(&fMutex); fTotalQuality = 0; } // // ACCESSORS QTSS_ServerState GetServerState() { return fServerState; } UInt32 GetNumRTPSessions() { return fNumRTPSessions; } UInt32 GetNumRTSPSessions() { return fNumRTSPSessions; } UInt32 GetNumRTSPHTTPSessions(){ return fNumRTSPHTTPSessions; } UInt32 GetTotalRTPSessions() { return fTotalRTPSessions; } UInt32 GetNumRTPPlayingSessions() { return fNumRTPPlayingSessions; } UInt32 GetCurBandwidthInBits() { return fCurrentRTPBandwidthInBits; } UInt32 GetAvgBandwidthInBits() { return fAvgRTPBandwidthInBits; } UInt32 GetRTPPacketsPerSec() { return fRTPPacketsPerSecond; } UInt64 GetTotalRTPBytes() { return fTotalRTPBytes; } UInt64 GetTotalRTPPacketsLost(){ return fTotalRTPPacketsLost; } UInt64 GetTotalRTPPackets() { return fTotalRTPPackets; } Float32 GetCPUPercent() { return fCPUPercent; } Bool16 SigIntSet() { return fSigInt; } Bool16 SigTermSet() { return fSigTerm; } UInt32 GetNumMP3Sessions() { return fNumMP3Sessions; } UInt32 GetTotalMP3Sessions() { return fTotalMP3Sessions; } UInt64 GetTotalMP3Bytes() { return fTotalMP3Bytes; } UInt32 GetDebugLevel() { return fDebugLevel; } UInt32 GetDebugOptions() { return fDebugOptions; } void SetDebugLevel(UInt32 debugLevel) { fDebugLevel = debugLevel; } void SetDebugOptions(UInt32 debugOptions){ fDebugOptions = debugOptions; } SInt64 GetMaxLate() { return fMaxLate; }; SInt64 GetTotalLate() { return fTotalLate; }; SInt64 GetCurrentMaxLate() { return fCurrentMaxLate; }; SInt64 GetTotalQuality() { return fTotalQuality; }; SInt32 GetNumThinned() { return fNumThinned; }; // // // GLOBAL OBJECTS REPOSITORY // This object is in fact global, so there is an accessor for it as well. static QTSServerInterface* GetServer() { return sServer; } //Allows you to map RTP session IDs (strings) to actual RTP session objects OSRefTable* GetRTPSessionMap() { return fRTPMap; } //Server provides a statically created & bound UDPSocket / Demuxer pair //for each IP address setup to serve RTP. You access those pairs through //this function. This returns a pair pre-bound to the IPAddr specified. UDPSocketPool* GetSocketPool() { return fSocketPool; } QTSServerPrefs* GetPrefs() { return fSrvrPrefs; } QTSSMessages* GetMessages() { return fSrvrMessages; } // // // SERVER NAME & VERSION static StrPtrLen& GetServerName() { return sServerNameStr; } static StrPtrLen& GetServerVersion() { return sServerVersionStr; } static StrPtrLen& GetServerPlatform() { return sServerPlatformStr; } static StrPtrLen& GetServerBuildDate() { return sServerBuildDateStr; } static StrPtrLen& GetServerHeader() { return sServerHeaderPtr; } static StrPtrLen& GetServerBuild() { return sServerBuildStr; } static StrPtrLen& GetServerComment() { return sServerCommentStr; } // // PUBLIC HEADER static StrPtrLen* GetPublicHeader() { return &sPublicHeaderStr; } // // KILL ALL void KillAllRTPSessions(); // // SIGINT - to interrupt the server, set this flag and the server will shut down void SetSigInt() { fSigInt = true; } // SIGTERM - to kill the server, set this flag and the server will shut down void SetSigTerm() { fSigTerm = true; } // // MODULE STORAGE // All module objects are stored here, and are accessable through // these routines. // Returns the number of modules that act in a given role static UInt32 GetNumModulesInRole(QTSSModule::RoleIndex inRole) { Assert(inRole < QTSSModule::kNumRoles); return sNumModulesInRole[inRole]; } // Allows the caller to iterate over all modules that act in a given role static QTSSModule* GetModule(QTSSModule::RoleIndex inRole, UInt32 inIndex) { Assert(inRole < QTSSModule::kNumRoles); Assert(inIndex < sNumModulesInRole[inRole]); return sModuleArray[inRole][inIndex]; } // // We need to override this. This is how we implement the QTSS_StateChange_Role virtual void SetValueComplete(UInt32 inAttrIndex, QTSSDictionaryMap* inMap, UInt32 inValueIndex, void* inNewValue, UInt32 inNewValueLen); // // ERROR LOGGING // Invokes the error logging modules with some data static void LogError(QTSS_ErrorVerbosity inVerbosity, char* inBuffer); // Returns the error log stream static QTSSErrorLogStream* GetErrorLogStream() { return &sErrorLogStream; } // // LOCKING DOWN THE SERVER OBJECT OSMutex* GetServerObjectMutex() { return &fMutex; } protected: // Setup by the derived RTSPServer object //Sockets are allocated global to the server, and arbitrated through this pool here. //RTCP data is processed completely within the following task. UDPSocketPool* fSocketPool; // All RTP sessions are put into this map OSRefTable* fRTPMap; QTSServerPrefs* fSrvrPrefs; QTSSMessages* fSrvrMessages; QTSServerPrefs* fStubSrvrPrefs; QTSSMessages* fStubSrvrMessages; QTSS_ServerState fServerState; UInt32 fDefaultIPAddr; // Array of pointers to TCPListenerSockets. TCPListenerSocket** fListeners; UInt32 fNumListeners; // Number of elements in the array // startup time SInt64 fStartupTime_UnixMilli; SInt32 fGMTOffset; static ResizeableStringFormatter sPublicHeaderFormatter; static StrPtrLen sPublicHeaderStr; // // MODULE DATA static QTSSModule** sModuleArray[QTSSModule::kNumRoles]; static UInt32 sNumModulesInRole[QTSSModule::kNumRoles]; static OSQueue sModuleQueue; static QTSSErrorLogStream sErrorLogStream; private: enum { kMaxServerHeaderLen = 1000 }; static void* TimeConnected(QTSSDictionary* inConnection, UInt32* outLen); static UInt32 sServerAPIVersion; static StrPtrLen sServerNameStr; static StrPtrLen sServerVersionStr; static StrPtrLen sServerBuildStr; static StrPtrLen sServerCommentStr; static StrPtrLen sServerPlatformStr; static StrPtrLen sServerBuildDateStr; static char sServerHeader[kMaxServerHeaderLen]; static StrPtrLen sServerHeaderPtr; OSMutex fMutex; UInt32 fNumRTSPSessions; UInt32 fNumRTSPHTTPSessions; UInt32 fNumRTPSessions; //stores the current number of playing connections. UInt32 fNumRTPPlayingSessions; //stores the total number of connections since startup. UInt32 fTotalRTPSessions; //stores the total number of bytes served since startup UInt64 fTotalRTPBytes; //total number of rtp packets sent since startup UInt64 fTotalRTPPackets; //stores the total number of bytes lost (as reported by clients) since startup UInt64 fTotalRTPPacketsLost; //because there is no 64 bit atomic add (for obvious reasons), we efficiently //implement total byte counting by atomic adding to this variable, then every //once in awhile updating the sTotalBytes. unsigned int fPeriodicRTPBytes; unsigned int fPeriodicRTPPacketsLost; unsigned int fPeriodicRTPPackets; //stores the current served bandwidth in BITS per second UInt32 fCurrentRTPBandwidthInBits; UInt32 fAvgRTPBandwidthInBits; UInt32 fRTPPacketsPerSecond; Float32 fCPUPercent; Float32 fCPUTimeUsedInSec; // stores # of UDP sockets in the server currently (gets updated lazily via. // param retrieval function) UInt32 fTotalUDPSockets; // are we out of descriptors? Bool16 fIsOutOfDescriptors; // Storage for current time attribute SInt64 fCurrentTime_UnixMilli; // Stats for UDP retransmits UInt32 fUDPWastageInBytes; UInt32 fNumUDPBuffers; // MP3 Client Session params UInt32 fNumMP3Sessions; UInt32 fTotalMP3Sessions; UInt32 fCurrentMP3BandwidthInBits; UInt64 fTotalMP3Bytes; UInt32 fAvgMP3BandwidthInBits; Bool16 fSigInt; Bool16 fSigTerm; UInt32 fDebugLevel; UInt32 fDebugOptions; SInt64 fMaxLate; SInt64 fTotalLate; SInt64 fCurrentMaxLate; SInt64 fTotalQuality; SInt32 fNumThinned; // Param retrieval functions static void* CurrentUnixTimeMilli(QTSSDictionary* inServer, UInt32* outLen); static void* GetTotalUDPSockets(QTSSDictionary* inServer, UInt32* outLen); static void* IsOutOfDescriptors(QTSSDictionary* inServer, UInt32* outLen); static void* GetNumUDPBuffers(QTSSDictionary* inServer, UInt32* outLen); static void* GetNumWastedBytes(QTSSDictionary* inServer, UInt32* outLen); static QTSServerInterface* sServer; static QTSSAttrInfoDict::AttrInfo sAttributes[]; static QTSSAttrInfoDict::AttrInfo sConnectedUserAttributes[]; friend class RTPStatsUpdaterTask; friend class SessionTimeoutTask;};class RTPStatsUpdaterTask : public Task{ public: // This class runs periodically to compute current totals & averages RTPStatsUpdaterTask(); virtual ~RTPStatsUpdaterTask() {} private: virtual SInt64 Run(); RTPSessionInterface* GetNewestSession(OSRefTable* inRTPSessionMap); Float32 GetCPUTimeInSeconds(); SInt64 fLastBandwidthTime; SInt64 fLastBandwidthAvg; SInt64 fLastBytesSent; SInt64 fLastTotalMP3Bytes;};#endif // __QTSSERVERINTERFACE_H__/* * * @APPLE_LICENSE_HEADER_START@ * * 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. * * @APPLE_LICENSE_HEADER_END@ * *//* File: QTSServerInterface.cpp Contains: Implementation of object defined in QTSServerInterface.h. *///INCLUDES:#ifndef kVersionString#include "revision.h"#endif#include "QTSServerInterface.h"#include "RTPSessionInterface.h"#include "OSRef.h"#include "UDPSocketPool.h"#include "RTSPProtocol.h"#include "RTPPacketResender.h"#ifndef __MacOSX__#include "revision.h"#endif// STATIC DATAUInt32 QTSServerInterface::sServerAPIVersion = QTSS_API_VERSION;QTSServerInterface* QTSServerInterface::sServer = NULL;#if __MacOSX__StrPtrLen QTSServerInterface::sServerNameStr("QTSS");#elseStrPtrLen QTSServerInterface::sServerNameStr("DSS");#endif// kVersionString from revision.h, include with -i at project levelStrPtrLen QTSServerInterface::sServerVersionStr(kVersionString);StrPtrLen QTSServerInterface::sServerBuildStr(kBuildString);StrPtrLen QTSServerInterface::sServerCommentStr(kCommentString);StrPtrLen QTSServerInterface::sServerPlatformStr(kPlatformNameString);StrPtrLen QTSServerInterface::sServerBuildDateStr(__DATE__ ", "__TIME__);char QTSServerInterface::sServerHeader[kMaxServerHeaderLen];StrPtrLen QTSServerInterface::sServerHeaderPtr(sServerHeader, kMaxServerHeaderLen);ResizeableStringFormatter QTSServerInterface::sPublicHeaderFormatter(NULL, 0);StrPtrLen QTSServerInterface::sPublicHeaderStr;QTSSModule** QTSServerInterface::sModuleArray[QTSSModule::kNumRoles];UInt32 QTSServerInterface::sNumModulesInRole[QTSSModule::kNumRoles];OSQueue QTSServerInterface::sModuleQueue;QTSSErrorLogStream QTSServerInterface::sErrorLogStream;QTSSAttrInfoDict::AttrInfo QTSServerInterface::sConnectedUserAttributes[] = { /*fields: fAttrName, fFuncPtr, fAttrDataType, fAttrPermission */ /* 0 */ { "qtssConnectionType", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 1 */ { "qtssConnectionCreateTimeInMsec", NULL, qtssAttrDataTypeTimeVal, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 2 */ { "qtssConnectionTimeConnectedInMsec", TimeConnected, qtssAttrDataTypeTimeVal, qtssAttrModeRead | qtssAttrModePreempSafe }, /* 3 */ { "qtssConnectionBytesSent", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 4 */ { "qtssConnectionMountPoint", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 5 */ { "qtssConnectionHostName", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe } , /* 6 */ { "qtssConnectionSessRemoteAddrStr", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 7 */ { "qtssConnectionSessLocalAddrStr", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 8 */ { "qtssConnectionCurrentBitRate", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 9 */ { "qtssConnectionPacketLossPercent", NULL, qtssAttrDataTypeFloat32, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, // this last parameter is a workaround for the current dictionary implementation. For qtssConnectionTimeConnectedInMsec above we have a param // retrieval function. This needs storage to keep the value returned, but if it sets its own param then the function no longer gets called. /* 10 */ { "qtssConnectionTimeStorage", NULL, qtssAttrDataTypeTimeVal, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe },};QTSSAttrInfoDict::AttrInfo QTSServerInterface::sAttributes[] = { /*fields: fAttrName, fFuncPtr, fAttrDataType, fAttrPermission */ /* 0 */ { "qtssServerAPIVersion", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModePreempSafe }, /* 1 */ { "qtssSvrDefaultDNSName", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead }, /* 2 */ { "qtssSvrDefaultIPAddr", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead }, /* 3 */ { "qtssSvrServerName", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe }, /* 4 */ { "qtssRTSPSvrServerVersion", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe }, /* 5 */ { "qtssRTSPSvrServerBuildDate", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe }, /* 6 */ { "qtssSvrRTSPPorts", NULL, qtssAttrDataTypeUInt16, qtssAttrModeRead }, /* 7 */ { "qtssSvrRTSPServerHeader", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe }, /* 8 */ { "qtssSvrState", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModeWrite }, /* 9 */ { "qtssSvrIsOutOfDescriptors", IsOutOfDescriptors, qtssAttrDataTypeBool16, qtssAttrModeRead }, /* 10 */ { "qtssRTSPCurrentSessionCount", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead }, /* 11 */ { "qtssRTSPHTTPCurrentSessionCount",NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead }, /* 12 */ { "qtssRTPSvrNumUDPSockets", GetTotalUDPSockets, qtssAttrDataTypeUInt32, qtssAttrModeRead }, /* 13 */ { "qtssRTPSvrCurConn", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead }, /* 14 */ { "qtssRTPSvrTotalConn", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead }, /* 15 */ { "qtssRTPSvrCurBandwidth", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead }, /* 16 */ { "qtssRTPSvrTotalBytes", NULL, qtssAttrDataTypeUInt64, qtssAttrModeRead }, /* 17 */ { "qtssRTPSvrAvgBandwidth", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead }, /* 18 */ { "qtssRTPSvrCurPackets", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead }, /* 19 */ { "qtssRTPSvrTotalPackets", NULL, qtssAttrDataTypeUInt64, qtssAttrModeRead }, /* 20 */ { "qtssSvrHandledMethods", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 21 */ { "qtssSvrModuleObjects", NULL, qtssAttrDataTypeQTSS_Object,qtssAttrModeRead | qtssAttrModePreempSafe }, /* 22 */ { "qtssSvrStartupTime", NULL, qtssAttrDataTypeTimeVal, qtssAttrModeRead }, /* 23 */ { "qtssSvrGMTOffsetInHrs", NULL, qtssAttrDataTypeSInt32, qtssAttrModeRead }, /* 24 */ { "qtssSvrDefaultIPAddrStr", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead }, /* 25 */ { "qtssSvrPreferences", NULL, qtssAttrDataTypeQTSS_Object,qtssAttrModeRead | qtssAttrModeInstanceAttrAllowed}, /* 26 */ { "qtssSvrMessages", NULL, qtssAttrDataTypeQTSS_Object,qtssAttrModeRead }, /* 27 */ { "qtssSvrClientSessions", NULL, qtssAttrDataTypeQTSS_Object,qtssAttrModeRead }, /* 28 */ { "qtssSvrCurrentTimeMilliseconds",CurrentUnixTimeMilli, qtssAttrDataTypeTimeVal,qtssAttrModeRead}, /* 29 */ { "qtssSvrCPULoadPercent", NULL, qtssAttrDataTypeFloat32, qtssAttrModeRead}, /* 30 */ { "qtssSvrNumReliableUDPBuffers", GetNumUDPBuffers, qtssAttrDataTypeUInt32, qtssAttrModeRead }, /* 31 */ { "qtssSvrReliableUDPWastageInBytes",GetNumWastedBytes, qtssAttrDataTypeUInt32, qtssAttrModeRead }, /* 32 */ { "qtssSvrConnectedUsers", NULL, qtssAttrDataTypeQTSS_Object, qtssAttrModeRead | qtssAttrModeWrite }, /* 33 */ { "qtssMP3SvrCurConn", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 34 */ { "qtssMP3SvrTotalConn", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 35 */ { "qtssMP3SvrCurBandwidth", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 36 */ { "qtssMP3SvrTotalBytes", NULL, qtssAttrDataTypeUInt64, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 37 */ { "qtssMP3SvrAvgBandwidth", NULL, qtssAttrDataTypeUInt32, qtssAttrModeRead | qtssAttrModeWrite | qtssAttrModePreempSafe }, /* 38 */ { "qtssSvrServerBuild", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe }, /* 39 */ { "qtssSvrServerPlatform", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe }, /* 40 */ { "qtssSvrRTSPServerComment", NULL, qtssAttrDataTypeCharArray, qtssAttrModeRead | qtssAttrModePreempSafe }, /* 41 */ { "qtssSvrNumThinned", NULL, qtssAttrDataTypeSInt32, qtssAttrModeRead | qtssAttrModeWrite }};void QTSServerInterface::Initialize(){ for (UInt32 x = 0; x < qtssSvrNumParams; x++) QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kServerDictIndex)-> SetAttribute(x, sAttributes[x].fAttrName, sAttributes[x].fFuncPtr, sAttributes[x].fAttrDataType, sAttributes[x].fAttrPermission); for (UInt32 y = 0; y < qtssConnectionNumParams; y++) QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kQTSSConnectedUserDictIndex)-> SetAttribute(y, sConnectedUserAttributes[y].fAttrName, sConnectedUserAttributes[y].fFuncPtr, sConnectedUserAttributes[y].fAttrDataType, sConnectedUserAttributes[y].fAttrPermission); //Write out a premade server header StringFormatter serverFormatter(sServerHeaderPtr.Ptr, kMaxServerHeaderLen); serverFormatter.Put(RTSPProtocol::GetHeaderString(qtssServerHeader)); serverFormatter.Put(": "); serverFormatter.Put(sServerNameStr); serverFormatter.PutChar('/'); serverFormatter.Put(sServerVersionStr); serverFormatter.PutChar(' '); serverFormatter.PutChar('('); serverFormatter.Put("Build/"); serverFormatter.Put(sServerBuildStr); serverFormatter.Put("; "); serverFormatter.Put("Platform/"); serverFormatter.Put(sServerPlatformStr); serverFormatter.PutChar(';'); if (sServerCommentStr.Len > 0) { serverFormatter.PutChar(' '); serverFormatter.Put(sServerCommentStr); } serverFormatter.PutChar(')'); sServerHeaderPtr.Len = serverFormatter.GetCurrentOffset(); Assert(sServerHeaderPtr.Len < kMaxServerHeaderLen);}QTSServerInterface::QTSServerInterface() : QTSSDictionary(QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kServerDictIndex), &fMutex), fSocketPool(NULL), fRTPMap(NULL), fSrvrPrefs(NULL), fSrvrMessages(NULL), fServerState(qtssStartingUpState), fDefaultIPAddr(0), fListeners(NULL), fNumListeners(0), fStartupTime_UnixMilli(0), fGMTOffset(0), fNumRTSPSessions(0), fNumRTSPHTTPSessions(0), fNumRTPSessions(0), fNumRTPPlayingSessions(0), fTotalRTPSessions(0), fTotalRTPBytes(0), fTotalRTPPackets(0), fTotalRTPPacketsLost(0), fPeriodicRTPBytes(0), fPeriodicRTPPacketsLost(0), fPeriodicRTPPackets(0), fCurrentRTPBandwidthInBits(0), fAvgRTPBandwidthInBits(0), fRTPPacketsPerSecond(0), fCPUPercent(0), fCPUTimeUsedInSec(0), fUDPWastageInBytes(0), fNumUDPBuffers(0), fNumMP3Sessions(0), fTotalMP3Sessions(0), fCurrentMP3BandwidthInBits(0), fTotalMP3Bytes(0), fAvgMP3BandwidthInBits(0), fSigInt(false), fSigTerm(false), fDebugLevel(0), fDebugOptions(0), fMaxLate(0), fTotalLate(0), fCurrentMaxLate(0), fTotalQuality(0), fNumThinned(0){ for (UInt32 y = 0; y < QTSSModule::kNumRoles; y++) { sModuleArray[y] = NULL; sNumModulesInRole[y] = 0; } this->SetVal(qtssSvrState, &fServerState, sizeof(fServerState)); this->SetVal(qtssServerAPIVersion, &sServerAPIVersion, sizeof(sServerAPIVersion)); this->SetVal(qtssSvrDefaultIPAddr, &fDefaultIPAddr, sizeof(fDefaultIPAddr)); this->SetVal(qtssSvrServerName, sServerNameStr.Ptr, sServerNameStr.Len); this->SetVal(qtssSvrServerVersion, sServerVersionStr.Ptr, sServerVersionStr.Len); this->SetVal(qtssSvrServerBuildDate, sServerBuildDateStr.Ptr, sServerBuildDateStr.Len); this->SetVal(qtssSvrRTSPServerHeader, sServerHeaderPtr.Ptr, sServerHeaderPtr.Len); this->SetVal(qtssRTSPCurrentSessionCount, &fNumRTSPSessions, sizeof(fNumRTSPSessions)); this->SetVal(qtssRTSPHTTPCurrentSessionCount, &fNumRTSPHTTPSessions,sizeof(fNumRTSPHTTPSessions)); this->SetVal(qtssRTPSvrCurConn, &fNumRTPSessions, sizeof(fNumRTPSessions)); this->SetVal(qtssRTPSvrTotalConn, &fTotalRTPSessions, sizeof(fTotalRTPSessions)); this->SetVal(qtssRTPSvrCurBandwidth, &fCurrentRTPBandwidthInBits,sizeof(fCurrentRTPBandwidthInBits)); this->SetVal(qtssRTPSvrTotalBytes, &fTotalRTPBytes, sizeof(fTotalRTPBytes)); this->SetVal(qtssRTPSvrAvgBandwidth, &fAvgRTPBandwidthInBits, sizeof(fAvgRTPBandwidthInBits)); this->SetVal(qtssRTPSvrCurPackets, &fRTPPacketsPerSecond, sizeof(fRTPPacketsPerSecond)); this->SetVal(qtssRTPSvrTotalPackets, &fTotalRTPPackets, sizeof(fTotalRTPPackets)); this->SetVal(qtssSvrStartupTime, &fStartupTime_UnixMilli, sizeof(fStartupTime_UnixMilli)); this->SetVal(qtssSvrGMTOffsetInHrs, &fGMTOffset, sizeof(fGMTOffset)); this->SetVal(qtssSvrCPULoadPercent, &fCPUPercent, sizeof(fCPUPercent)); this->SetVal(qtssMP3SvrCurConn, &fNumMP3Sessions, sizeof(fNumMP3Sessions)); this->SetVal(qtssMP3SvrTotalConn, &fTotalMP3Sessions, sizeof(fTotalMP3Sessions)); this->SetVal(qtssMP3SvrCurBandwidth, &fCurrentMP3BandwidthInBits,sizeof(fCurrentMP3BandwidthInBits)); this->SetVal(qtssMP3SvrTotalBytes, &fTotalMP3Bytes, sizeof(fTotalMP3Bytes)); this->SetVal(qtssMP3SvrAvgBandwidth, &fAvgMP3BandwidthInBits, sizeof(fAvgMP3BandwidthInBits)); this->SetVal(qtssSvrServerBuild, sServerBuildStr.Ptr, sServerBuildStr.Len); this->SetVal(qtssSvrRTSPServerComment, sServerCommentStr.Ptr, sServerCommentStr.Len); this->SetVal(qtssSvrServerPlatform, sServerPlatformStr.Ptr, sServerPlatformStr.Len); this->SetVal(qtssSvrNumThinned, &fNumThinned, sizeof(fNumThinned)); sServer = this;}void QTSServerInterface::LogError(QTSS_ErrorVerbosity inVerbosity, char* inBuffer){ QTSS_RoleParams theParams; theParams.errorParams.inVerbosity = inVerbosity; theParams.errorParams.inBuffer = inBuffer; for (UInt32 x = 0; x < QTSServerInterface::GetNumModulesInRole(QTSSModule::kErrorLogRole); x++) (void)QTSServerInterface::GetModule(QTSSModule::kErrorLogRole, x)->CallDispatch(QTSS_ErrorLog_Role, &theParams); // If this is a fatal error, set the proper attribute in the RTSPServer dictionary if ((inVerbosity == qtssFatalVerbosity) && (sServer != NULL)) { QTSS_ServerState theState = qtssFatalErrorState; (void)sServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState)); }}void QTSServerInterface::KillAllRTPSessions(){ OSMutexLocker locker(fRTPMap->GetMutex()); for (OSRefHashTableIter theIter(fRTPMap->GetHashTable()); !theIter.IsDone(); theIter.Next()) { OSRef* theRef = theIter.GetCurrent(); RTPSessionInterface* theSession = (RTPSessionInterface*)theRef->GetObject(); theSession->Signal(Task::kKillEvent); } }void QTSServerInterface::SetValueComplete(UInt32 inAttrIndex, QTSSDictionaryMap* inMap, UInt32 inValueIndex, void* inNewValue, UInt32 inNewValueLen){ if (inAttrIndex == qtssSvrState) { Assert(inNewValueLen == sizeof(QTSS_ServerState)); // // Invoke the server state change role QTSS_RoleParams theParams; theParams.stateChangeParams.inNewState = *(QTSS_ServerState*)inNewValue; static QTSS_ModuleState sStateChangeState = { NULL, 0, NULL, false }; if (OSThread::GetCurrent() == NULL) OSThread::SetMainThreadData(&sStateChangeState); else OSThread::GetCurrent()->SetThreadData(&sStateChangeState); UInt32 numModules = QTSServerInterface::GetNumModulesInRole(QTSSModule::kStateChangeRole); { for (UInt32 theCurrentModule = 0; theCurrentModule < numModules; theCurrentModule++) { QTSSModule* theModule = QTSServerInterface::GetModule(QTSSModule::kStateChangeRole, theCurrentModule); (void)theModule->CallDispatch(QTSS_StateChange_Role, &theParams); } } // // Make sure to clear out the thread data if (OSThread::GetCurrent() == NULL) OSThread::SetMainThreadData(NULL); else OSThread::GetCurrent()->SetThreadData(NULL); }}RTPStatsUpdaterTask::RTPStatsUpdaterTask(): Task(), fLastBandwidthTime(0), fLastBandwidthAvg(0), fLastBytesSent(0), fLastTotalMP3Bytes(0){ this->SetTaskName("RTPStatsUpdaterTask"); this->Signal(Task::kStartEvent);}Float32 RTPStatsUpdaterTask::GetCPUTimeInSeconds(){ // This function returns the total number of seconds that the // process running RTPStatsUpdaterTask() has been executing as // a user process. Float32 cpuTimeInSec = 0.0;#ifdef __Win32__ // The Win32 way of getting the time for this process HANDLE hProcess = GetCurrentProcess(); SInt64 createTime, exitTime, kernelTime, userTime; if(GetProcessTimes(hProcess, (LPFILETIME)&createTime, (LPFILETIME)&exitTime, (LPFILETIME)&kernelTime, (LPFILETIME)&userTime)) { // userTime is in 10**-7 seconds since Jan.1, 1607. // (What type of computers did they use in 1607?) cpuTimeInSec = (Float32) (userTime/10000000.0); } else { // This should never happen!!! Assert(0); cpuTimeInSec = 0.0; }#else // The UNIX way of getting the time for this process clock_t cpuTime = clock(); cpuTimeInSec = (Float32) cpuTime / CLOCKS_PER_SEC;#endif return cpuTimeInSec;}SInt64 RTPStatsUpdaterTask::Run(){ QTSServerInterface* theServer = QTSServerInterface::sServer; // All of this must happen atomically wrt dictionary values we are manipulating OSMutexLocker locker(&theServer->fMutex); //First update total bytes. This must be done because total bytes is a 64 bit number, //so no atomic functions can apply. // // NOTE: The line below is not thread safe on non-PowerPC platforms. This is // because the fPeriodicRTPBytes variable is being manipulated from within an // atomic_add. On PowerPC, assignments are atomic, so the assignment below is ok. // On a non-PowerPC platform, the following would be thread safe: //unsigned int periodicBytes = atomic_add(&theServer->fPeriodicRTPBytes, 0); unsigned int periodicBytes = theServer->fPeriodicRTPBytes; (void)atomic_sub(&theServer->fPeriodicRTPBytes, periodicBytes); theServer->fTotalRTPBytes += periodicBytes; // Same deal for packet totals unsigned int periodicPackets = theServer->fPeriodicRTPPackets; (void)atomic_sub(&theServer->fPeriodicRTPPackets, periodicPackets); theServer->fTotalRTPPackets += periodicPackets; // ..and for lost packet totals unsigned int periodicPacketsLost = theServer->fPeriodicRTPPacketsLost; (void)atomic_sub(&theServer->fPeriodicRTPPacketsLost, periodicPacketsLost); theServer->fTotalRTPPacketsLost += periodicPacketsLost; SInt64 curTime = OS::Milliseconds(); //for cpu percent Float32 cpuTimeInSec = GetCPUTimeInSeconds(); //also update current bandwidth statistic if (fLastBandwidthTime != 0) { Assert(curTime > fLastBandwidthTime); UInt32 delta = (UInt32)(curTime - fLastBandwidthTime); // Prevent divide by zero errror if (delta < 1000) { WarnV(delta >= 1000, "delta < 1000"); (void)this->GetEvents();//we must clear the event mask! return theServer->GetPrefs()->GetTotalBytesUpdateTimeInSecs() * 1000; } UInt32 packetsPerSecond = periodicPackets; UInt32 theTime = delta / 1000; packetsPerSecond /= theTime; Assert(packetsPerSecond >= 0); theServer->fRTPPacketsPerSecond = packetsPerSecond; UInt32 additionalBytes = 28 * packetsPerSecond; // IP headers = 20 + UDP headers = 8 UInt32 headerBits = 8 * additionalBytes; headerBits /= theTime; Float32 bits = periodicBytes * 8; bits /= theTime; theServer->fCurrentRTPBandwidthInBits = (UInt32) (bits + headerBits); // okay let's do it for MP3 bytes now bits = (Float32)(((SInt64)theServer->fTotalMP3Bytes - fLastTotalMP3Bytes) * 8); bits /= theTime; theServer->fCurrentMP3BandwidthInBits = (UInt32)bits; //do the computation for cpu percent Float32 diffTime = cpuTimeInSec - theServer->fCPUTimeUsedInSec; theServer->fCPUPercent = (diffTime/theTime) * 100; UInt32 numProcessors = OS::GetNumProcessors(); if (numProcessors > 1) theServer->fCPUPercent /= numProcessors; } fLastTotalMP3Bytes = (SInt64)theServer->fTotalMP3Bytes; fLastBandwidthTime = curTime; // We use a running average for avg. bandwidth calculations theServer->fAvgMP3BandwidthInBits = (theServer->fAvgMP3BandwidthInBits + theServer->fCurrentMP3BandwidthInBits)/2; //for cpu percent theServer->fCPUTimeUsedInSec = cpuTimeInSec; //also compute average bandwidth, a much more smooth value. This is done with //the fLastBandwidthAvg, a timestamp of the last time we did an average, and //fLastBytesSent, the number of bytes sent when we last did an average. if ((fLastBandwidthAvg != 0) && (curTime > (fLastBandwidthAvg + (theServer->GetPrefs()->GetAvgBandwidthUpdateTimeInSecs() * 1000)))) { UInt32 delta = (UInt32)(curTime - fLastBandwidthAvg); SInt64 bytesSent = theServer->fTotalRTPBytes - fLastBytesSent; Assert(bytesSent >= 0); //do the bandwidth computation using floating point divides //for accuracy and speed. Float32 bits = (Float32)(bytesSent * 8); Float32 theAvgTime = (Float32)delta; theAvgTime /= 1000; bits /= theAvgTime; Assert(bits >= 0); theServer->fAvgRTPBandwidthInBits = (UInt32)bits; fLastBandwidthAvg = curTime; fLastBytesSent = theServer->fTotalRTPBytes; //if the bandwidth is above the bandwidth setting, disconnect 1 user by sending them //a BYE RTCP packet. SInt32 maxKBits = theServer->GetPrefs()->GetMaxKBitsBandwidth(); if ((maxKBits > -1) && (theServer->fAvgRTPBandwidthInBits > ((UInt32)maxKBits * 1024))) { //we need to make sure that all of this happens atomically wrt the session map OSMutexLocker locker(theServer->GetRTPSessionMap()->GetMutex()); RTPSessionInterface* theSession = this->GetNewestSession(theServer->fRTPMap); if (theSession != NULL) if ((curTime - theSession->GetSessionCreateTime()) < theServer->GetPrefs()->GetSafePlayDurationInSecs() * 1000) theSession->Signal(Task::kKillEvent); } } else if (fLastBandwidthAvg == 0) { fLastBandwidthAvg = curTime; fLastBytesSent = theServer->fTotalRTPBytes; } (void)this->GetEvents();//we must clear the event mask! return theServer->GetPrefs()->GetTotalBytesUpdateTimeInSecs() * 1000;}RTPSessionInterface* RTPStatsUpdaterTask::GetNewestSession(OSRefTable* inRTPSessionMap){ //Caller must lock down the RTP session map SInt64 theNewestPlayTime = 0; RTPSessionInterface* theNewestSession = NULL; //use the session map to iterate through all the sessions, finding the most //recently connected client for (OSRefHashTableIter theIter(inRTPSessionMap->GetHashTable()); !theIter.IsDone(); theIter.Next()) { OSRef* theRef = theIter.GetCurrent(); RTPSessionInterface* theSession = (RTPSessionInterface*)theRef->GetObject(); Assert(theSession->GetSessionCreateTime() > 0); if (theSession->GetSessionCreateTime() > theNewestPlayTime) { theNewestPlayTime = theSession->GetSessionCreateTime(); theNewestSession = theSession; } } return theNewestSession;}void* QTSServerInterface::CurrentUnixTimeMilli(QTSSDictionary* inServer, UInt32* outLen){ QTSServerInterface* theServer = (QTSServerInterface*)inServer; theServer->fCurrentTime_UnixMilli = OS::TimeMilli_To_UnixTimeMilli(OS::Milliseconds()); // Return the result *outLen = sizeof(theServer->fCurrentTime_UnixMilli); return &theServer->fCurrentTime_UnixMilli;}void* QTSServerInterface::GetTotalUDPSockets(QTSSDictionary* inServer, UInt32* outLen){ QTSServerInterface* theServer = (QTSServerInterface*)inServer; // Multiply by 2 because this is returning the number of socket *pairs* theServer->fTotalUDPSockets = theServer->fSocketPool->GetSocketQueue()->GetLength() * 2; // Return the result *outLen = sizeof(theServer->fTotalUDPSockets); return &theServer->fTotalUDPSockets;}void* QTSServerInterface::IsOutOfDescriptors(QTSSDictionary* inServer, UInt32* outLen){ QTSServerInterface* theServer = (QTSServerInterface*)inServer; theServer->fIsOutOfDescriptors = false; for (UInt32 x = 0; x < theServer->fNumListeners; x++) { if (theServer->fListeners[x]->IsOutOfDescriptors()) { theServer->fIsOutOfDescriptors = true; break; } } // Return the result *outLen = sizeof(theServer->fIsOutOfDescriptors); return &theServer->fIsOutOfDescriptors;}void* QTSServerInterface::GetNumUDPBuffers(QTSSDictionary* inServer, UInt32* outLen){ // This param retrieval function must be invoked each time it is called, // because whether we are out of descriptors or not is continually changing QTSServerInterface* theServer = (QTSServerInterface*)inServer; theServer->fNumUDPBuffers = RTPPacketResender::GetNumRetransmitBuffers(); // Return the result *outLen = sizeof(theServer->fNumUDPBuffers); return &theServer->fNumUDPBuffers;}void* QTSServerInterface::GetNumWastedBytes(QTSSDictionary* inServer, UInt32* outLen){ // This param retrieval function must be invoked each time it is called, // because whether we are out of descriptors or not is continually changing QTSServerInterface* theServer = (QTSServerInterface*)inServer; theServer->fUDPWastageInBytes = RTPPacketResender::GetWastedBufferBytes(); // Return the result *outLen = sizeof(theServer->fUDPWastageInBytes); return &theServer->fUDPWastageInBytes; }void* QTSServerInterface::TimeConnected(QTSSDictionary* inConnection, UInt32* outLen){ SInt64 connectTime; void* result; UInt32 len = sizeof(connectTime); inConnection->GetValue(qtssConnectionCreateTimeInMsec, 0, &connectTime, &len); SInt64 timeConnected = OS::Milliseconds() - connectTime; *outLen = sizeof(timeConnected); inConnection->SetValue(qtssConnectionTimeStorage, 0, &timeConnected, sizeof(connectTime)); inConnection->GetValuePtr(qtssConnectionTimeStorage, 0, &result, outLen); // Return the result return result;}QTSS_Error QTSSErrorLogStream::Write(void* inBuffer, UInt32 inLen, UInt32* outLenWritten, UInt32 inFlags){ // For the error log stream, the flags are considered to be the verbosity // of the error. if (inFlags >= qtssIllegalVerbosity) inFlags = qtssMessageVerbosity; QTSServerInterface::LogError(inFlags, (char*)inBuffer); if (outLenWritten != NULL) *outLenWritten = inLen; return QTSS_NoErr;}void QTSSErrorLogStream::LogAssert(char* inMessage){ QTSServerInterface::LogError(qtssAssertVerbosity, inMessage);}/* * * @APPLE_LICENSE_HEADER_START@ * * 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. * * @APPLE_LICENSE_HEADER_END@ * */#ifndef QTSS_H#define QTSS_H// #ifdef __cplusplus// extern "C" {// #endif#include "OSHeaders.h"#include "QTSSRTSPProtocol.h"#ifndef __Win32__#include <sys/uio.h>#endif#define QTSS_API_VERSION 0x00040000#define QTSS_MAX_MODULE_NAME_LENGTH 64#define QTSS_MAX_SESSION_ID_LENGTH 32#define QTSS_MAX_ATTRIBUTE_NAME_SIZE 64//*******************************// ENUMERATED TYPES/**********************************/// Error Codesenum{ 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 functionenum{ 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 = 0x00000001, // have the server generate RTCP Sender Reports qtssPlayFlagsAppendServerInfo = 0x00000002 // 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_SendStandardRTSPResponseenum{ qtssPlayRespWriteTrackInfo = 0x00000001, qtssSetupRespDontWriteSSRC = 0x00000002};// Flags for the qtssRTSPReqAction attribute in a QTSS_RTSPRequestObject.enum { qtssActionFlagsNoFlags = 0x00000000, qtssActionFlagsRead = 0x00000001, qtssActionFlagsWrite = 0x00000002 };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;// Eventsenum{ QTSS_ReadableEvent = 1, QTSS_WriteableEvent = 2};typedef UInt32 QTSS_EventType;// Authentication schemesenum{ 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};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 requestenum{ 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 typesenum{ qtssUnknownPayloadType = 0, qtssVideoPayloadType = 1, qtssAudioPayloadType = 2};typedef UInt32 QTSS_RTPPayloadType;/**********************************/// QTSS API OBJECT TYPESenum{ 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};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 IDenum{ 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 issuedenum{ qtssDontCallSendPacketsAgain = -1};// DATA TYPESenum{ 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;/**********************************///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 qtssRTPStrNumParams = 39};typedef UInt32 QTSS_RTPStreamAttributes;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 qtssCliSesNumParams = 37 };typedef UInt32 QTSS_ClientSessionAttributes;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_RTSPSessionType //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 qtssRTSPSesNumParams = 14};typedef UInt32 QTSS_RTSPSessionAttributes;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 true, 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 qtssRTSPReqNumParams = 38 };typedef UInt32 QTSS_RTSPRequestAttributes;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, //r/w //SInt32 //Number of thinned sessions qtssSvrNumParams = 42};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 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 qtssPrefsNumParams = 73};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 qtssUserNumParams = 4};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;//***********************************************/// TYPEDEFStypedef 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_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 MAINQTSS_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 notedQTSS_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 objectQTSS_Error QTSS_LockObject(QTSS_Object inObject); /********************************************************************/// QTSS_UnlockObject//// Releases the mutex for this object.////// Returns: QTSS_NoErr// QTSS_BadArgument: bad objectQTSS_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_AddObjectValue.//// 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 IDQTSS_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 argumentQTSS_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_AttrDoesntExistQTSS_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_AttrDoesntExistQTSS_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_AttrDoesntExistQTSS_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_BadArgumentQTSS_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 argumentQTSS_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_BadArgumentQTSS_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_BadArgumentQTSS_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_BadArgumentQTSS_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 argumentQTSS_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 argumentQTSS_Error QTSS_SendRTSPHeaders(QTSS_RTSPRequestObject inRef);//// Returns: QTSS_NoErr// QTSS_BadArgument: Bad argumentQTSS_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 argumentQTSS_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 argumentQTSS_Error QTSS_Pause(QTSS_ClientSessionObject inClientSession);//// Returns: QTSS_NoErr// QTSS_BadArgument: Bad argumentQTSS_Error QTSS_Teardown(QTSS_ClientSessionObject inClientSession);// Returns: QTSS_NoErr// QTSS_BadArgument: Bad argumentQTSS_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_BadArgumentQTSS_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_BadArgumentQTSS_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_NoErrQTSS_Error QTSS_CreateStreamFromSocket(int inFileDesc, QTSS_SocketStream* outStream);/********************************************************************/// QTSS_DestroySocketStream//// Creates a socket stream.//// Arguments: inFileDesc: the socket//// Returns: QTSS_NoErrQTSS_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 nullQTSS_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_BadArgumentQTSS_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_AddStaticAttributeQTSS_Error QTSS_AddAttribute(QTSS_ObjectType inObjectType, const char* inAttributeName, void* inUnused);#endif// #ifdef __cplusplus// }// #endif#endif
新闻热点
疑难解答