Wowza Media Server V3 for Amazon EC2

Wowza V3 pre-built AMIs are now available – The devpay licensing remains, as does the pricing. The new AMI listing can be found at the Wowza V3 for EC2 page. Wowza has also added pre-built AMIs for subscription licenses, which are priced at standard instance rates. The caveat is that on devpay, the premium add-on modules won’t be available – if all you’re doing is what you were doing on V2, that won’t change anything for you.

The license key instances can also be used as a basis for your own custom images. License key can either be manually changed or included in your startup packages.

Wowza V3 Costs for churches

In my previous post, I mentioned that Wowza’s licensing is changing for EC2-based instances. Naturally, this is going to have an effect on how much it costs. I’m going to break down the numbers for a typical church scenario.

The assumptions I’m going to make are based on usage patterns for Resurrection Online:

  • 1 full-time server
  • 2 services on Sunday, approx. 2 hours each,
  • 2 repeaters per service
  • 1000GB traffic/month
  • US-East zone

Under the current V2 scenario with DevPay, you have the following costs:

  • Full-time instance: 720 hours @ 0.15/hr : $108
  • Repeaters: 32 hours @ 0.15/hr : $4.80
  • Traffic: 1000GB @ $0.15/GB: $150.00
  • Wowza AMI Access: $5

Total: $267.80/month

Under V3, it looks like this:

  • Full-time instance: 720 hours @ $0.085/hr : $61.20
  • Monthly Wowza License: $55
  • Monthly nDVR Add-On License: $20
  • Repeaters: 32 hours @ $0.085/hr: $2.72
  • Daily Wowza License @ $5/day/repeater: $40
  • Daily nDVR License @ $2/day/repeater: $16
  • Traffic: 1000GB @ $0.12/GB: $120.00

Total: $278.92 ($314.92 with nDVR)

Pretty close… But because V3 is no longer tied to DevPay, you have the freedom of using reserved instances. I’ll assume you won’t do a reserved instance for the repeaters.

  • Full-time instance: 720 hours @ $0.03/hr : $21.60
  • Monthly Wowza License: $55
  • Monthly nDVR License: $20
  • Repeaters: 32 hours @ $0.085/hr: $2.72
  • Daily Wowza License @ $5/day/repeater: $40
  • Daily nDVR License @ $2/day/repeater: $16
  • nDVR License:
  • Traffic: 1000GB @ $0.12/GB: $120.00

Subtotal: $239.32 ($273.92 with nDVR)

  • Reserved instance fee – 1 year: $227.50 ($18.96/month) : $ 258.28
  • Reserved instance fee – 3 years : $350 ($9.72/month) : $249.04

Additional repeaters will set you back about $6/day extra ($8 with nDVR)

Summary:

  • V2: 267.80/month
  • V3: 278.92/month
  • V3 (1 year reserved): 258.28/month
  • V3 (3 year reserved): 249.04/month
  • nDVR on V3 will add $36/month

As you can see, the economics of this have been turned on their ear – now, instead of multiple small servers being the most cost-effective method of doing repeaters, it now makes sense to spin up one or two considerably larger instances for a couple of hours. If each small costs you $5.16  for two hours, and gains you 150Mbps, it looks a lot better to supersize your instance for about 9 bucks for two hours and get several gigabits out of it. When the folks at Wowza get done benchmarking the EC2 instances with V3, I’ll post another entry.

 

Hot off the presses: Wowza Media Server Version 3

** UPDATED INFORMATION : 6 November 2011 **

