Retired Document
Important: Apple recommends that developers explore QTKit and Core Video for new development in this technology area. See QTKit Framework Reference and Core Video Programming Guide for more information.
Tasks
This chapter describes common QTSS tasks:
Building the Streaming Server, described in
“Building the Streaming Server”
.Compiling and installed a QTSS module, described in
“Compiling a QTSS Module into the Server”
.Getting and setting attribute values, described in
“Working with Attributes”
. This section also tells you how to add your own attributes to an object.Using the server’s file module to open, read, and close files, described in
“Using Files”
. This section also tells you how to implement your own file system module.Communicating with the server with the Admin protocol, described in
“Using the Admin Protocol”
.
Building the Streaming Server
This section describes the Streaming Server build and install process for Mac OS X, POSIX, and Windows platforms.
Mac OS X
Use the Buildit
script
to build the Streaming Server for Mac OS X. Use the following command
line options: StreamingServer.pbroj -target
DSS
. As they are built, the binaries are left
in the build directory.
The command BuildOSXInstallerPkg dss
creates
a file named DarwinStreamingServer.pkg
.
POSIX
Use the Buildit
script
to build the Streaming Server on POSIX platforms. Binaries are left in
the source directories. To create the installer, use the buildtarball
script,
which creates an install directory with Install script and tar file.
Windows
Use the WindowsNTSupport/StreamingServer.dsw
script
to build the Streaming Server on Windows platforms. Batch build
all. Binaries are left in the Debug and Release directory. The WindowsNTSupport/makezip.bat
script
creates an install directory with an Install.bat file.
Building a QuickTime Streaming Server Module
You can add a QTSS module to the QuickTime Streaming Server by compiling the code directly into the server itself or by building a module as a separate code fragment that is loaded when the server starts up.
Whether compiled into the server or built as a separate module, the code for the module is the same. The only difference is the way in which the code is compiled.
Compiling a QTSS Module into the Server
If you have the source code for the QuickTime Streaming Server, you can compile your module into the server.
To compile your code into the server, locate the function QTSServer::LoadCompiledInModules
in QTSServer.cpp
and
add to it the following lines
QTSSModule* myModule = new QTSSModule("__XYZ__"); |
(void)myModule->Initialize(&sCallbacks, &__XYZMAIN__); |
(void)AddModule(myModule); |
where XYZ
is the
name of your module and XYZMAIN
is
your module’s main routine.
Some platforms require that each module use unique function names. To prevent name conflicts when you compile a module into the server, make your functions static.
Modules that are compiled into the server are known as static modules.
Building a QTSS Module as a Code Fragment
To have the server load at runtime a QTSS module that is a code fragment, follow these steps:
Compile the source for your module as a dynamic shared library for the platform you are targeting. For Mac OS X, the project type must be
loadable bundle
.Link the resulting file against the QTSS API stub library for the platforms you are targeting.
Place the resulting file in the
/Library/QuickTimeStreaming/Modules
directory (Mac OS X),/usr/local/sbin/StreamingServerModules (Darwin platforms)
, andc:\Program Files\Darwin StreamingServer\QTSSModules
. The server will load your module the next time it restarts.
Some platforms require that each module use unique function names. To prevent name conflicts when the server loads your module, strip the symbols from your module before you have the server load it.
Debugging
Several server preferences in the streamingserver.xml file are available for enabling the generation of debugging information, which is printed on the terminal screen. The following sections provide information on debugging:
RTSP and RTP Debugging
To enable the display of RTSP and RTP informati on the terminal screen, modify the RTSP_debug_printfs preference in the streamingserver.xml file and restart the server:
<PREF NAME="RTSP_debug_printfs" TYPE="BOOL16" >true</PREF> |
To enable the display of packet header information, modify the “enable_packet_header_printfs” preference in the streamingserver.xml file:
<PREF NAME="enable_packet_header_printfs" TYPE="BOOL16" >true</PREF> |
Then specify which packet headers to display by modifying the “packet_header_printf_options” preference. The following example enables the display of all packet headers:
<PREF NAME="packet_header_printf_options" >rtp;rr;sr;app;ack;</PREF> |
In the previous example, rtp
enables
the display of RTP packet headers, rr
enables
the display of RTCP receiver reports, sr
enables
the display of RTCP sender reports, app
enables
the display of RTCP application packets, and ack
enables
the display of Reliable UDP RTP acknowledgement packets.
After enabling RTSP and RTP debugging, restart the Streaming Server in debug mode using this command:
QuickTimeStreamingServer -d |
When you connect a client, debug information is displayed on the terminal screen.
Source File Debugging Support
You can enable debugging in specific source files. For example, in the file CommonUtilitiesLib/Task.h, make the following change:
#define TASK_DEBUG 1 |
Rebuild and start the Streaming Server in debug mode:
QuickTimeStreamingServer -d |
Here is some sample output:
Task::Signal enque task TaskName=RTSPSession ... |
TaskThread::Entry run task TaskName=RTSPSession ... |
TaskThread::Entry insert task TaskName=RTSPSession ... |
TaskThread::Entry run task TaskName=RTSPSession ... |
TaskThread::WaitForTask found timer task TaskName=QTSSAccessLog ... |
TaskThread::Entry run task TaskName=QTSSAccessLog ... |
You can also enable debugging in CommonUtilitiesLib/OSFileSource.cpp:
#define FILE_SOURCE_DEBUG 1 |
Here is some sample output:
OSFileSource::SetLog=/Library/QuickTimeStreaming/Movies/sample_100kbit.mov |
FileMap::AllocateBufferMap shared buffers |
OSFileSource::ReadFromCache inPosition =272 ... |
OSFileSource::ReadFromCache inPosition =276 ... |
OSFileSource::ReadFromCache inPosition =280 ... |
... |
OSFileSource::ReadFromCache inPosition =80667 |
Working with Attributes
QTSS objects consist of attributes that are used to store data. Every attribute has a name, an attribute ID, a data type, and permissions for reading and writing the attribute’s value. There are two attribute types:
static attributes. Static attributes are present in all instances of an object type. A module can add static attributes to objects from its Register role only. All of the server’s built-in attributes are static attributes. For information about adding static attributes to object types, see the section Adding Attributes
instance attributes. Instance attributes are added to a specific instance of any object type. A module can use any role to add an instance attribute to an object and can also remove instance attributes that it has added to an object. For information about adding instance attributes to objects, see the section Adding Attributes.
Getting Attribute Values
Modules use attributes stored in objects to exchange information with the server, so they frequently get attribute values. Three callback routines get attribute values:
QTSS_GetValue
, which copies the attribute value into a buffer provided by the module. This callback can be used to get the value of any attribute, but it is not as efficient asQTSS_GetValuePtr
.QTSS_GetValueAsString
, which copies the attribute value as a string into a buffer provided by the module. This callback can be used to get the value of any attribute. This is the least efficient way to get the value of an attributeQTSS_GetValuePtr
, which returns a pointer to the server’s internal copy of the attribute value. This is the most efficient way to get the value of preemptive safe attributes. It can also be used to get the value of non-preemptive safe attributes, but the object must first be locked and must be unlocked after QTSS_GetValuePtr is called. When getting the value of a single non-preemptive-safe attribute, calling QTSS_GetValue may be more efficient than locking the object, calling QTSS_GetValuePtr and unlocking the object.
The sample code in Listing 2-1 calls QTSS_GetValue
to
get the value of the qtssRTPSvrCurConn
attribute,
which is not preemptive safe, from the QTSS_ServerObject
object.
Listing 2-1 Getting the value of an attribute by calling QTSS_GetValue
UInt32 MyGetNumCurrentConnections(QTSS_ServerObject inServerObject) |
{ |
// qtssRTPSvrCurConn is a UInt32, so provide a UInt32 for the result. |
UInt32 theNumConnections = 0; |
// Pass in the size of the attribute value. |
UInt32 theLength = sizeof(theNumConnections); |
// Retrieve the value. |
QTSS_Error theErr = QTSS_GetValue(inServerObject, qtssRTPSvrCurConn, 0, |
&theNumConnections, &theLength); |
// Check for errors. If the length is not what was expected, return 0. |
if ((theErr != QTSS_NoErr) || (theLength != sizeof(theNumConnections)) |
return 0; |
return theNumConnections; |
} |
The sample code in Listing 2-2 calls QTSS_GetValuePtr
,
which is the preferred way to get the value of preemptive-safe attributes.
In this example, value of the qtssRTSPReqMethod
attribute
is obtained from the object QTSS_RTSPRequestObject
.
Listing 2-2 Getting the value of an attribute by calling QTSS_GetValuePtr
QTSS_RTSPMethod MyGetRTSPRequestMethod(QTSS_RTSPRequestObject inRTSPRequestObject) |
{ |
QTSS_RTSPMethod* theMethod = NULL; |
UInt32 theLen = 0; |
QTSS_Error theErr = QTSS_GetValuePtr(inRTSPRequestObject, qtssRTSPReqMethod, 0, |
(void**)&theMethod, &theLen); |
if ((theErr != QTSS_NoErr) || (theLen != sizeof(QTSS_RTSPMethod)) |
return -1; // Return a -1 if there is an error, which is not a valid |
// QTSS_RTSPMethod index |
else |
return *theMethod; |
} |
You can obtain the value any attribute by calling QTSS_GetValueAsString
,
which gets the attribute’s value as a C string. Calling QTSS_GetValueAsString
is
convenient when you don’t know the type of data the attribute
contains. In Listing 2-3, the value of the qtssRTPSvrCurConn
attribute
is obtained as a string from the QTSS_ServerObject
.
Listing 2-3 Getting the value of an attribute by calling QTSS_GetValueAsString
void MyPrintNumCurrentConnections(QTSS_ServerObject inServerObject) |
{ |
// Provide a string pointer for the result |
char* theCurConnString = NULL; |
// Retrieve the value as a string. |
QTSS_Error theErr = QTSS_GetValueAsString(inServerObject, qtssRTPSvrCurConn, 0, &theCurConnString); |
if (theErr != QTSS_NoErr) return; |
// Print out the result. Because the value was returned as a string, use |
// %s in the printf format. |
::printf("Number of currently connected clients: %s\n", theCurConnString); |
// QTSS_GetValueAsString allocates memory, so reclaim the memory by calling QTSS_Delete. |
QTSS_Delete(theCurConnString); |
} |
Setting Attribute Values
Two QTSS callback routines are available for setting the value
of an attribute: QTSS_SetValue
and QTSS_SetValuePtr
.
The sample code in Listing 2-4 would be found handling the Route role. It calls QTSS_GetValuePtr
to
get the value of the qtssRTSPReqFilePath
.
If the path matches a certain string, the function sets a new request
root directory by calling QTSS_SetValue
to
set the qtssRTSPReqRootDir
attribute
to a new path.
Listing 2-4 Setting the value of an attribute by calling QTSS_SetValue
// First get the file path for this request using QTSS_GetValuePtr |
char* theFilePath = NULL; |
UInt32 theFilePathLen = 0; |
QTSS_Error theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqFilePath, 0, &theFilePath, |
&theFilePathLen); |
// Check for any errors |
if (theErr != QTSS_NoErr) return; |
// See if this path is a match. If it is, use QTSS_SetValue to set the root directory for this request. |
if ((theFilePathLen == sStaticFilePathLen) && |
(::strncmp(theFilePath, sStaticFilePath, theFilePathLen) == 0)) |
{ |
theErr = QTS_SetValue(inParams->inRTSPRequest, qtssRTSPReqRootDir, 0, sNewRootDirString, |
sNewRootDirStringLen); |
if (theErr != QTSS_NoErr) return; |
} |
Listing 2-5 demonstrates the use of the QTSS_SetValuePtr
callback.
The QTSS_SetValuePtr
callback
associates an attribute with the value of a module’s variable.
This code sample modifies the QTSS_ServerObject
object
nonatomically, so it calls QTSS_LockObject
to prevent
other threads from accessing the attributes of the QTSS_ServerObject
before
the value has been set.
Then the code sample calls QTSS_CreateObjectValue
to
create a QTSS_ConnectedUserObject
object
as the value of the qtssSvrConnectedUsers
attribute
of the QTSS_ServerObject
object. Then
the code sample calls QTSS_SetValuePtr
to
set the value of the qtssConnectionBytesSent
attribute
of the QTSS_ConnectedUserObject
object
to the module’s fBytesSent
variable.
Thereafter, when any module gets the value of the qtssConnectionBytesSent
attribute,
it will get the current value of the module’s fBytesSent
variable.
After calling QTSS_SetValuePtr
,
the code sample calls QTSS_UnlockObject
to
unlock the QTSS_ServerObject
object.
Listing 2-5 Setting the value of an attribute by calling QTSS_SetValuePtr
UInt32 index; |
QTSS_LockObject(sServer); |
QTSS_CreateObjectValue(sServer, qtssSvrConnectedUsers, qtssConnectedUserTypeObject, &index, &fQTSSObject); |
QTSS_CreateObjectValue(sServer, qtssSvrConnectedUsers, qtssConnectedUserObjectType, &index, &fQTSSObject); |
QTSS_SetValuePtr(fQTSSObject, qtssConnectionBytesSent, &fBytesSent, sizeof(fBytesSent)); |
QTSS_UnlockObject(sServer); |
Adding Attributes
Any module can add an attribute to a QTSS object type by calling
the QTSS_AddStaticAttribute
callback
routine from its Register role. Modules can also call QTSS_AddInstanceAttribute
from
any role to add an attribute to an instance of an object.
Once added, the new attribute is included in every object
of that type that the server creates and its value can be set and
obtained by calling that same callback routines that set and obtain
the value of the server’s built-in attributes: QTSS_SetValue
, QTSS_SetValuePtr,
QTSS_GetValue
,
and QTSS_GetValuePtr
.
The sample code in Listing 2-6 calls QTSS_AddStaticAttribute
to
add an attribute to the object QTSS_ClientSessionObject
.
Listing 2-6 Adding a static attribute
QTSS_Error MyRegisterRoleFunction() |
{ |
// Add the static attribute. The third parameter is always NULL. |
QTSS_Error theErr = QTSS_AddStaticAttribute(qtssClientSessionObjectType, |
"MySampleAttribute", NULL, qtssAttrDataTypeUInt32); |
// Retrieve the ID for this attribute. This ID can be passed into QTSS_GetValue, |
// QTSS_SetValue, and QTSS_GetValuePtr. |
QTSS_AttributeID theID; |
theErr = QTSS_IDForAttr(qtssClientSessionObjectType, MySampleAttribute", &theID); |
// Store the attribute ID in a global for later use. Attribute IDs do not |
// change while the server is running. |
gMyExampleAttrID = theID; |
} |
Using Files
QTSS supports file system modules so that QTSS can transparently and easily work with custom file systems. For example, a QTSS file system module can allow a QTSS module to read a custom networked file system or a custom database. Support for reading files consists of the following:
QTSS file system callback routines that any module can use to open, read, and close files. Calling the file system callback routines is described in the section Reading Files Using Callback Routines. The QTSS file system callback routines allow QTSS to easily work with many different file system types. A QTSS module that uses the file system callbacks for reading all files can transparently use whatever file system is deployed on a server.
File system roles for which modules that implement file systems register. These roles provide a bridge between QTSS and a specific file system. The file system roles are described in the section Implementing a QTSS File System Module. You could, for example, write a file system module that interfaces QTSS to a custom database or a custom networked file system.
Reading Files Using Callback Routines
In QTSS, a file is represented by a QTSS stream, so you can use existing QTSS stream callback routines to read files. The callback routines that are available for working with files are:
QTSS_OpenFileObject
, which is called to open a file in the local operating system. This call is one of two callback routines that is only used when working with files.QTSS_CloseFileObject
, which is called to close a file that was opened by a previous call toQTSS_OpenFileObject
. This call is one of two callback routines that is only used when working with files.QTSS_Read
, which is called to read data from a file object’s stream that was created by a previous call toQTSS_OpenFileObject
.QTSS_Seek
, which is called to set the current position of a file object’s stream.QTSS_Advise
, which is called to tell a file system module that a specified section of one of its streams will be read soon.QTSS_RequestEvent
, which is called to tell a file system module that the calling module wants to be notified when one of the events in the specified event mask occurs. The events are when a stream becomes readable and when a stream becomes writable.
In QTSS, a file is QTSS_Object
that
has its own object type, QTSS_FileObject
,
that allows you to use standard QTSS callbacks (QTSS_GetValue
, QTSS_GetValueAsString
,
and QTSS_GetValuePtr
)
to get meta information about a file, such as its length and modification date.
You can use standard QTSS callbacks to store any amount of file
system meta information with the file object. For example, a module
working with a POSIX file system would want to add an attribute
to the file object that stores the POSIX file system descriptor.
A file object also has a QTSS stream reference that can be used
when calling QTSS stream routines that work with files, such as QTSS_Read
.
The sample code in Listing 3-7
shows how to open a file, determine the file’s length,
read the entire file, close the file, and return the data it contains.
Listing 2-7 Reading a file
QTSS_Error ReadEntireFile(char* inPath, void** outData, UInt32* outDataLen) |
{ |
QTSS_Object theFileObject = NULL; |
QTSS_Error theErr = QTSS_OpenFileObject(inPath, qtssOpenFileNoFlags, &theFileObject); |
if (theErr != QTSS_NoErr) |
return theErr; // The file wasn't found or it couldn't be opened. |
// The file is open. Find out how long it is. |
UInt64* theLength = NULL; |
UInt32 theParamLen = 0; |
theErr = QTSS_GetValuePtr(theFileObject, qtssFlObjLength, 0, (void**)&theLength, &theParamLen); |
if (theErr != QTSS_NoErr) |
return theErr; |
if (theParamLen != sizeof(UInt64)) |
return QTSS_RequestFailed;; |
// Allocate memory for the file data. |
*outData = new char[*theLength + 1]; |
*outDataLen = *theLength; |
// Read the data |
UInt32 recvLen = 0; |
theErr = QTSS_Read(theFileObject, *outData, *outDataLen, &recvLen); |
if ((theErr != QTSS_NoErr) || (recvLen != *outDataLen)) |
{ |
delete *outData; |
return theErr; |
} |
// Close the file. |
(void)QTSS_CloseFileObject(theFileObject); |
} |
Implementing a QTSS File System Module
A file system module provides a way for QTSS modules to read
files in a specific file system regardless of that file system’s
type. Typically, a file system module handles a subset of paths
in a file system, but it may handle all paths on the system. If
a file system module handles only a certain subset of paths, it
usually handles all paths inside a certain root path. For example,
a module handling files stored in a certain database may only respond
to paths that begin with /Local/database_root/
.
Implementing a QTSS file system module begins with registering for one of the following roles:
Open File Preprocess role, which the server calls in response to a module (or the server) that calls the
QTSS_OpenFileObject
callback routine to open a file. If the module does not handle files of the specified type, the module immediately returnsQTSS_FileNotFound
. If the module handles the files of the specified type, it opens the file, updates a file object provided by the server and returnsQTSS_NoErr
. If an error occurs during this setup period, the module returnsQTSS_RequestFailed
. Once the module returnsQTSS_NoErr
, it should be prepared to handle the Advise File, Read File, Request Event File and Close File roles for the opened file. The server calls each module registered in the Open File Preprocess role until one of the called modules returnsQTSS_NoErr
orQTSS_RequestFailed
.Open File role, which the server calls in response to a module (or the server) that calls the
QTSS_OpenFileObject
callback routine for which all modules handling the Open File Preprocess role returnQTSS_FileNotFound
. Only one module can register for the Open File role. Like modules called for the Open File Preprocess role, the module called for the Open File role must determine whether it can handle the specified file. It it can, it opens the file, updates the file object provided by the server and returnsQTSS_NoErr
. If an error occurs during the setup process or if the module cannot handle the specified file, the module returnsQTSS_RequestFailed
orQTSS_FileNotFound
, respectively.
A file system module should register in the Open File Preprocess role if it handles a subset of files available on the system. For instance, a file system module that serves files out of a database may only handle files rooted at a certain path. All other paths should fall through to other modules that handle other paths.
A file system module should register in the Open File role if it implements the default file system on a system. For instance, on a UNIX system the module handling the Open File Role would probably provide an interface between the server and the standard POSIX file system.
Once a module returns QTSS_NoErr
from
either the Open File Role or the Open File Preprocess role, it is
responsible for the newly opened file. It should be prepared to
handle the following roles on behalf of that file:
Advise File role, which is called in response to a module (or the server) calling the
QTSS_Advise
callback for a file object. TheQTSS_Advise
callback is made to inform the file system module that a specific region of the file will be needed soon.Read File role, which is called in response to a module (or the server) calling the
QTSS_Read
callback for a file object. It is the responsibility of a file system module handling this role to make a best-effort attempt to fill the buffer provided by the caller with the appropriate file data.Request Event File role, which is called in response to a module (or the server) calling the
QTSS_RequestEvent
callback on a file object.Close File role, which is called in response to a module (or the server) calling the
QTSS_Close
callback on a file object. The module should clean up any file-system and module-specific data structures for this file. This role is always the last role a file system module will be invoked in for a given file object.
File System Module Roles
This section describes the file system module roles. The roles are:
“Open File Preprocess Role”
which is called to process requests to open files.“Open File Role”
which is the default role that is called when none of the modules registered for the Open File Preprocess role opens the specified file.“Advise File Role”
which is called to tell a file system module about the caller’s I/O preferences.“Read File Role”
which is called to read a file.“Close File Role”
which is called to close a file.“Request Event File Role”
which is called to request notification when a file becomes available for reading or writing.
Open File Preprocess Role
The server calls the Open File Preprocess role in response
to a module that calls the QTSS_OpenFileObject
callback
routine to open a file. It is the responsibility of a module handling
this role to determine whether it handles the type of file specified
to be opened. If it does and if the file exists, the module opens
the file, updates the file object provided by the server, and returns QTSS_NoErr
.
When called, an Open File Preprocess role receives a QTSS_OpenFile_Params
structure, which
is defined as follows:
typedef struct |
{ |
char* inPath; |
QTSS_OpenFileFlags inFlags; |
QTSS_Object inFileObject; |
} QTSS_OpenFile_Params;s |
inPath
A pointer to a null-terminated C string containing the full path to the file that is to be opened.
inFlags
Open flags specifying whether the module that called
QTSS_OpenFileObject
can handle asynchronous read operations (qtssOpenFileAsync
) or expects to read the file in order from beginning to end (qtssOpenFileReadAhead
).inFileObject
A QTSS object that the module updates if it can open the file specified by
inPath
.
If the file is a file the module handles, the module should
do whatever work is necessary to open and set up the file. It can
use inFileObject
to store
any module-specific information for that file. In addition, the
module should set the value of the file object’s qtssFlObjLenth
and qtssFlObjModDate
attributes.
If the file is a file the module handles but an error occurs
while attempting to set up the file, the module should return QTSS_RequestFailed
.
If every module registered for the Open File Preprocess role
returns QTSS_FileNotFound
,
the server calls the one module that is registered in the Open File
role.
A module that wants to be called in the Open File Preprocess
role must in its Register role call QTSS_AddRole
and
specify QTSS_OpenFilePreprocess_Role
as
the role. Modules that register for this role must also handle the
following roles, but they do not need to explicitly register for
them: Advise File, Read File, Request Event File, and Close File.
Open File Role
The server calls the module registered for the Open File role
when all modules registered for the Open File Preprocess role have
been called and have returned QTSS_FileNotFound
. Only
one module can be registered for the Open File role, and that module
is the first module that registers for this role when QTSS starts
up.
Like modules called for the Open File Preprocess role, it
is the responsibility of a module handling the Open File role to
determine whether it handles the type of file specified to be opened.
If it does and if the file exists, the module opens the file, updates
the file object provided by the server, and returns QTSS_NoErr
.
When called, the module receives a QTSS_OpenFile_Params
structure,
which is defined as follows:
typedef struct |
{ |
char* inPath; |
QTSS_OpenFileFlags inFlags; |
QTSS_Object inFileObject; |
} QTSS_OpenFile_Params; |
inPath
A pointer to a null-terminated C string containing the full path to the file that is to be opened.
inFlags
Open flags specifying whether the module that called
QTSS_OpenFileObject
can handle asynchronous read operations (qtssOpenFileAsyn
c) or expects to read the file in order from beginning to end (qtssOpenFileReadAhead
).inFileObject
A QTSS object that the module updates if it can open the file specified by
inPath
.
If the file is a file the module handles, the module should
do whatever work is necessary to open and set up the file. It can
use inFileObject
to store
any module-specific information for that file. In addition, the
module should set the value of the file object’s qtssFlObjLength
and qtssFlObjModDate
attributes.
If the file is a file the module handles but an error occurs
while attempting to set up the file, the module should return QTSS_RequestFailed
.
A module that wants to be called in the Open File role must
in its Register role call QTSS_AddRole
and
specify QTSS_OpenFile_Role
as
the role. Modules that register for this role must also handle the
following roles, but they do not need to explicitly register for
them: Advise File, Read File, Request Event File, and Close File.
Advise File Role
The server calls modules for the Advise File role in response
to a module (or the server) calling the QTSS_Advise
callback
routine for a file object in order to inform the file system module
that the calling module will soon read the specified section of
the file.
When called, an Advise File role receives a QTSS_AdviseFile_Params
structure,
which is defined as follows:
typedef struct |
{ |
QTSS_Object inFileObject; |
UInt64 inPosition; |
UInt32 inSize; |
} QTSS_AdviseFile_Params; |
inFileObject
The file object for the opened file. The file system module uses the file object to determine the file for which the
QTSS_Advise
callback routine was called.inPosition
The offset in bytes from the beginning of the file that represents the beginning of the section that is soon to be read.
inSize
The number of bytes that are soon to be read.
The file system module is not required to do anything while handling this role, but it may take this opportunity to read the specified section of the file.
File system modules do not need to explicitly register for this role.
Modules should always return QTSS_NoErr
when
they finish handling this role.
Read File Role
The server calls modules for the Read File role in response
to a module (or the server) calling the QTSS_Read
callback
routine for a file object in order to read the specified file.
When called, a Read File role receives a QTSS_ReadFile_Params
structure,
which is defined as follows:
typedef struct |
{ QTSS_Object inFileObject; |
UInt64 inFilePosition; |
void* ioBuffer; |
UInt32 inBufLen; |
UInt32* outLenRead; |
} QTSS_ReadFile_Params; |
inFileObject
The file object for the file that is to be read. The file system module uses the file object to determine the file for which the
QTSS_Read
callback routine was called.inFilePosition
The offset in bytes from the beginning of the file that represents the beginning of the section that is to be read. The server maintains the file position as an attribute of the file object, so the file system module does not have to cache the file position internally and can obtain the position at any time.
ioBuffer
A pointer to the buffer in which the file system module is to place the data that is read.
ioBufLen
The length of the buffer pointed to by
ioBuffer
.outLenRead
The number of bytes actually read.
The file system module should make a best-effort attempt to
fill the buffer pointed to by ioBuffer
with
data from the file that is being read starting with the position
specified by inFilePosition
.
If the file was opened with the qtssOpenFileAsync
flag,
the module should return QTSS_WouldBlock
if
reading the data will cause the thread to block. Otherwise, the
module should block the thread until all of the data has become
available. When the buffer pointed to by ioBuffer
is
full or the end of file has been reached, the file system module
should set outLenRead
to
the number of bytes read and return QTSS_NoErr
.
If the read fails for any reason, the file system module handling
this role should return QTSS_RequestFailed
.
File system modules do not need to explicitly register for this role.
Close File Role
The server calls modules for the Close File role in response
to a module (or the server) calling the QTSS_CloseFile
callback
routine for a file object in order to close a file that has been
opened.
When called, a Close File role receives a QTSS_CloseFile_Params
structure,
which is defined as follows:
typedef struct |
{ |
QTSS_Object inFileObject; |
} QTSS_CloseFile_Params; |
inFileObject
The file object for the file that is to be closed. The file system module uses the file object to determine the file for which the
QTSS_Close
callback routine was called.
A module handling this role should dispose of any data structures that it has created for the file that is to be closed.
This role is always the last role for which a file system module will be invoked for any given file object.
File system modules do not need to explicitly register for this role.
Modules should always return QTSS_NoErr when they finish handling this role.
Request Event File Role
The server calls modules for the Request Event File role in
response to a module (or the server) calling the QTSS_RequestEvent
callback
routine. If a module or the server calls the QTSS_OpenFileObject
callback
routine and specifies the qtssOpenFileAsync
flag,
the file system module handling that file object may return QTSS_WouldBlock
from
its Read File role. When that occurs, the caller of QTSS_Read
may
call QTSS_RequestEvent
callback
to tell the server that the caller of QTSS_Read
wants
to be notified when the data becomes available for reading.
When called, a Request Event File role receives a QTSS_RequestEventFile_Params
structure, which
is defined as follows:
typedef struct |
{ |
QTSS_Object inFileObject; |
QTSS_EventType inEventMask; |
} QTSS_RequestEventFile_Params; |
inFileObject
The file object for the file for which notifications are requested. The file system module uses the file object to determine the file for which the
QTSS_RequestEvent
callback routine was called.inEventMask
A mask specifying the type of events for which notification is requested. Possible values are
QTSS_ReadableEvent
andQTSS_WriteableEvent
.
If the file system that the file system module is implementing
supports notification, the file system module should do whatever
setup is necessary to receive an event for the file for which the QTSS_RequestEvent
callback
routine was called. When the file becomes readable, the file system
module should call the QTSS_SignalStream
callback
routine and pass the stream reference for this file object (which
can be obtained through the file object’s qtssFlObjStream
attribute).
Calling the QTSS_SignalStream
callback
routine tells the server that the caller of QTSS_RequestEvent
should
be notified that the file is now readable.
File system modules do not need to explicitly register for this role.
Modules should always return QTSS_NoErr
when
they finish handling this role.
Sample Code for the Open File Role
The sample code in Listing 3-8
handles the Open File role, but it could also be used
to handle the Open File Preprocess role. This code uses the POSIX
file system layer as the file system and does not support asynchronous
I/O.
Listing 2-8 Handling the Open File Role
QTSS_Error OpenFile(QTSS_OpenFile_Params* inParams) |
{ |
// Use the POSIX open call to attempt to open the specified file. |
// If it doesn't exist, return QTSS_FileNotFound |
int theFile = open(inParams->inPath, O_RDONLY); |
if (theFile == -1) |
return QTSS_FileNotFound; |
// Use the POSIX stat call to get the length and the modification date |
// of the file. This information must be set in the QTSS_FileObject |
// by every file system module. |
UInt64 theLength = 0; |
time_t theModDate = 0; |
struct stat theStatStruct; |
if (::fstat(fFile, &theStatStruct) >= 0) |
{ |
theLength = buf.st_size; |
theModDate = buf.st_mtime; |
} |
else |
{ |
::close(theFile); |
return QTSS_RequestFailed; // Stat failed |
} |
// Set the file length and the modification date attributes of this file |
// object before returning |
(void)QTSS_SetValue(inParams->inFileObject, qtssFlObjLength, 0, &theLength, sizeof(theLength)); |
(void)QTSS_SetValue(inParams->inFileObject, qtssFlObjModDate, 0, &theModDate, sizeof(theModDate)); |
// Place the file reference in a custom attribute in the QTSS_FileObject. |
// This way, we can easily get the file reference in other role handlers, |
// such as the QTSS_ReadFile_Role and the QTSS_CloseFile_Role. |
QTSS_Error theErr = QTSS_SetValue(inParams->inFileObject, sFileRefAttr, 0, |
&theFile, sizeof(theFileSource)); |
if (theErr != QTSS_NoErr) |
{ |
::close(theFile); |
return QTSS_RequestFailed; |
} |
return QTSS_NoErr; |
} |
Implementing Asynchronous Notifications
If a module, or the server, calls the QTSS_OpenFileObject
and
specifies the qtssOpenFileAsync
flag,
the file system module handling that file object may return QTSS_WouldBlock
from
its QTSS_ReadFile_Role
handler.
Once that happens, the caller of QTSS_Read
may
want to be notified when the requested data becomes available for
reading. This is possible by calling the QTSS_RequestEvent
callback,
which tells the server that the caller would like to be notified
when data is available to be read from the file.
Not all file systems support notification mechanisms, and
if they do, the notification mechanisms are particular to each file
system architecture. Therefore, whether a file system module supports
notifications is at the discretion of the developer of the file
system module. In general it is better for a file system module
to support asynchronous notifications and not block in QTSS_ReadFile_Role
because
blocking on one file operation may disrupt service for many of the
server’s clients.
Two facilities allow file system modules to implement notifications:
QTSS_RequestEventFile_Role
, which is called in response to a module (or the server) calling theQTSS_RequestEvent
callback on a file object. Modules do not need to explicitly register for this role. If a module doesn’t implement asynchronous notifications, it should returnQTSS_RequestFailed
from this role. If a module does implement asynchronous notifications, it should do whatever setup is necessary to receive an event for this file when the file becomes readable.QTSS_SendEventToStream
callback, called by a file system module when a file does become readable. CallingQTSS_SendEventToStream
tells the server that the caller ofQTSS_RequestEvent
should be notified that the file is now readable.
Using the Admin Protocol
You can use the Admin protocol to communicate with QTSS. The Admin Protocol relies on the URI mechanism defined by RFC 2396 for specifying a container entity using a path and on the request and response mechanism for the Hypertext Transfer Protocol defined in RFC 1945.
The server’s internal data is mapped to a hierarchical tree of element arrays. Each element is a named type including a container type for retrieval of sub-node elements.
The server state machine and database can be accessed through a regular expression. The Admin Protocol abstracts the QTSS module API to handle data access and in some cases to provide data access triggers for execution of server functions.
Server streaming threads are blocked while the Admin Protocol accesses the server’s internal data. To minimize blocking, the Admin Protocol allows scoped access to the server’s data structures by allowing specific URL paths to any element.
The Admin Protocol uses the HTTP GET as the request and response method. At the end of each response, the session between client and server is closed. The Admin Protocol also supports the Authorization request header field as described in RFC 1945, section 10.2.
Access to Server Data
The Admin Protocol uses URIs to specify the location of server data. The following URI references the top level of the server’s hierarchical data tree using a simple HTTP GET request.
GET /modules/admin |
Request Syntax
A valid request is an absolute reference followed by the server
URI. An absolute reference is a path beginning with a forward slash
character (/
). A path
represents the server’s virtual hierarchical data structure of
containers and is expressed as a URL.
Here is the request syntax:
[absolute URL]?
[parameters
=”values"
]+
[command="
value"
]+
["option"="
value”]
The following rules govern URIs:
/path is an absolute reference.
path
/*
is defined as all elements contained in the “path” container.An asterisk (
*
) in the current URL location causes each element in that location to be iterated.A question mark (
?
) indicates that options follow. Options are specified asname="
value"
pairs delimited by the plus (+
) character.Space and tab characters are treated as stop characters.
Values can be enclosed by the double quotation characters (
"
). Enclosing double quotation characters is required for values that contain spaces and tabs.These characters cannot be used: period (
.
), two periods (..
), and semicolon (;
).
Here is an example of a request:
GET /modules/admin/server/qtssSvrClientSessions?parameters=rt+command=get |
Request Functionality
Requests can contain an array iterator, a name lookup, a recursive tree walk, and a filtered response. All functions can execute in a single URI query.
Here is a request that gets the stream time scale and stream payload name for every stream in every session:
GET /modules/admin/server/qtssSvrClientSessions/*/qtssCliSesStreamObjects? |
parameters=r+command=get+filter1=qtssRTPStrTimescale+filter2=qtssRTPStrPayloadName |
where
*
iterates the array of sessionsr
inparameter=rt
specifies a recursive walk and t specifies that data types are to be included in the resultfilter=qtssRTPStrTimescale
specifies that the stream time scale is to be returnedfilter2=qtssRTPStrPayloadName
specifies that the stream payload is to be returned
This request gets all server module names and their descriptions:
GET /modules/admin/server/qtssSvrModuleObjects? |
parameters=r+command=get+filter2=qtssModDesc+filter1=qtssModName |
The following example does a recursive search and gets all server attributes and their data types:
GET /modules/admin/server/?parameters=rt |
The following examples return server attributes and their paths:
GET /modules/admin/server/* |
GET /modules/admin/server/qtssSvrPreferences/* |
Data References
All elements are arrays. Single element arrays may be referenced in any of the following ways:
path
/
elementpath
/
element/
path
/
element/
*
path
/
element/
1
The references listed above are all evaluated as the same request.
Request Options
URIs that do not include a question mark (?) default to a GET request option.
URIs that include a question mark (?) must be followed by
a "command=
command-option"
request
option, where command-option is GET
, SET
, ADD
,
or DEL
. URIs may also
be followed by a "parameters=
parameter-option"
that
refines the action of the command option.
Request options are not case-sensitive, but request option values are case-sensitive.
The Admin Protocol ignores any request option that it does not recognize as well any request options that a command does not require.
Command Options
The Admin Protocol recognizes the following command options:
GET
, described in the section“GET Command Option”
SET
, described in the section“SET Command Option”
DEL, described in the section
“DEL Command Option”
ADD
, described in the section“ADD Command Option”
Any unknown command option is reported as an error.
The effect of a command option may be modified by in the inclusion of one or more of the following modifiers:
value
— used to specify a valuetype
— used to specify a data typename
— used to specify an element name
GET Command Option
The GET
command
option gets the data identified by the URI. It is the default command option.
For that reason, it does not have to be specified, as shown in the
following example:
GET /modules/admin/example_count |
The GET
command
does not require any request options. If any request options were specified,
they would be ignored.
SET Command Option
The SET
command
option sets the data identified by the URI. No value checking is performed.
Conversion between the text value and the actual value is type-specific.
Here are two examples of the SET command option
:
GET /modules/admin/example_count?command=SET+value=5 |
GET /modules/admin/maxcount?command=SET+value=5+type=SInt32 |
If the type
option
is included in the command, type checking of the server element
type and the set type is performed. If the types do not match, an
error is returned and the command fails.
DEL Command Option
The DEL
command
option deletes the element referenced by the URL and any data it contains.
Here is an example:
GET /modules/admin/maxcount?command=DEL |
ADD Command Option
The ADD
command
option adds the data specified by the URI to the specified element.
If the end of the URL is an element, the ADD
command
performs an add to the array of elements referenced by the element
name. The following example adds 6 to example_count
if
the data type of example_count is SInt16
:
GET /modules/admin/example_count?command=ADD+value=6+type=SInt16 |
If the element at the end of the URL is a QTSS_Object
container,
the ADD
command option adds
the element to the container. The following example adds 5 to the
element whose name is maxcount
if
the data type of maxcount
is SInt16
:
GET /modules/admin/?command=ADD+value=5+name=maxcount+type=SInt16 |
Parameter Options
Parameter options are single characters without delimiters that appear after the URL.
The Admin Protocol recognizes the following parameter options:
r
— Walk downward in the hierarchy starting at end of the URL. Recursion should be avoided if “*” iterators or direct URL access to elements can be used instead.v
— Return the full path in name.a
— Return the access type.t
— Return the data type of value.d
— Return debugging information if an error occurs.c
— Return the count of elements in the path.
Here is an example that uses the r and t parameter options
to recursively get the data type of all qtssSvrClientSessions
:
GET /modules/admin/server/qtssSvrClientSessions?parameters=rt+command=get |
Attribute Access Types
The following access types are used to control access to server data:
r
— Read access typew
— Write access typep
— Preemptive safe access type
Data Types
Data types can be any server-allowed text value. New data types can be defined and returned by the server, so data types are not limited to the basic set listed here:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Values of type QTSS_Object
,
pointers, and unknown data types always converted to a host-ordered
string of hexadecimal values. Here is an example of a hexadecimal
value result:
unknown_pointer=halogen; type=void_pointer |
Server Responses
This section describes the data that is returned in response to a request. The information on response data is organized in the following sections:
Unauthorized Response
Here is an example of an unauthorized response:
HTTP/1.1 401 Unauthorized |
WWW-Authenticate: Basic realm="QTSS/modules/admin" |
Server: QTSS |
Connection: Close |
Content-Type: text/plain |
OK Response
Here is an example of an “OK” response:
HTTP/1.0 200 OK |
Server: QTSS/4.0 [v408]-MacOSX |
Connection: Close |
Content-Type: text/plain |
Container="/" |
admin/ |
error:(0) |
All OK responses end with error:(0)
.
Response Data
All entity references in response data follow this form:
[NAME=
VALUE];
[attribute="
value"
],
[attribute=
”value"
]
where brackets ([ ]) indicate that the enclosed response data is optional. Therefore, the response data may take the following forms:
NAME=
VALUE
NAME=
VALUE;
attribute="
value"
NAME=
VALUE;
attribute="
value",
attribute="
value"
All container references follow this form:
[NAME/
];
[attribute="
value"
],
[attribute="
value"
]
where brackets ([ ]) indicate that the enclosed response data is optional. Therefore, response data may take the following forms:
NAME/
NAME/;
attribute="
value"
NAME;
attribute="
value",
attribute="
value"
The order of appearance of container references and the container’s entity references are important. This is especially true when the response is a recursive walk of a container hierarchy.
Each new level in the hierarchy must begin with a Container=
reference.
Each container list of elements must be a complete list of the contained
elements and any containers. The appearance of a Container=
reference
indicates the end of a previous container’s contents and the beginning
of a new container.
This example shows how each new container is identified with a unique path:
Container="/level1/" |
field1="value" |
field2="value" |
level2a/ |
level2b/ |
Container="/level1/level2a/" |
field1="value" |
level3a/ |
level3b/ |
Container="/level1/level2a/level3a" |
field1="value" |
Container="/level1/level2a/level3b" |
Container="/level1/level2b/" |
field1="value" |
level3a/ |
Container="/level1/level2b/level3a/" |
field1="value" |
Array Values
For arrays of elements, a numerical value represents the index. Arrays are containers. Here is an example:
Container="/level1/" |
field1="value" |
field2="value" |
array1/ |
Container="/level1/array1/" |
1=value |
2=value |
Array elements may be containers, as shown in this example:
Container="/level1/array1/" |
1/ |
2/ |
3/ |
Container="/level1/array1/1/" |
field1="value" |
field2="value" |
Container="/level1/array1/2/" |
Container="/level1/array1/3/" |
field1="value" |
Response Root
The root for responses is /admin
.
Errors in Responses
For each response, the error state for the request is reported at the end of the data. Here are some examples:
Error:(0)
indicates
that no error occurred
Error:(404)
indicates
that no data was found
The number enclosed by parentheses is an HTTP error code followed
by an error string when debugging is turned on using the "parameters=d"
query
option. Here is an example:
error:(404);reason="No data found" |
Request and Response Examples
An easy way to make requests is to use a web browser and a URL like this:
http://
IP-address:554/modules/admin/?parameters=a+command=get
The following example uses basic authentication and shows the HTTP response headers:
Request: GET /modules/admin?parameters=a+command=get
Authorization: Basic QWXtaW5pT3RXYXRvcjXkZWZhdWx0
Response:
HTTP/1.0 200 OK |
Server: QTSS/4.0 [v408]-MacOSX |
Connection: Close |
Content-Type: text/plain |
Container="/" |
admin/;a=r |
error:(0) |
The following recursive request gets the value of each element
in /modules/admin
:
GET /modules/admin?command=get+parameters=r |
The following recursive request returns the access type and
data type for the value of each element in /modules/admin
:
GET /modules/admin?command=get+parameters=rat |
The following request gets the elements in /modules/admin
.
Note that the GET
command option
is not required because request options are not present.
GET /modules/admin/* |
A request like the following can be used to monitor the session list:
GET /modules/admin/server/qtssSvrClientSessions/* |
The response is a list of unique qtssSvrClientSessions
session
IDs. Here is an example::
Container="/admin/server/qtssSvrClientSessions/" |
12/ |
2/ |
4/ |
8/ |
error:(0) |
The following request gets the indexes for the qtssCliSesStreamObjects
object,
which is an indexed array of streams:
GET /modules/admin/server/qtssSvrClientSessions/*/qtssCliSesStreamObjects/* |
The response might look like this:
Container="/admin/server/qtssSvrClientSessions/3/qtssCliSesStreamObjects/" |
0/ |
1/ |
error:(0) |
Here is another request:
GET /modules/admin/server/qtssSvrClientSessions/3/qtssCliSesStreamObjects/0/* |
And here is a typical response:
qtssRTPStrTrackID="4" |
qtssRTPStrSSRC="683618521" |
qtssRTPStrPayloadName="X-QT/600" |
qtssRTPStrPayloadType="1" |
qtssRTPStrFirstSeqNumber="-7111" |
qtssRTPStrFirstTimestamp="433634204" |
qtssRTPStrTimescale="600" |
qtssRTPStrQualityLevel="0" |
qtssRTPStrNumQualityLevels="3" |
qtssRTPStrBufferDelayInSecs="3.000000" |
qtssRTPStrFractionLostPackets="0" |
qtssRTPStrTotalLostPackets="52" |
qtssRTPStrJitter="0" |
qtssRTPStrRecvBitRate="1526072" |
qtssRTPStrAvgLateMilliseconds="501" |
qtssRTPStrPercentPacketsLost="0" |
qtssRTPStrAvgBufDelayInMsec="30" |
qtssRTPStrGettingBetter="0" |
qtssRTPStrGettingWorse="0" |
qtssRTPStrNumEyes="0" |
qtssRTPStrNumEyesActive="0" |
qtssRTPStrNumEyesPaused="0" |
qtssRTPStrTotPacketsRecv="6763" |
qtssRTPStrTotPacketsDropped="0" |
qtssRTPStrTotPacketsLost="0" |
qtssRTPStrClientBufFill="0" |
qtssRTPStrFrameRate="0" |
qtssRTPStrExpFrameRate="3903" |
qtssRTPStrAudioDryCount="0" |
qtssRTPStrIsTCP="false" |
qtssRTPStrStreamRef="18861508" |
qtssRTPStrCurrentPacketDelay="-2" |
qtssRTPStrTransportType="0" |
qtssRTPStrStalePacketsDropped="0" |
qtssRTPStrTimeFlowControlLifted="974373815109" |
qtssRTPStrCurrentAckTimeout="0" |
qtssRTPStrCurPacketsLostInRTCPInterval="52" |
qtssRTPStrPacketCountInRTCPInterval="689" |
QTSSReflectorModuleStreamCookie=(null) |
qtssNextSeqNum=(null) |
qtssSeqNumOffset=(null) |
QTSSSplitterModuleStreamCookie=(null) |
QTSSFlowControlModuleLossAboveTol="0" |
QTSSFlowControlModuleLossBelowTol="3" |
QTSSFlowControlModuleGettingWorses="0" |
error:(0) |
Here is an request that returns the IP addresses of connected clients:
GET /modules/admin/server/qtssSvrClientSessions/*/qtssCliRTSPSessRemoteAddrStr |
And here is a typical response:
Container="/admin/server/qtssSvrClientSessions/5/ "qtssCliRTSPSessRemoteAddrStr=17.221.40.1 |
Container="/admin/server/qtssSvrClientSessions/6/ "qtssCliRTSPSessRemoteAddrStr=17.221.40.2 |
Container="/admin/server/qtssSvrClientSessions/8/ "qtssCliRTSPSessRemoteAddrStr=17.221.40.3 |
Container="/admin/server/qtssSvrClientSessions/14/ "qtssCliRTSPSessRemoteAddrStr=17.221.40.4 |
error:(0) |
Changing Server Settings
To change a server setting, the entity name and the value to be set are specified in the request body. If a match is made on the URL base and entity name at the current container level and if the setting is writable, the value is set.
base = /base/container |
name = value |
/base/container/name="value" |
Getting and Setting Preferences
Preferences paths are useful for getting and setting a server or module preference. Setting a preference causes the preference’s new value to be flushed to the server’s XML preference file. The new value takes effect immediately.
Server preferences are stored in /modules/admin/server/qtssSvrPreferences
.
Module preferences are stored in /modules/admin/server/qtssSvrModuleObjects/*/qtssModPrefs/
.
The elements defined in the qtssSvrPreferences
object
can only be modified — they cannot be deleted.
The elements defined in qtssModPrefs
can
be added to, deleted, and modified.
A module or the server can automatically restore some deleted
elements if the elements are needed by a module or the server. When
applied to a qtssModPrefs
element,
the ADD
, DEL
, and SET
commands
cause the streaming server’s XML preference file to be rewritten.
Getting and Changing the Server’s State
The qtssSvrState
attribute
controls the server’s state. The path is
/modules/admin/server/qtssSvrState
.
It can be modified as a UInt32
with
the following values.
qtssStartingUpState = 0, |
qtssRunningState = 1, |
qtssRefusingConnectionsState = 2, |
qtssFatalErrorState = 3, |
qtssShuttingDownState = 4, |
qtssIdleState = 5 |
Copyright © 2002, 2009 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2009-06-01