Darwin Streaming Server 2 - Source Code FAQ

## Darwin Streaming Server Source Code

## Frequently Asked Questions

### 1. What is the QTFile library?

One of the major features of the Darwin Streaming Server is the ability to serve hinted QuickTime files over RTSP and RTP. All of the code for parsing hinted QuickTime files has been abstracted into the QTFile library. Separating the code in this way keeps both parts much simpler: QTFile only deals with file parsing, the Darwin Streaming Server only deals with networking and protocols. The RTPFileModule in the server calls the QTFile library to retrieve packets and meta-data from hinted QuickTime files.

### 2. What is the reflector, and how does it work?

The reflector allows an administrator to deliver live broadcasts to RTSP clients. The reflector is implemented as an RTP module, and the source code is entirely in RTPReflectorModule.h/.cpp, and ReflectorSession.h/.cpp.

When a QuickTime client wants to view a broadcast, it first connects to the Darwin Streaming Server reflector module and directs the module to look for a proper incoming broadcast. If the broadcast is found, the Darwin Streaming Server will then "reflect" the broadcast to the client. The following is a detailed description of how this works. Readers may want to familiarize themselves with SDP (Session Description Protocol), and IP multicast before continuing.

In order to reflect something, there must be a live broadcast available to reflect. A broadcast is a stream of RTP packets generated by an application or process external to the Darwin Streaming Server and typically run on a separate machine. In this discussion we will call the live stream generator the "Broadcaster". The Broadcaster converts a live media source (like a camera, or microphone, or whatever) into RTP packets. It sends the packets over UDP, to either a multicast or unicast destination address. Broadcasters will usually create .sdp files containing all the SDP (Session Description Protocol), information about this live presentation needed by the client and reflector.

Most importantly, the .sdp file contains the (destination) IP address and ports for the live presentation. The IP address can define a multicast or unicast connection for the client. QuickTime Clients can read .sdp files directly and use them to connect directly to a Broadcaster. When the IP address in the .sdp file specifies a multicast address, the client will join the multicast provided there are multicast-aware routers between it and the Broadcaster. When the IP address is a unicast type, the client will connect when the .sdp destination IP address is the IP address of the client.This is because the broadcaster is sending UDP packets directly to that machine!

In order to reflect the broadcast stream, the .sdp file created by the Broadcaster must be located on the server, and inside the server's media folder. Let's say that on our server, there's a .sdp file called "fish.sdp" located at the root of the media folder.

When a RTSP client connects and specifies "rtsp://ourserver.com/fish", the reflector module will look for and find the file called "fish.sdp in the media folder.

After the .sdp file is found, the Reflector parses it to get the source IP address and ports for the live presentation. When the server then makes the connection, the same rules apply to the server as to a real client. This is because the .sdp specified server connection is simply a client of the live presentation. This means the IP address must specify a multicast address, or the IP address of the server itself (can it specify another server?).

Once the source address for the live presentation is located, the reflector binds some sockets to the specified ports. If the specified IP address is a multicast type, the Reflector will join the multicast. At this point, those sockets will begin receiving all the data being sent by the Broadcaster.

The reflector module allows a multicast client to view the broadcast stream as a normal unicast stream coming from the Darwin Streaming Server. The .sdp file is rewritten on the fly by the reflector to erase the source IP address and port information, to hide the information from the client. Once a PLAY request is issued by the client, the reflector begins sending all incoming packets from the broadcaster to the client.

As additional clients connect to the same live stream, the Reflector increments refcounts and adds each new client to its stream tracking data structures. This efficiently allows each client to receive identical copies of all incoming packets from the Broadcaster.

When all clients have disconnected, the Reflector closes the source UDP sockets, and deallocates all resources for that broadcast.

There is no limit on the number of unique live broadcasts that a single server can reflect, nor on the number of clients that can be connected to a single Reflection, apart from the overhead of file descriptor limitations, CPU, memory & bandwidth constraints. The CPU & memory consumed by a reflected streams is typically much less than normal locally stored media. Note: each unique live broadcast must be represented on the server by a unique .sdp file.

### 3. What is the Event Queue?

The Event Queue is an extension to the sockets API that exists on Mac OS X. It consists of three API calls:

watchevent:

Watch for events on a file descriptor (socket).

waitevent:

Wait for events on any of sockets (this is a blocking call, it only returns when there is an event pending).

modwatch:

When waitevent returns an event for a socket, that socket won't receive any new events until modwatch is called for that socket.

The use of these API calls is almost exclusively contained within Socket.cpp. This file contains the implementation of a thread object called SocketEventQueueThread. This thread blocks on waitevent and notifies the proper Task object (See "What Are Task Objects?") when an event is received.

For other UNIX platforms, an implementation of these three Event Queue API calls is provided in terms of select(). This implementation is contained in ev.cpp.

### 4. How does the Darwin Streaming Server(DSS) employ threads?

Most work done by DSS happens inside the Run function of various Task objects. Task objects run on Task threads (see Task.h / .cpp for this code). By default, DSS uses only 1 task thread. For uniprocessor systems, this tends to run the fastest because it eliminates any unnecessary thread switching the kernel would do. This does mean that every client connection runs on that one thread, so if anything blocks, it will affect all clients. For multiprocessor systems or if blocking is a serious concern, the server is designed to scale up to any number of Task threads.

### 5. What are Task Objects?

Because the server is largely asynchronous, there needs to be a communication mechanism for events. For instance, when a socket used for an RTSP connection gets data, something has to be notified so that data can be processed. The Task object is a generalized mechanism for performing this communication.

Each Task object has two major methods: Signal and Run. Signal is called by the server to send an event to a Task object. Run is called to give time to the Task for processing the event. The goal of each Task Object is to implement server functionality using small non-blocking time slices. Run is a pure virtual function that is called when a Task object has events to process. Inside the Run function, the Task object can call GetEvents to receive and automatically dequeue all its current and previously Signaled events. The Run function will never be re-entered: if a Task object calls GetEvents in its Run function, and is then Signaled before the Run function completes, the Run function will be called again for the new event only after exiting the function. In fact, the Task's Run function will be called repeatedly until the all task object's events have been cleared with GetEvents.

This core concept of event triggered Tasks is integrated into almost every subsystem in DSS. For instance, a Task object can be associated with a Socket object. If the Socket gets an event (through a select() notification, or through the Mac OS X Event Queue (see "What is the Event Queue?") the corresponding Task object will be Signaled. In this case the body of the Run function will contain the code for processing whatever event was received on that Socket.

By using these Task objects, it is possible to run all connections using a single thread, which is the default configuration of DSS.

© 1999 Apple Computer, Inc. All rights reserved. Apple, the Apple logo, Mac, Macintosh, PowerBook, Power Macintosh, and QuickTime are trademarks of Apple Computer, Inc., registered in the United States and other countries. iBook, iMac, and Power Mac are trademarks of Apple Computer, Inc. All other product names are trademarks or registered trademarks of their respective holders.