After many months of hard work, the team at Wowza put the finishing touches on the latest major release of Wowza Media Server. The new version adds a couple of key features in the form of licensed add-ons:

  • Network DVR:
    • Wowza nDVR AddOn (Beta release) is an innovative live stream cache that stores content in a normalized format accessible to Wowza Media Server 3 for any-screen HTTP playout. Compared to client-specific nDVR implementations, Wowza nDVR significantly reduces cost by minimizing network storage requirements and simplifying the delivery workflow for all screens. Wowza nDVR enables Wowza licensees to increase revenues and viewer engagement by delivering live linear streams as time-shifted services with features like live pause, rewind, and resume.
  • Live Transcoding:
    • Wowza Transcoder AddOn takes advantage of the same hardware as the server to transform incoming live streams from encoders, IP cameras, IPTV headends, and other live sources into multiple stream sets ‘done right’ for H.264-everywhere adaptive bitrate delivery. Adaptive bitrate streaming is supported for Flash RTMP and HTTP Dynamic Streaming, Apple HLS, and Silverlight Smooth Streaming. Wowza Transcoder also delivers non-adaptive streams over any transport protocol supported by Wowza Media Server 3, including RTMP, RTSP/RTP, MPEG-TS, and HTTP.
  • DRM Integration
    • Wowza DRM AddOn provides simultaneous secure key exchange with multiple DRM platforms such as Verimatrix VCAS and Microsoft PlayReady. Individual live or on-demand content is encrypted on-the-fly for HLS and Silverlight delivery to viewers on a wide range of devices including set-top boxes (STBs), connected TVs, smartphones and tablets. Wowza DRM AddOn can help users up-sell content for OTT premium services, and cross-sell content for multi-device distribution.

The other key feature is a change in the subscription license model, adding a daily license in addition to the monthly license. The subscription licenses are allowed to be used on Amazon’s EC2 cloud. The monthly subscription license has also seen a price reduction (there are also tiered price breaks on the monthly subscription). The subscription license is based on the number of instances you start during a given day or month. This is likely to be the new licensing model for EC2, moving away from Amazon’s DevPay model which required a monthly subscription as well as limiting instances to S3-backed images that couldn’t take advantage of Amazon’s reserved instances. By using a subscription license, you still get the scalability of the Amazon cloud, but the flexibility of using an instance type and OS that works for you. As of the release this week, there are no pre-built EC2 images for Wowza V3, but they’re coming soon. Wowza Media Server V3 Overview (PDF) Wowza Media Server V3 User’s Guide (PDF) Wowza Media Server V3 Pricing Wowza Media Server V3 Add-Ons

Grabbing thumbnails from Wowza

Here’s a quick and dirty way to grab a thumbnail from a Wowza application:

rtmpdump -v -B 0.01 -r rtmp://wowza.server/application/stream -o temp.flv
ffmpeg -i temp.flv -vframes 1 -s qvga /var/www/frame.jpg

There’s also a way to do it with a Wowza module, but it’s considerably more complex and not for the faint of Java.

Caveat: This likely won’t work if you have hotlink denial turned on on your stream.

 

Browser-aware player code, revisited

I posted a while back about selecting video players based on browsers… It was an ugly javascript hack, and since then LongTail has updated their excellent JWPlayer to support multiple methods. In order to create an embed that worked best for supporting both HTML5 and Flash players, I had to dig through the documentation a little bit, and combine a couple of different sections.

Here’s how to embed JWPlayer 5.7 to try flash first, with multiple bitrates, and then attempt HTML5 if Flash is not supported. This particular scenario is for iOS support.

<script type="text/javascript" src="jwplayer.js"></script>

<div id="container">Loading the player ...</div>

<script type="text/javascript">
        jwplayer("container").setup({
                height: 360,
                width: 480,
                image: "http://server.com/images/thumbnail.jpg",
                skin: "bekle.zip",
                modes: [
                        {
                        type: "flash",
                        src: "player.swf",
                        config: {
                                levels: [
                                        { bitrate: 250, file: "playlist-low", width: 320 },
                                        { bitrate: 500, file: "playlist-high", width: 480 }
                                        ],
                                streamer: "rtmp://streamer.com:1935/live",
                                provider: "rtmp"
                                }
                        },
                        {
                        type: "html5",
                        config: {
                                file: "http://streamer.com/live/ipad.smil/playlist.m3u8"
                                }
                        } ]
                }
        );
</script>

This still doesn’t support RTSP and other HTML5  fallbacks due to limitations in JWPlayer, so if you’re on a BlackBerry, you’ll still need to switch the player. The order that the “type” statements appear in the javascript determines the order in which they’ll be tried. Generally, you’ll want to try Flash first, otherwise browsers that support HTML5 but not Apple’s HTTP Live Streaming (which is pretty much all of them), will default to the HTML5 player, but be unable to get the stream. You can, however, provide multiple video sources with different codecs (for on-demand content) to support the different flavors of browsers, though.

Kicking Skype Up a Notch

A few weeks ago, our Senior Pastor asked for some assistance with setting up a skype video conference so that Adam could participate in a meeting being held in Texas. The alternative was to have him fly down to Dallas for a 1-hour meeting, effectively blowing out an entire day of productive hours.

