`
weiyuhu
  • 浏览: 235901 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

DSS6.03如何在REDHAT下编译及其性能测试(很好的一个文章

阅读更多
http://www.codeproject.com/KB/audio-video/DarwinSS_on_Linux.aspx


Introduction
Darwin Streaming Server is Apple's open source streaming server for the internet. This article will get you started with it, and if you choose to, take you to a certain depth regarding how to tinker with its code to add what you need. It has a very well supported modular architecture, which means you can add one or more custom module(s) - just dropping the binary in a certain folder is sufficient for the server to pick it up and use it. That's what I call plug an play. This is very useful if you do not want to re-build the server's code itself. This article will demonstrate exactly how to do that. Custom modules can be used to perform custom authentication/ authorization, custom logging, reporting, data collection or whatever you have in mind.

This article will also discuss the results of some load tests and performance tests I did on the server, trying to discover its limits. My platform was Redhat Linux, but the steps of setting up DSS are pretty much the same for most Unix platforms.

Hinting
Darwin Streaming Server can stream hinted media files, like mp4 or mov files - Apple formats that can be played back using the Quick Time Player. In fact, it can stream almost any other kind of content, as long as the file is hinted. You can "hint" media files using tools Apple provides. If you need to hint your media files, please research separately. I will assume that you have a catalog of hinted files which your Darwin server will stream to a Quicktime player (or any other player able to play Apple streams) running on some machine which can communicate with your server. The default installation of Darwin already comes with some sample hinted media files. To set up and test, you do not need any more files.

Platform and Hardware

Server Platform
$ cat /etc/redhat-release

Red Hat Enterprise Linux AS release 4 (Nahant Update 6)

$ gcc -v

Reading specs from /usr/lib/gcc/x86_64-redhat-linux/3.4.6/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-java-awt=gtk --host=x86_64-redhat-linux
Thread model: posix
gcc version 3.4.6 20060404 (Red Hat 3.4.6-9)

Server Hardware
Summary: Dell SC1425, 2 x Xeon 2.80GHz, 3.9GB / 4GB 400MHz
System: Dell PowerEdge SC1425 (Dell 0D7449)
Processors: 2 x Xeon 2.80GHz 800MHz FSB (HT enabled, 4 threads) - Nocona E0, 64-bit, 90nm, L2: 1MB
Memory: 3.9GB / 4GB 400MHz == 2 x 2GB - 2GB PC2-3200 Netlist DDR2-400 ECC Registered CL3 2R
4 x empty
Disk: sda (ata_piix0): 80GB (63%) JBOD == 1 x 80GB 7.2K SATA/300 WD Caviar SE 8MB
Disk-Control: piix_ide0: Dell/Intel ICH5 82801EB ATA/100
Disk-Control: ata_piix0: Dell/Intel ICH5 82801EB SATA/150
Chipset: Intel E7520 C1 (Lindenhurst), 82801E (ICH5)
Network: eth0 (e1000): Dell/Intel 82541PI Gigabit, *** masked MAC ***, 1Gb/s <full-duplex>
Network: eth1 (e1000): Dell/Intel 82541PI Gigabit, *** masked MAC ***, no carrier
OS: RHEL AS 4 U6, Linux 2.6.9-67.ELsmp x86_64, 64-bit
BIOS: Dell A02 08/23/2005
Hostname: **** masked hostname ****  


Client

For playback, I used a Windows box running Quicktime Player. For load testing, I used a different Linux box with profile similar to that of the server.


Steps to download, install and setup DSS 6.0.3
The following steps should work on both 32 bit and 64 bit Linux. Steps 3 and 4 use patch files that makes the DSS code build without errors on 64 bit. You can go ahead and apply the patches even if you are working on 32 bit, it is better to have portable code. On 64 bit, however, you must apply the patches, otherwise it won't build.

It is possible, however, that Apple guys change the code on macosforge server without changing the version number, and then these patches might not work. I always encourage to use the --dry-run option with the patch command to make sure that the patch will actually go through before patching it without the option.

Similarly, step 5 uses an "Install" script that is different from what will come with the download from macosforge server. The new install script has the necessary changes for 64 bit installation to go through nicely.

The 3 files that steps 3, 4 and 5 refer to are in the "Patches" directory of the source code.

Downloaded source tarball from http://dss.macosforge.org/ --> Darwin Streaming Server 6.0.3 --> Source code
tar xf DarwinStreamingSrvr6.0.3-Source.tar --> will create directory "DarwinStreamingSrvr6.0.3-Source"
patch -p0 < dss-6.0.3.patch - patch file included in uploaded source - patch makes it 64 bit compatible, run from directory containing DarwinStreamingSrvr6.0.3-Source
patch -p0 < dss-hh-20080728-1.patch - patch file included in uploaded source - patch makes it 64 bit compatible, run from directory containing DarwinStreamingSrvr6.0.3-Source
cp Install DarwinStreamingSrvr6.0.3-Source (new Install script file included in uploaded source - makes it 64 bit compatible, will overwrite existing Install script)
sudo groupadd qtss
sudo useradd qtss -g qtss
cd DarwinStreamingSrvr6.0.3-Source; ./Buildit to build - this will create the server binary in DarwinStreamingSrvr6.0.3-Source directory, along with some other binaries in some other subdirectories. "BuildIt" script comes with the download. Do not mind warnings. If build fails, you have to tweak this script. It is not that hard as it looks. Locate the line "case $PLAT" in the script. From that point on, it assigns some values to important compile/link variables depending on your $PLAT. $PLAT is a concatenation of `uname` and `uname -m` with a dot in between. Go ahead and find your $PLAT. Check if the Build script handles it. If you are using 32 bit linux, it should be "Linux.i686" or "Linux.i586". If you are 64 bit, it should be "Linux.x86_64". All these 3 cases are handled in the Build script - so you need to play around with the individual variables under that section if it is still not working. If your $PLAT is something else, and you think that the values from another case handler will work for you, go ahead and add a new case section and copy the lines from the one that works for you.
To Install: run sudo ./Install (You must be logged in as root to install Darwin Streaming Server)
During installation, it will ask: "Please enter a new administrator user name:" - type in some user name
It will ask "Please enter a new administrator Password:" - type in some admin password
After installation, DSS writes its binaries and files to various locations. Important directories are:
/usr/local/sbin: server executable (DarwinStreamingServer) and admin server (streamingadminserver.pl)
/usr/local/movies: default content directory (where streams are served from, already contains sample hinted streams)
/usr/local/bin: some other binaries, including "StreamingLoadTool" - a load test tool (discussed in the Performace/ Load Tests section)
/etc/streaming: streamingserver.xml (configuration file for DSS), streamingloadtool.conf (configuration file for the load test tool StreamingLoadTool), qtusers and qtgroups file (contains users and groups for DSS, at this point only the admin user created during installation will be present) and
/var/streaming/logs: DSS logs folder (StreamingServer.log, Error.log are the most important, also has streamingadminserver.log and mp3_access.log)
Run Server: cd /usr/local/sbin; sudo ./DarwinStreamingServer. 2 processes will run - the first one runs as root, and forks the main server as qtss. While doing a ps aux | grep Darwin, the one with the higher process id is the forked server process. If it crashes or gets killed, the other one immediately respawns another
Run Admin Server: sudo -u qtss /usr/local/sbin/streamingadminserver.pl. Target machine should have yapache installed and set up as a web server for accessing the administration console. If you are not allowed to impersonate qtss, just do a "sudo /usr/local/sbin/streamingadminserver.pl"
Access admin console from browser at: http://<your server name>:1220/parse_xml.cgi. Provide admin user id and password created during installation. Most of the parameters that you can change from the admin console write to /etc/streaming/streamingserver.xml
Play streams: Install apple's quick time player on any windows box that can resolve your linux machine's name, File->Open URL... and use rtsp://<your_server_name>/<any_sample_movie_from_/use/local/movies>. Note that DSS can only play "hinted" streams. Hinting can be done by Apple's tools, find out how to hint custom tracks if you want to play streams other than the sample ones
To stop server: kill the 2 server processes. If you only kill the one with higher pid, it will keep on getting re-spawned, so kill the lower one first. If you use -9, it results in the server not calling the cleanup modules, do not do that as a rule.

Before you start writing your own modules to customize DSS...
This is the fun part for C++ developers. Before attempting to create your own module, you have to understand the architecture of the server. The architecture is pretty decoupled, as the entire server is already written with various modules strung together - many of the core server functionalities are implemented as modules.

The core server code is in the Server.tproj folder. After starting up, the server loads modules. Modules are either static or dynamic. Static modules are built when the server is built. When the server runs, it loads the dynamic modules first. It looks for shared libraries (unix so files, only that they are not named with so extension) under the directory /usr/local/sbin/StreamingServerModules. After that, it loads the static modules. The code for all modules that already come with the server are in APIModules directory.

If you just want to add custom functionality without tampering the actual server code, it is easiest to write and build a dynamic module - all you need to do is build the dynamic module as a shared object and place the binary in /usr/local/sbin/StreamingServerModules directory. Restarting the server will result in loading of the dynamic modules from this directory, including yours.

Roles
You must understand Roles in order to start creating your own modules.

Each module must register itself in one or more "role"-s. There are many roles: Register, Initialize, Filter, Route, RTSP Pre-Processing, RTSP Request, RTSP Post Processing, RTP Roles, RTSP Authentication Role, RTSP AUthorization role, Error Logging Role, etc. Think of roles as the building blocks in a flowchart. The Server first invokes the Register role. Followed by Initialize Role. Then it sits quietly, waiting for requests. Once it receives a RTSP Request, it invokes the Filter Role, and passes it the entire URI for "filtering" - modules subscribed to this Role have the privilege of getting a writable copy of the RTSP request: it can actually change any part of the request! The server then invokes the Route role - modules subscribed in this role gets a writable copy of the location (folder) from where to serve the content, and can effectively change the serving point (thereby re-routing the request to a new point). And so on.

The sequence in which the server invokes its roles is fixed (coded in the server). More than one modules can subscribe to a role and a module can subscribe to more than one roles. Some roles are exceptions - only one module can register for them. This "subscription" happens inside the code handler for the Register Role - all modules must register to this role. When called upon in this role, a module tells the server what other roles it is subscribing to.

Pre-requisite - QTSSAPIDocs.pdf
Read the QTSSAPIDocs.pdf file, however boring it might be. Though it is available in the Documentation directory, I have uploaded it along with the source zip just to be handy. It is the single most important document amongst all others in the Documentation directory. It is the bible for module developers. Unfortunately, it leaves many things to be desired. It is organized, but messily. Still, this article cannot substitute reading this document. This article is like a compass - it will guide you, give you a better bigger picture, but the nity gritty of code must refer to this pdf file. Come back and read the rest of this article again once you are done with QTSSAPIDocs.pdf.


Let us create a custom Auth Module
Let us imagine a likely scenario: you want to allow/decline requests based on a logic that takes into account the connection url (which would be the case if you force your users to supply some specific signature as a query string) and client IP address (which would be the case if you want to restrict access to some subnetwork). You would need a custom Auth module. 

Authorization/ Authentication in DSS
How does DSS handle Authentication and Authorization? And how is Authorization different from Authentication?

I chose the example of a custom Auth module because this will bring us to the topic of how DSS handles authorization/ authentication. It does a fairly good job at whatever basic scheme it supports - but that is, at best, basic. It uses a rudimentary mechanism, and it might not suffice for your purpose. Let us review what DSS provides us with.

Right after installation, all users can access all movies from the movies directory. But say you want to set up n user accounts in such a way that each user will have access to his or her own content only. See http://dss.macosforge.org/ --> "Administrator’s Guide" to see how to add user accounts. Basically, it boils down to the fact that each user will have a subdirectory under the movies directory (/usr/local/movies - the main movies directory). Each such subdirectory will have a "qtaccess" file present, which will list the users that can access that given subdirectory. The master list of all users, along with their credentials are stored in /etc/streaming/qtusers file (password is stored in encrypted format), which only linux user qtss can access.

Now, when a given user wants to play a file, he/she can send user id and password as part of the RTSP request. The process of validating that pair of user-id and password against the master list in qtusers is called Authentication. So the output of Authentication process is "yes this is a valid user id - password pair" or "no it is not". Simply passing Authentication does not guarantee that the user will be able to play the requested file, because the user may still not be listed in the qtaccess file of the subdirectory where the requested content lies. In other words, passing Authentication means "you are a valid user of the system". It does not mean "you have access to the requested file". Authorization is the other half, which is the process of allowing the user access to the requested content. Authorization may fail due to a variety of causes, including the scenario described above: the user is not listed in the qtaccess file. So bottomline: a user will pass Authentication if he/she supplies the correct password, but still will not be able to play the stream (more generically, access a resource) if he/she fails Authorization, which may happen due to a variety of causes.

Needless to say, the server calls "RTSP Autheticate Role" before "RTSP Authorize role".

Interestingly and correctly, DSS enforces that only one module can subscribe for "Authenticate" Role, but unlimited number of modules can subscribe for "Authorize" role. That corroborates our conclusion above: Authentication will fail only under one circumstance: if you are an unknown user. But Authorization may fail under various situations, and there may be multiple modules, each verifying some aspect of Authorization. Thus, when the server calls the module subscribed for "Authenticate" role, it expects that module to validate the user id/ password. If that passes, it then invokes all the modules signed up for "Authorize" role - and each one of them can then apply custom rules to either approve or deny that RTSP request. For example, an Authorize module may deny the request because of too high bandwidth or too many number of open connections. Such custom authorization must be implemented as separate modules subcscribing to the Authorize role.

Implementing a completely different auth scheme
This mechanism of validating users (looking for them in qtusers file and so on) is implemented as a static module (you guessed it - that's the good thing guys - core features of DSS are simply modules). It is QTSSAccessModule. Go ahead, look at its code from the APIModules directory, getting your feet wet is always a good thing.

If you see the code of QTSSAccessModule, you will see that it has registered to the Authenticate Role (look in QTSSAccessModuleDispatch method). Of course, in addition to that, it is also regsiered to 5 other roles, including the Authorize Role. But the fact that it is registered to Authenticate role is significant, because Authenticate Role is one of the roles which only one single module can register to. So if you want to register another new module to Authenticate role, it's not gonna work!

So if we were to married to the idea that our new module must register to Authenticate role, we would be forced to retire the QTSSAccessModule. That may be okay, but it is not safe to write off the core modules of DSS just like that. Who knows what else that module must be doing? Anyway, QTSSAccessModule is a static module, it is not dynamic - so getting rid of it means getting the hands dirty with server code. Alternatively, we can edit QTSSAccessModule and write our code inside that module. Again, something that is not elegant.

For the kind of problem we are trying to solve, Authorize Role fits the bill perfectly.

One way to suppress whatever QTSSAccessModule does is not to have any qtaccess file anywhere. Which means all users, including anonymous users (those who do not supply any username or credentials while submitting the RTSP request), are allowed access to all content. QTSSAccessModule will pass all requests. Now we can add a custom module implementing Authorize role, and add the custom logic to allow/ deny in there. This is a simplistic approach indeed, designed to solve our fabricated needs. This is to just give you an idea - your situation will surely warrant a slightly different solution.

Writing the code
A word on your reference copy: the entire code for QTSSMyAuthModule, our glorious first DSS module, is inside the source zip: as a directory that you can just copy to APIModules folder, alongside existing modules.

Building an existing module as dynamic library
Before starting to write the code for a new module, you should learn how to build (make) an existing module - how to build only a module (not the whole server) as a shared library so that you can simply drop the module binary into the /usr/local/sbin/StreamingServerModules directory.

With some tweaking of the build scripts, I came up with one that I could copy directly to the subdirectory containing the code for the module. Each subdirectory under APIModules directory already contains a Makefile.POSIX - but that cannot be used directly, as it expects some exported variables that the global build script (remember "Buildit"?) populates before invoking it. But to build a module separately, we need a local build script that will enable us to similarly use the local Makefile.POSIX. On top of that, I tweaked it some more to output a shared library, not a static one. The result is "build" - refer source zip/QTSSMyAuthModule.

Try it on an existing module first, say QTSSHomeDirectoryModule. Simply copy the "build" script I have uploaded to QTSSHomeDirectoryModule folder. Edit the script's final echo statement (also you might need to run chmod to make it executable) to print out the correct module's name, and you should be done. Run "build clean" first and then run "build", and see the object files and the binary called "QTSSHomeDirectoryModule" getting created.

Now copy that binary to /usr/local/sbin/StreamingServerModules. Restart the server. Look at /var/streaming/logs/Error.log at this point, and it should have freshly logged a list of modules the server just loaded. For each module, it specifies whether it is static or dynamic. If you are successful in building QTSSHomeDirectoryModule, it should have it listed as loaded and dynamic. Repeating the experiment by removing the binary from /usr/local/sbin/StreamingServerModules will result in that line of log not appearing.

Time to create QTSSMyAuthModule
Copy any existing module (The following tutorial assumes you have used QTSSHomeDirectoryModule) source code in a new subdirectory "QTSSMyAuthModule" and change all references to the old name to new name.

There are 2 roles that QTSS Home Directory Module handles which we do not need to: RTSPRoute Role and ClientSessionClosing Role. I am not going to explain all these roles here, please read the bible for that. Remove the code handling those roles. The other 4 (Register, Initialize, RereadPrefs and RTSPAuthorize) we need. And we need an extra role that is not in there: Shutdown Role, so add code for that one (see uploaded source zip).

Let me give you a quick idea of the roles we will handle in QTSSMyAuthModule.

Register, Initialize and Shutdown: Register is mandatory (this is when you tell the server what other role you plan to register to). Initialize is a good idea to have - e.g., I added a new class in a separate H - CPP file pair, and I needed a static object of my class type. So I created an instance in Intialize (by calling new() or QTSS_New()). Naturally question of cleaning up this memory arises. In such a case where we need to call cleanup code at the end, we must subscribe to another role: Shutdown Role, so that our module is called when the server is shutting down.

RereadPrefs: RereadPrefs is also good to have especially if you want your new modules to be controlled by configuration settings. Adding configuration settings is very easy: just edit /etc/streaming/streamingserver.xml and add, under <CONFIGURATION>, a new node: <MODULE NAME="<Your module name, in this case QTSSMyAuthModule>> with as many configuration parameters as needed. For example, I wanted a master setting which enabled/disabled by auth plugin, so I added <PREF NAME="enabled" TYPE="Bool16">true</PREF>. Once the setting is added, the handler for RereadPrefs Role should simply read in the value of the setting using QTSSModuleUtils::GetAttribute(...) (see my code) Having the module subscribe to RereadPrefs Role simply ensures that your module can read in changed settings without a server restart.

RTSPAuthorize: For RTSPAuthorize handler, I modeled my code after how QTSSHomeDirectoryModule handles the Authorize Role. I noticed that the server calls my module several times for playing back a single stream - and I only need to authorize once (the first time) to be efficient. On all subsequent calls, I have to figure out that this rtsp session is already authorized. A custom attribute added to the RTSPSession object helped me to do that, and the technique is clearly implemented in the existing code of QTSSHomeDirectory code. As part of my implementation of Authorize role, I had to extract the RTSP URL and Client IP for the request. Combing through the code of existing modules, I found URL extraction technique from QTSSSplitterModule.cpp (see QTSSReflectorModule subdirectory) - but the string extracted there did not start from "rtsp://" but only from the filename part. Scanning QTSSAPIDocs.pdf, I found lists of all supported attributes for the standard objects, and located one that contains the URL in the format I need.

Thus, as you see, no article can show you the exact technique you need to know - there is an ocean of information among the code of existing modules and QTSSAPIDocs.pdf, though scattered and difficult to locate. How quickly you find the technique you need from the existing code base and/or the documentation depends how much back you broke.

Using your own classes
I will refrain from explaining each line of my code of QTSSMyAuthModule - the place where the exact authentication is performed is full of running comments. I separated the functionality of allowing/denying to a separate class - MyAuth.cpp, MyAuth.h.

I have seen that I could not use STL classes and methods directly into QTSSMyAuthModule.cpp (the main template file that houses the mandatory methods a module must have) - they conflict with some header file DSS uses. That should be fixable with some effort, but I opted for the easier route: if I follow my scheme of including a separate class, I can use STL in MyAuth.h/cpp without any problem.

How configuration works
/etc/streaming/streamingserver.xml is precise and clear. It has all the server's settings listed under /CONFIGURATION/SERVER, followed by the settings for all the modules, each with node /CONFIGURATION/MODULE@NAME . Changing these settings do not need a server restart - only sending the HUP signal to the server process (one with the higher pid). For a custom dynamic module to re-load preferences at the same time when the server does (in response to HUP signal), it must implement the RereadPrefs Role.

How logging works

Error Logging
Though DSS has all modules in the APIModules directory, I found one that is hidden in Server.tproj folder - QTSSErrorLogModule. That is probably because this module is deemed an integral part of the server itself, and becasue it is so important. It is because of this module that any other module and the sever itself can write to the Error.log file located in /var/streaming/logs.

Even informational logs can be simply written to the Error.log file with verbosity parameter = qtssDebugVerbosity, and it won't actually show up unless /CONFIGURATION/SERVER/error_logfile_verbosity is set to 4 (qtssDebugVerbosity). The possible values of this configuration setting is: 0 (Fatal Verbosity), 1 (Warning Verbosity), 2 (Message or INFO Verbosity), 3 (Assert Verbosity) and 4 (Debug). Whatever the value is set to, logs written with verbosity parameter equal to or less than that value will actually get written. Writing a log with Fatal Verbosity causes the server to shut down.

In order to write to the error log stream from anywhere in a custom module, we need to save the error log stream in a static QTSS_StreamRef object in the Initialize handler method, where the parameter passed to the handler has a reference to the stream. Please refer to my code.

Stream Request Logging
The module QTSSAccessLogModule does this for us: it logs a bunch of statistics for every rtsp session closing/ playback stopping (to /var/streaming/logs/StreamingServer.log). Unlike flash media server, it does not trap pause/unpause. Any change/addition to the exact fields being logged should be easily achievable by tweaking the code of this module. The streamingserver.xml file has all the configurable parameters for this module in the corresponding MODULE node.

Server monitoring/ Heath check support
DSS has a cool feature: turn on the entry "enable_monitor_stats_file" in streamingserver.xml, and it will output a stats file to the same folder as Error.log is written to (which is /var/streaming/logs, but you can change it using the "error_logfile_dir" entry in streamingserver.xml). This stats file is refreshed every X seconds, where X is "monitor_stats_file_interval_seconds".

Worth mentioning here is my encounters with this monitoring feature: the file has XML format, so I decided to monitor my server using a browser. But somehow, DSS writes some gibberish to the top part of the stats file, which makes the XML malformed, so the browser cannot render it. Also, the permissions of the file is set such that not everybody can read the file. I also wanted to output the file anywhere I wanted (using the "monitor_stats_file_name" entry), but DSS is written to expect only the file name from that entry, not the entire path. My adventures to fix this led me to Server.tproj/RunServer.cpp, LogStatus() function, where I could fix all these. Now I output my stats file to /tmp, and my apache server serves the file fine, because I have created a sym link to the real file from apache's htdocs directory. And it is pure XML now, no gibberish.


Performance and Load tests
I am not happy with the load tests I ran on DSS. It cannot take thousands of concurrent users. Someday I might take the time and effort to try to fix (read improve) that in the code, but let me first point you to how I ran these tests.

StreamingLoadTool
DSS comes with a load test tool, /usr/local/bin/StreamingLoadTool. Pass -v to it to know about the other parameters it takes. I used the -f <configuration file> parameter mostly, and used the config file to specify everything else. The sample config file is located as /etc/streaming/streamingloadtool.conf.

I could successfully run load tests from a different client machine - all it takes is to copy the load test binary and the configuration file over.

I found that the StreamingLoadTool that comes with DSS 6.0.3 crashes when I run it with runforever=yes and concurrent number of clients=100. Pfft.

openRTSP
A better tool to load test DSS is openRTSP from www.live555.com. If you want to build it from source, you have to download the entire live555 streaming media source code. I went ahead and followed their instruction, and it went smoothly. You will end up with a "live" directory at the end - it's your choice where to put it, but that contains everything. openRTSP, the command line RTSP client tool, is inside testProgs folder. It has excellent documentation, so I will refrain from explaining how it should be used. I wrote a script that spawned lots of openRTSP sessions, each with -c option (for continuous play), -r (do not render video) and -p 80 (to redirect the video to that port), and I controlled the number and rate of spawning.

Results
The result is that DSS, with all optimizatoins I could think of (increase the maximum number of open file handles in your system to a gigantic number using /etc/security/limits.conf, set "maximum_bandwidth" and "maximum_connection" in streamingserver.xml to -1, "run_num_threads" to various values, including 0 for auto-detection of number of cores) - DSS cannot handle more than 350 concurrent users.


# of concurrent users CPU (Forked Server) CPU Idle (Overall) Memory (Server Process) Stable? Bandwidth (MBPS)
100 1% 99% 4 MB yes 3.75
200 2% 97% 7 MB yes 7.5
300 3% 96% 12 MB yes 11.25
350 4% 96% 21 MB yes 13.125
400 4% 96% 22 MB no  - 

The bandwidth numbers above is a straightforward calculation considering I was streaming 300 kbps files. DSS sample files also comes with 1000 kbps files and high definition files, I am sure these files will result in much higher bandwidth, but performance and latency using the high bitrate files was not measured in my experiments - I will await feedback from readers on how that went.

I did not notice any memory leaks - in stable mode, it ran constantly for weeks, processing 350 continuous and concurrent users, with no leaks perceived.

Running multiple DSS servers for scalability
For some, that could be enough. I needed more. There is a solution though - even when DSS topples over with 400 users, it uses virtually no CPU & memory (which indicates that the bottleneck is somewhere in the code, and that makes me bolder when I think that it can be fixed). This "leanness" can be effectively used by running multiple DSS servers!

While executing the server, we can pass -c <whatever streamingserver.xml we want>. If we create multiple streamingserver.xml files, each with a different port listed in "rtsp_port" entry (like 7070 in one, 554 in another, and so on), and then execute multiple DSS instances each using a separate streamingserver.xml as configuration file, we can achieve this. I have parallelly run 3 instances of the server, each processing 350 concurrent users - that is one way to reach four digits - 1000 users!

Only caveat is the url you pass to openRTSP (or whatever client you are using) must access the correct instance of the server using the port number specified using a colon after the hostname. If you are thinking of using this setup in real world, you have to write another application - a software load balancer, that will listen on any given port, and delegate the request to any of the N running DSS instances. Clients then must connect to that port always. I have not yet tried this thing out though.


RTSP, RTP, UDP, RUDP, TCP...
While dealing with Darwin Streaming Server, you will come across these acronyms. At times you will have to choose one over another. For example, while setting up StreamingLoadTool (the load test tool that comes inherently with DSS), its configuration file has entries where you pick the mode of execution - RUDP? UDP? TCP? You can retain the defaults if you are not sure what to pick.

Explaining the differences and meanings of these is beyond the scope of this article. May be in a future article. I mention these here because you need to understand these concepts to get the most out of DSS. If you are only setting up a server for your users to use - you may ask - why do you care whether it is doing RUDP or UDP? At the beginning, you might not need to care. But if lots of users are using your service, you may be asked which one gives higher throughput. They may be using clients of varying capabilities, and ones that gives it users the option to pick a mode.

For absolute beginners, just inderstand this much: RTSP is the protocol of delivery (corresponds to HTTP). RTP is the underlying protocol used for packets transfers by RTSP. TCP and UDP are alternate network protocols - TCP is characterized by high reliability but low timeliness, as it involves a lot of handshaking (ACK-s, NACK-s etc). UDP is characterized by low reliability (packets may not make it to the other end) but high timeliness. R-UDP is Apple's proprietary version of UDP that adds some handshaking, thus they call it "Reliable UDP". Here is an article that attempts a better explanation: http://www.vbrick.net/Topics/RTSP.asp.

You may ask why does DSS need to deal with these concepts? Any streaming server would, as its sole job is to deliver a streaming media to the client - which, as opposed to playing back a file, is characterized by the fact that nothing is stored on the client side. Bits must be transmitted exactly at the speed that the video being played demands - a lighter scene may need 100,000 bits per second, and a darker scene may need 700,000 bits per second. This number is called the bitrate of the video. Coordinating this transfer, where the speed goes up and down, ensuring reliability and handshaking is the complex domain of streaming servers. And it cannot be solved by HTTP, as HTTP can only fetch the whole file (it can also fetch partial files based on byte range requests) but it cannot fetch a file continuously over the same connection. RTSP, RTMP, etc are protocols used for this purpose.


Conclusion
Apple guys have coded this well. A reviewer, on afterthought, can always point out areas of improvement, but the project is so massive that finally ending up with a coherent, working and mostly bugfree set of files of this magnitude is a commendable achievement.

Usage of so many static variables is something I do not like - it exerts pressure on the heap. And amateurishly conceived utility classes like StrPtrLen literally drives me mad - these guys have created a class to hold a pointer to sombody else's data! Please be extremely careful while using this almost criminal class - it invites misuse. Overall, the code could have been neater, but hey, I did not pay for this. I will take what DSS is worth for.



License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author
Koushik Biswas




Member  Designing complex systems to get the highest throughput is what Koushik loves and does at Yahoo Video Streaming Center. First love is C++, but that list has grown over time to include C# and Java. He has been working with Unix systems at Yahoo, but his prior experience with Windows allows him to seamlessly switch contexts. If he ever writes a book it will be either on Object Oriented Design or Evolution of Psychology - two topics which, he finds, are under-represented in the literary world.

An Electronics Engineer from Jadavpur University, he is father of twin daughters. Apart from spending time with work and projects, he loves cricket, Kishore Kumar, Bruce Willis, outdoor hiking, camping and fishing, Mediterranean food, sea beaches and his gas Grill.
Occupation:  Architect
Company:  Yahoo! Inc
Location:   United States

分享到:
评论

相关推荐

    U盘量产工具SM3280&3281&3282-AvidiaV0209整合版

    U盘量产工具FLASH量产工具SM3280&3281&3282-AvidiaV0209整合版

    java课程期末考试.zip

    java课程期末考试

    分布式消息中间件,参考kafka,未完成.zip

    分布式消息中间件,参考kafka,未完成

    修木工施工规范及流程.docx

    修木工施工规范及流程.docx

    汽车电子中MICROSAR OBD协议栈解决方案及其应用

    内容概要:本文详细介绍了VECTOR提供的MICROSAR OBD协议栈解决方案,涵盖了OBD模块、ECU支持、监控功能和服务请求等方面的内容。此外,还讨论了OBD在不同国家和地区的技术标准与法规要求,以及MICROSAR OBD解决方案的优势,如适应不同项目的需求和高度集成于AUTOSAR 4平台。 适合人群:汽车电子工程师、软件开发者、汽车制造商及相关行业从业人员。 使用场景及目标:① 适用于车辆诊断系统的开发和维护;②帮助工程师理解和掌握OBD协议的具体实施方法和应用场景;③ 提供了一个成熟、可扩展的解决方案,用于满足OBD相关标准和法规的要求。 其他说明:本文不仅提供了技术层面的详细解析,还探讨了实际操作过程中可能遇到的问题和解决方案。同时强调了屏蔽信息过载的重要性,提醒工程师保持内心平静,专注做好本职工作。

    适用于 Python 的 LINE 消息 API SDK.zip

    适用于 Python 的 LINE 消息 API SDK适用于 Python 的 LINE Messaging API 的 SDK。介绍适用于 Python 的 LINE Messaging API SDK 可以轻松使用 LINE Messaging API 开发机器人,您可以在几分钟内创建一个示例机器人。文档请参阅官方 API 文档了解更多信息英语https //developers.line.biz/en/docs/messaging-api/overview/日语https://developers.line.biz/ja/docs/messaging-api/overview/要求Python >= 3.9安装$ pip 安装 line-bot-sdk概要用法from flask import Flask, request, abortfrom linebot.v3 import ( WebhookHandler)from linebot.v3.exceptions import ( InvalidSig

    Java字节码工程工具包.zip

    Java字节码工程工具包Javassist 版本 3版权所有 (C) 1999-2023 Shigeru Chiba,保留所有权利。Javassist(JAVA 编程助手)使 Java 字节码操作变得简单。它是一个用于编辑 Java 字节码的类库它使 Java 程序能够在运行时定义新类并在 JVM 加载类文件时对其进行修改。与其他类似的字节码编辑器不同,Javassist 提供两个级别的 API源代码级别和字节码级别。如果用户使用源代码级别 API,他们可以编辑类文件而无需了解 Java 字节码的规范。整个 API 仅使用 Java 语言的词汇表进行设计。您甚至可以以源文本的形式指定插入的字节码Javassist 会即时编译它。另一方面,字节码级别 API 允许用户像其他编辑器一样直接编辑类文件。该软件根据 Mozilla 公共许可证版本 1.1、GNU 宽通用公共许可证版本 2.1 或更高版本或 Apache 许可证版本 2.0 分发。文件README.md 此自述文件。Changes.md 发行说明。License.html 许可证文件。tuto

    毕设源码-基于python的西西家居全屋定制系统的设计与实现_ijsj--论文-期末大作业+说明文档.rar

    本项目是基于Python语言开发的西西家居全屋定制系统,旨在为家居行业提供一个高效、智能的定制解决方案。项目涵盖了从客户需求分析、设计方案生成、材料选购到最终订单生成的全过程,力求实现家居定制的数字化和智能化。 在主要功能方面,系统具备强大的客户管理模块,能够详细记录和分析客户的定制需求。设计模块则采用先进的三维建模技术,为客户提供直观、真实的家居设计方案预览。此外,系统还整合了丰富的材料数据库,方便客户根据自身喜好和预算进行材料选择。 框架方面,项目采用了B/S架构,确保了系统的稳定性和可扩展性。后端使用Python的Django框架,前端则结合了HTML、CSS和JavaScript等技术,实现了用户界面的友好和响应速度。 开发此项目的目的,不仅是为了满足家居行业对个性化定制的需求,也为计算机相关专业的学生提供了一个实践和学习的平台,有助于提升他们的实际开发能力。

    Javascript 是数字化创新的起点,是语言的基础,也是基本概念 .zip

    Javascript 是数字化创新的起点,是语言的基础,也是基本概念。Basecamp JavascriptJavascript 是数字化创新的起点,是语言的基础,也是基本概念。嵌套存储库,可作为启动项下待办事项的实践活动。

    已弃用 - Coinbase Python API.zip

    已弃用 — Coinbase Python APICoinbase Coinbase API V2的官方 Python 库。重要提示此库当前针对的是 API V2,而 OAuth 客户端需要 V2 权限(即wallet:accounts:read)。如果您仍在使用 API V1,请使用此库的旧版本。特征接近 100% 的测试覆盖率。支持API Key + Secret和OAuth 2身份验证。调用 API 的便捷方法 - 为您打包 JSON!自动将 API 响应解析为相关的 Python 对象。使用IPython时,所有对象都具有可制表完成的方法和属性。安装coinbase可以在PYPI上使用。使用以下命令安装pippip install coinbase或者easy_installeasy_install coinbase该库目前针对 Python 版本 2.7 和 3.4+ 进行了测试。注意此软件包名称过去是指George Sibble维护的非官方 coinbase_python 库。George 慷慨地允许我们使用此软件包

    基于RBAC权限控制的基础后台.zip

    基于RBAC权限控制的基础后台

    毕设源码-python-基于Python爬虫的网络小说数据分析系统的设计与实现-期末大作业+说明文档.rar

    本项目是基于Python爬虫的网络小说数据分析系统的设计与实现,旨在为计算机相关专业的大学生提供一个实践平台,特别是在毕业设计和项目实战练习方面。项目通过Python强大的网络爬虫技术,从流行的网络小说网站自动抓取数据,包括书籍信息、章节内容、用户评论等。 主要功能涵盖数据采集、数据清洗、数据存储和数据分析。数据采集模块利用Scrapy等爬虫框架高效抓取网页内容;数据清洗模块确保数据的准确性和一致性;数据存储则采用MySQL等数据库系统,便于数据管理和查询;数据分析模块通过Pandas、NumPy等工具进行数据处理和分析,生成多维度的统计报告和可视化图表。 此项目不仅帮助学生掌握Python编程和网络爬虫技术,还能让他们深入了解数据分析的全过程,提升解决实际问题的能力。同时,系统的实现和应用也反映了现代信息技术在文学创作和消费领域的应用价值和潜力。

    ssm框架Java项目源码-基于Java的在线日语培训平台的设计与实现+jsp毕设-大作业.zip

    本项目是一个基于Java的在线日语培训平台的设计与实现,采用SSM框架(Spring+SpringMVC+MyBatis)进行开发,旨在为计算机相关专业的学生提供一个实践和学习的平台,同时也为日语学习者提供一个在线学习的空间。项目中主要功能涵盖了用户管理、课程管理、学习资源上传下载、在线测试与反馈等多个方面。通过该平台,教师能够轻松管理课程内容和学生信息,学生则可以随时随地访问学习资源,参与在线课程和测试,从而提高学习效率和兴趣。 在开发此项目的过程中,我们重点关注了系统的可维护性和可扩展性,确保代码结构清晰,便于后续的功能迭代和优化。此外,通过使用SSM框架,实现了前后端的分离,提高了开发效率和系统的响应速度。该项目不仅能够满足毕设的需求,还能作为Java学习者提升编程能力和实践经验的实用工具。

    基于java的机票管理系统设计与实现.docx

    基于java的机票管理系统设计与实现.docx

    基于Java实现的数据结构设计源码学习指南

    该项目为《基于Java实现的数据结构设计源码》,共包含51个文件,主要由46个Java源文件构成,辅以2个文本文件、1个Git忽略文件、1个许可证文件以及1个XML文件,全面涵盖了数据结构设计的核心内容。

    绿色食品 水稻生产操作规程.docx

    绿色食品 水稻生产操作规程.docx

    这款出色的应用程序可以纠正您之前的控制台命令 .zip

    他妈的 Fuck是一款出色的应用程序,其灵感来自@liamosaur 的 推文,它可以纠正以前控制台命令中的错误。The Fuck太慢了吗?试试实验性的即时模式!更多示例➜ apt-get install vimE: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?➜ fucksudo apt-get install vim [enter/↑/↓/ctrl+c][sudo] password for nvbn:Reading package lists... Done...➜ git pushfatal: The current branch master has no upstream branch.To push the current branch and set the remote

    全国大学生FPGA创新设计竞赛作品 泡罩包装药品质量在线检测平台.zip

    全国大学生FPGA创新设计竞赛作品 “泡罩包装药品质量在线检测平台“.zip

    桃苗木质量基本要求表.docx

    桃苗木质量基本要求表.docx

    使用 Python 漂亮地打印表格数据,这是一个库和一个命令行实用程序 存储库从 bitbucket.org,astanin,python-tabulate 迁移而来 .zip

    使用 Python 漂亮地打印表格数据,这是一个库和一个命令行实用程序。存储库从 bitbucket.org/astanin/python-tabulate 迁移而来。python-tabulate使用 Python、库和命令行实用程序漂亮地打印表格数据。该库的主要用例是轻松打印小表格只需一个函数调用,格式由数据本身引导为轻量级纯文本标记创作表格数据多种输出格式适合进一步编辑或转换混合文本和数字数据的可读表示智能列对齐、可配置数字格式、小数点对齐安装要安装 Python 库和命令行实用程序,请运行pip install tabulate命令行实用程序将在 Linux 上安装为(例如tabulate)或者在 Windows 上的 Python 安装中安装为(例如)。bin/usr/bintabulate.exeScriptsC:\Python39\Scripts\tabulate.exe您可以考虑仅为当前用户安装该库pip install tabulate --user在这种情况下,命令行实用程序将安装到 ~/.local/bin/tabula

Global site tag (gtag.js) - Google Analytics