We don’t currently have a dedicated video conference system, so we had to improvise.

We scheduled the meeting in our studio and coordinated with the other end to make the conference happen via Skype.

On our end, we took Adam’s MacBook Pro, and hooked up a Canon XL2 via FireWire for the video, a Shure wired lapel mic hooked to the camera (for phantom power) and then the audio output from the camera into the MacBook’s line-level audio input (because it appears that Skype doesn’t recognize the audio device on the XL2). We then connected the audio and display output from the mac into a 40″ LCD TV.

Here’s what it looked like:

The end result is a conference that looks and sounds excellent.

Inside Wowza Startup Packages

Startup packages are one of the more useful features of Wowza Media Server for EC2 – they allow you to custom-configure a system for rapid scaling and provisioning. Wowza provides several starter packages to build on.

A startup package is a file (up to 16384 bytes in size) that’s passed to the instance through the –user-data-file parameter on the API tools (if you’re uploading it via the AWS Web Console, you’ll need to encode it to Base64 and paste it into the text box) . There are a few ways that the data can get into the instance, which Amazon documents over here. For a generic EC2 instance, this can be anything, from text to binary data, depending on what the processing on the target instance is set up to do. In the case of Wowza, it’s a zip file with a specific structure. Much of this is digested from the Wowza for EC2 guide.

File Contents

A startup package for EC2 contains the following:

  • startup.xml (startup manifest)
  • tuning folder
  • wowza folder
  • Any other folders referenced in the startup manifest

A startup package is limited to a maximum of 16KB.

Startup activities are logged to /usr/local/WowzaMediaServer/logs/wowzamediaserver_startup.log. This is a good place to look if it’s not behaving as expected. The package is unpacked to /opt/working.

Startup Manifest

This file controls the startup processing for instantiating a Wowza server on Amazon EC2. It allows three commands: Install, Download, and RunScript.

Download

The <Download> command will download content from a web server and save it to the local Amazon instance. The <Download> command includes the following elements: URL, Data, Header, Destination, and Action:

<Download>
<URL>[URL]</URL>
<Data>[data]</Data>
<Header><Name>[key-name]</Name><Value>[value]</Value></Header>
<Header><Name>[key-name]</Name><Value>[value]</Value></Header>
<Destination>[relative-or-absolute-file-path]</Destination>
<Action>[UNZIP, INSTALL]</Action>
</Download>

The only two required elements are <URL> and <Destination>. To download a file from the url http://www.mycompany.com/myfile.zip, save it to the local machine at the location /opt/myfile.zip and unzip the file after download, the command is:

<Download>
<URL>http://www.mycompany.com/myfile.zip</URL>
<Destination>/opt/myfile.zip</Destination>
<Action>UNZIP</ Action >
</Download>

When completed, the contents of the zip archive are located in /opt.

One use of the <Download> command is to work around the 16kB startup package size limitation. For example, if you need to add several .jar files into the Wowza Server “lib” folder and these files push your startup package size over the 16kB limit, you might package these files into a separate zip archive. You can then host this zip archive on a web server and use the <Download> command to install the files into the Wowza Server “lib” folder.

It’s important to remember that the zipfile path structure is critical. If it uses no paths, you’ll need your destination to be where it ultimately lives, either in the staging area, or the absolute path to the Wowza install. When creating the zipfile with relative paths, create the path tree as if you were in the Wowza installation root.

URL

The <URL> is the URL of the file to be downloaded. The download can be performed over SSL by starting the url with https:// rather than http://. The url can also contain query parameters. The file will be downloaded using the GET method unless <Data> is specified.

Data

The Data is text data that will be included as part of the body of the HTTP request. You can use post data to send user name and password information to your web server so you can protect your content.

Header: <Name> and <Value>

The <Header> elements are name value pairs added to the header part of the HTTP request. An example would be:

<Header>
<Name>Content-type</Name>
<Value>text/plain</Value>
</Header>

Destination

The <Destination>element is the path to which the file will be saved (including the filename). This path can be relative or absolute. The base directory when calculating a relative file path, is the root directory of the startup package (the folder that contains the startup.xml file).

Action

The <Action> element is the action performed after the file is downloaded. The action can either be UNZIP or INSTALL. If the action is UNZIP the downloaded file will be unzipped using the unzip command. If the action is INSTALL the downloaded file will be unzipped and the contents of the folder will be installed (copied) into the Wowza Server installation folder.

Install

The <Install> command will copy the contents of a folder into the Wowza Server installation folder. The <Install> command can either contain a single <Package> element or single <Folder> element.

<Install>
<Package>[path-to-package]</Package>
</Install>
<Install>
<Folder>[foldername]</Folder>
</Install>

The Package path can reference an external URL, like http://wowzamediasystems.s3.amazonaws.com

The Folder path can reference either a relative path (relative to the root of the startup package where Startup.xml is located) or an absolute path on the local file system.

RunScript

The <RunScript> command will execute a script on a running Amazon instance.

<RunScript>
<Script>[relative-or-absolute-file-path]</Script>
<Param>[parameter]</Param>
<Param>[parameter]</Param>
</RunScript>

Script

The <Script> element is the path to the script file to be executed. This path can be relative or absolute. The base directory when calculating a relative file path, is the root directory of the startup package (the folder that contains the startup.xml file). This can refer to a script, or be a single-line command.

Any files referenced in the script need absolute paths or a path relative to the startup package root.

Param

The <Param> elements are parameters that will be passed to the running script. For example the following <RunScript> command:

<RunScript>
<Script>scripts/copyfile.sh</Script>
<Param>filea.txt</Param>
<Param>fileb.txt</Param>
</RunScript>

Would be the equivalent of executing the command:

./scripts/copyfile.sh filea.txt fileb.txt

Environment Variables

The following environment variables are available to scripts launched by the startup processor:

AWSEC2_METADATA_INSTANCE_ID - Amazon instance id
AWSEC2_METADATA_SECURITY_GROUPS - Security group
AWSEC2_METADATA_LOCAL_IPV4 - Local IP address
AWSEC2_METADATA_AMI_LAUNCH_INDEX - Launch index
AWSEC2_METADATA_PUBLIC_HOSTNAME - Public host name
AWSEC2_METADATA_PRODUCT_CODES - DevPay product code
AWSEC2_METADATA_INSTANCE_TYPE - instance type (m1-small, m1-large, m1-xlarge)
AWSEC2_METADATA_HOSTNAME - Public host name
AWSEC2_METADATA_LOCAL_HOSTNAME - Local host name
AWSEC2_METADATA_PUBLIC_IPV4 - Public IP address
AWSEC2_METADATA_AMI_MANIFEST_PATH - S3 manifest path
AWSEC2_METADATA_RESERVATION_ID - Instance reservation ID
AWSEC2_METADATA_AMI_ID - AMI ID

Wowza Folder

The Wowza folder in the startup package is meant to mirror the Wowza installation folder on the server. When the Install command in the startup manifest is invoked with this folder, the file structure of this folder will be copied to the installation folder.

Applications Tree

Contains folders for each Wowza application configured. There are not typically any files in this tree, just folders.

Conf Tree

Contains the configuration files for the server (at the root of the tree) and for each application (in folders matching the applications tree

Content Tree

Contains any content referenced by the applications. This is where SMIL files, stream schedules, and such go. Any audio/video content that goes here won’t fit in the startup package and will need to be downloaded separately.

Lib Tree

Contains any additional modules for the server.

Tuning Folder

This folder contains tuning scripts that tune the Wowza server.  It copies the requisite environment variables and tuning commands to a script executed as part of the Wowza startup, which happens after the startup processor is run.

Scripting

The following useful linux tools are available on the standard EC2 build (based on Fedora):

  • MySQL client commands
  • zip/unzip, bzip/bunzip, gzip/gunzip
  • s3fs
  • Compiler and build tools
  • WGet
  • RSync
  • SSH commands
  • dos2unix/unix2dos
  • curl
  • perl 5.8.8 (with MySQL support)
  • GPG
  • Shells: bash, sh
  • RRDTool
  • PHP5
  • EC2 API tools

Services

The following services are available on the EC2 build of Wowza, in startup order:

  • SNMP
  • SSH
  • FTP (Anonymous access to /var/ftp/)
  • MySQL (default password = “password”)
  • Wowza
  • Apache 2 – port 8080, content in /var/www/html
    • Cacti
  • Java Console

Startup package scripts and data are invoked by the Wowza startup script. If you modify any applications started prior to that, you’ll need to restart them.

Example

Here’s what my startup.xml looks like:

<Startup>
 <Commands>

 <!--
  Comments
  -->

 <Download>
  <URL>http://webserver/wowza/wms-plugin-collection.zip</URL>
  <Destination>wowza/lib/wms-plugin-collection.zip</Destination>
  <Action>UNZIP</Action>
 </Download>

 <RunScript>
  <Script>scripts/mount-s3.sh</Script>
 </RunScript>

 <Install>
  <Folder>wowza</Folder>
 </Install>

 <RunScript>
  <Script>tuning/tune.sh</Script>
 </RunScript>

 <RunScript>
 <Script>scripts/enable_cacti.sh</Script>
 </RunScript>
 </Commands>
</Startup>

How this works:

  • Download the wms-plugin-collection.zip file and dump it in the staging area for wowza (/opt/working/wowza/lib)
  • Unzip it (this leaves the zip file behind, but that doesn’t matter)
  • Run the a script that mounts some s3 buckets and copies them into the content folder:
#!/bin/sh
mkdir -p /usr/local/WowzaMediaServer/content/s3
mkdir -p /usr/local/WowzaMediaServer/content/archives

s3fs bucket1 -o accessKeyId=XXX -o secretAccessKey=YYY /usr/local/WowzaMediaServer/content/s3

s3fs bucket2 -o accessKeyId=XXX -o secretAccessKey=YYY /usr/local/WowzaMediaServer/content/archives/

cp /usr/local/WowzaMediaServer/content/s3/* /usr/local/WowzaMediaServer/content
  • Install the Wowza configs from the staging area
  • Run the tuning scripts
  • Run a script that automatically enables Cacti (since polling the local host is disabled by default):
#!/bin/sh
mysql -u root -ppassword < scripts/enable_cacti.sql

enable_cacti.sql contains the following statement:

update cacti.host set disabled='' where id='2';

(note that if you’re using an Elastic IP, you’ll need to restart the Wowza Service for Cacti to behave)

In my Wowza directories, I have:

  • applicationsdirectory
    • live directory (think of this as a mount point – it’s an empty directory, but has to exist)
  • confdirectory
    • Server.xml (general server parameters)
    • VHost.xml (host bindings, HTTP providers, etc.)
    • livedirectory (this is the application configuration for the live application)
      • Application.xml (defining the live application)
  • content directory
    • ipad.smil ( multi-bitrate stream selection for iOS devices)
    • mobile.smil (defining where the Roku stream goes – this abstracts a potentially changing stream name, as well as giving me a way to track Roku traffic using this perl stats collection script)
    • streamschedule.smil (defines the schedule for the Stream Class module)
    • Additional material is pulled from S3 in the aforementioned script
  • lib directory (empty mount point)

To start my Wowza instances, I create the startup package file and tree structure, and then call this startup script that packs up the zip file and fires off the instance.

More Updated Code

Updated the Wowza Launch Script. Changed it to be more friendly to a non-root user directory, as well as adding logic that makes the startup package on the fly, so that if you want to edit the contents, the next launch will send the current incarnation.

Stay tuned for a post soon on the anatomy of a Wowza startup package for EC2.

Using the Wowza Stream Class

I mentioned in the previous post about using ffmpeg in a cron job to create Simulated Live events via Wowza. In this post, I’ll explain how to do it using the Wowza Stream Class module, which allows you to set a broadcast schedule to play a mix of recorded and live content.

Wowza has a pretty good document on how to add this module in to your server and do a test playlist.If you’re setting this up on Amazon EC2, you’ll need to update your startup package by putting the module in the wowza/lib directory and the playlist in the wowza/content directory

Unfortunately, the tutorial doesn’t really cover playlist creation beyond the example. This is especially tricky, given that the scheduling parameters don’t seem to conform to any known SMIL standard. Yes, it’s XML, so theoretically, it doesn’t matter, but there are extensions in SMIL 3.0 that are meant to deal with server-side playlists for automating programming.

Unless you specified a different application name in the Properties section of Server.xml, the automated playlist will publish to the live application.

The basic structure of the SMIL file body consists of <stream> and <playlist> statements.

Stream Element

The <stream> element defines one or more virtual stream names that the playlists will feed into:

<stream name="playlist-high"></stream>
<stream name="playlist-low"></stream>

In this example, I have a high and low bandwidth stream. In your player, you reference the stream name, rather than the streamshedule.smil file, like this:

Flash RTMP:

streamer: rtmp://wowza.server.address(:port)/live
file: playlist-high

Flash HTTP:

http://wowza.server.address/live/playlist-high/manifest.f4m

HLS:

http://wowza.server.address/live/playlist-high/playlist.m3u8

Silverlight

http://wowza.server.address/live/playlist-high/Manifest

Playlist element

The <playlist> element defines specific video sequences that go into the virtual stream. There are four key parameters to the playlist element:

  • name : This is a unique name for that particular sequence.
  • playOnStream : This tells the Stream Class module which of the previously defined streams this playlist is associated with.
  • repeat : Valid values are true/false. This defines whether this playlist loops when it gets to the end.
  • scheduled : When this playlist is scheduled, in the format “YYYY-MM-DD HH:MM:SS” (24-hour time)

Within the playlist element are one or more <video> statements that use the following parameters:

  • src: the video to be played. Can either be:
    • a stream within the same live application (use the stream name only)
    • an MP4 video file in the Wowza content directory (use mp4:filename.mp4)
    • A stream elsewhere (requires some additional modules)
  • start : The number of seconds into the video to start playing. If this is a live source, use the value -2.
  • length : The number of seconds to play the video. The value -1 indicates to play until it ends.

Using start/length is a useful way to introduce commercial breaks or intermissions into a stream. This example would show BigBuckBunny.mp4 from the start, for 60 seconds, then cut to a commercial for the duration of the advertisement-1.mp4 file. After the commercial, it would resume and play for 2 more minutes, play a 30-second commercial from advertisement-2.mp4 and then plays the rest of the BigBuckBunny.mp4 file. If the playlist set to repeat, this will loop.

<video src="mp4:BigBuckBunny.m4v" start="0" length="60"/>
<video src="mp4:advertisement-1.mp4 start="0" length="-1"/>
<video src="mp4:BigBuckBunny.m4v" start="60" length="120"/>
<video src="mp4:advertisement-2.mp4 start="0" length="30"/>
<video src="mp4:BigBuckBunny.m4v" start="180" length="-1"/>

When a particular playlist has ended, and there are no others currently scheduled, it will default to the last playlist, even if that playlist’s repeat is set to false.

Here’s an example for our weekly service replays, and live sunday events:

<smil>
 <head>
 </head>
 <body>

  <stream name="playlist-high"></stream>
  <stream name="playlist-low"></stream>

  <playlist name="mon-l" playOnStream="playlist-low" repeat="false" scheduled="2011-01-17 07:45:00">
   <video src="mp4:traditions-l.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="mon-h" playOnStream="playlist-high" repeat="false" scheduled="2011-01-17 07:45:00">
   <video src="mp4:traditions-h.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="mon-loop-h" playOnStream="playlist-high" repeat="true" scheduled="2011-01-17 09:30:00">
   <video src="mp4:1-16-Loop-H.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="mon-loop-l" playOnStream="playlist-low" repeat="true" scheduled="2011-01-17 09:30:00">
   <video src="mp4:1-16-Loop-L.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="tue-l" playOnStream="playlist-low" repeat="false" scheduled="2011-01-18 12:45:00">
   <video src="mp4:praise-l.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="tue-h" playOnStream="playlist-high" repeat="false" scheduled="2011-01-18 12:45:00">
   <video src="mp4:praise-h.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="tue-loop-h" playOnStream="playlist-high" repeat="true" scheduled="2011-01-18 14:30:00">
   <video src="mp4:1-16-Loop-H.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="tue-loop-l" playOnStream="playlist-low" repeat="true" scheduled="2011-01-18 14:30:00">
   <video src="mp4:1-16-Loop-L.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="wed-l" playOnStream="playlist-low" repeat="false" scheduled="2011-01-19 21:45:00">
   <video src="mp4:praise-l.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="wed-h" playOnStream="playlist-high" repeat="false" scheduled="2011-01-19 21:45:00">
   <video src="mp4:praise-h.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="wed-loop-h" playOnStream="playlist-high" repeat="true" scheduled="2011-01-19 23:30:00">
   <video src="mp4:1-16-Loop-H.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="wed-loop-l" playOnStream="playlist-low" repeat="true" scheduled="2011-01-19 23:30:00">
   <video src="mp4:1-16-Loop-L.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="thu-l" playOnStream="playlist-low" repeat="false" scheduled="2011-01-20 03:15:00">
   <video src="mp4:traditions-l.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="thu-h" playOnStream="playlist-high" repeat="false" scheduled="2011-01-20 03:15:00">
   <video src="mp4:traditions-h.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="thu-loop-h" playOnStream="playlist-high" repeat="true" scheduled="2011-01-20 05:00:00">
   <video src="mp4:1-16-Loop-H.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="thu-loop-l" playOnStream="playlist-low" repeat="true" scheduled="2011-01-20 05:00:00">
   <video src="mp4:1-16-Loop-L.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="fri-l" playOnStream="playlist-low" repeat="false" scheduled="2011-01-21 07:45:00">
   <video src="mp4:traditions-l.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="fri-h" playOnStream="playlist-high" repeat="false" scheduled="2011-01-21 07:45:00">
   <video src="mp4:traditions-h.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="fri-loop-h" playOnStream="playlist-high" repeat="true" scheduled="2011-01-21 09:30:00">
   <video src="mp4:1-16-Loop-H.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="fri-loop-l" playOnStream="playlist-low" repeat="true" scheduled="2011-01-21 09:30:00">
   <video src="mp4:1-16-Loop-L.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="sat-l" playOnStream="playlist-low" repeat="false" scheduled="2011-01-22 02:45:00">
   <video src="mp4:praise-l.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="sat-h" playOnStream="playlist-high" repeat="false" scheduled="2011-01-22 02:45:00">
   <video src="mp4:praise-h.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="sat-loop-h" playOnStream="playlist-high" repeat="true" scheduled="2011-01-22 04:30:00">
   <video src="mp4:1-16-Loop-H.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="sat-loop-l" playOnStream="playlist-low" repeat="true" scheduled="2011-01-22 04:30:00">
   <video src="mp4:1-16-Loop-L.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="sun-am-high" playOnStream="playlist-high" repeat="false" scheduled="2011-01-23 11:30:00">
   <video src="mobile-2" start="-2" length="6300"/>
  </playlist>

  <playlist name="sun-am-low" playOnStream="playlist-low" repeat="false" scheduled="2011-01-23 11:30:00">
   <video src="mobile-1" start="-2" length="6300"/>
  </playlist>

  <playlist name="sun-am-loop-h" playOnStream="playlist-high" repeat="true" scheduled="2011-01-23 13:15:00">
   <video src="mp4:1-16-Loop-H.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="sun-am-loop-l" playOnStream="playlist-low" repeat="true" scheduled="2011-01-23 13:15:00">
   <video src="mp4:1-16-Loop-L.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="sun-pm-high" playOnStream="playlist-high" repeat="false" scheduled="2011-01-23 17:45:00">
   <video src="mobile-2" start="-2" length="6300"/>
  </playlist>

  <playlist name="sun-pm-low" playOnStream="playlist-low" repeat="false" scheduled="2011-01-23 17:45:00">
   <video src="mobile-1" start="-2" length="6300"/>
  </playlist>

  <playlist name="sun-pm-loop-h" playOnStream="playlist-high" repeat="true" scheduled="2011-01-23 19:30:00">
   <video src="mp4:1-16-Loop-H.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="sun-pm-loop-l" playOnStream="playlist-low" repeat="true" scheduled="2011-01-23 19:30:00">
   <video src="mp4:1-16-Loop-L.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="default-high" playOnStream="playlist-high" repeat="true" scheduled="2011-01-01 00:00:01">
   <video src="mp4:1-16-Loop-H.mp4" start="0" length="-1"/>
  </playlist>

  <playlist name="default-low" playOnStream="playlist-low" repeat="true" scheduled="2011-01-01 00:00:01">
   <video src="mp4:1-16-Loop-L.mp4" start="0" length="-1"/>
  </playlist>

 </body>
</smil>

Video files can either be uploaded, or recorded on the server using a live-record application type.

Once your playlist is built, you’ll need to restart the Wowza service for it to read the new playlist in and schedule it internally.

Update(July 18, 2001) : I’ve added a post about my Excel playlist generator.