Like a TV station, a Stream in this schedule is a channel, the Playlists are programs with one or more Video segments. A schedule can have as many Streams (channels) as you want, with as many Playlists (programs) as you want, where each Playlist (program) is scheduled to play on a Stream (channel) at a certain time. If a Playlist is scheduled to start on a Stream while another Playlist is running, it will start. If you set a schedule in the past the Playlist will play immediately.
Note: This runs when Wowza starts only. See this post for a version that will load this schedule on demand
Code:
package com.wowza.wms.plugin.collection.serverlistener;
import com.wowza.wms.application.*;
import com.wowza.wms.server.*;
import com.wowza.wms.vhost.*;
import com.wowza.wms.stream.publish.*;
import com.wowza.wms.logging.*;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class ServerListenerStreamPublisher implements IServerNotify {
WMSLogger log = WMSLoggerFactory.getLogger(null);
Map<String, Stream> streamMap = new HashMap<String, Stream>();
Map<String, Playlist> playlistMap = new HashMap<String, Playlist>();
public void onServerInit(IServer server)
{
log.info("ServerListenerStreamPublisher Started.");
IVHost vhost = null;
IApplication app = null;
try
{
vhost = VHostSingleton.getInstance(server.getProperties().getPropertyStr("PublishToVHost", "_defaultVHost_"));
} catch (Exception evhost) {
log.info("ServerListenerStreamPublisher: Failed to get Vhost can not run.");
return;
}
try
{
app = vhost.getApplication(server.getProperties().getPropertyStr("PublishToApplication", "live"));
} catch (Exception eapp) {
log.info("ServerListenerStreamPublisher: Failed to get Application can not run.");
return;
}
// Belt and Braces check for VHost and App
if ( vhost == null || app == null )
{
log.info("ServerListenerStreamPublisher: VHost or Application failed, not running.");
return;
}
Boolean passThruMetaData = server.getProperties().getPropertyBoolean("PassthruMetaData", true);
String storageDir = app.getAppInstance("_definst_").getStreamStorageDir();
try
{
String smilLoc = storageDir + "/streamschedule.smil";
File playlistxml = new File(smilLoc);
if (playlistxml.exists() == false){
log.info("ServerListenerStreamPublisher: Could not find playlist file: " + smilLoc);
return;
}
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = null;
Document document = null;
try {
db = dbf.newDocumentBuilder();
document = db.parse("file:///" + smilLoc);
} catch (Exception e ) { log.info("ServerListenerStreamPublisher: XML Parse failed"); return; }
document.getDocumentElement().normalize();
NodeList streams = document.getElementsByTagName("stream");
for (int i = 0; i < streams.getLength(); i++)
{
Node streamItem = streams.item(i);
if (streamItem.getNodeType() == Node.ELEMENT_NODE)
{
Element e = (Element) streamItem;
String streamName = e.getAttribute("name");
log.info("ServerListenerStreamPublisher: Streame name is '"+streamName+"'");
Stream stream = Stream.createInstance(vhost, app.getName(), streamName);
streamMap.put(streamName, stream);
app.getAppInstance("_definst_").getProperties().setProperty(streamName, stream);
}
}
NodeList playList = document.getElementsByTagName("playlist");
if (playList.getLength() == 0){
log.info("ServerListenerStreamPublisher: No playlists defined in smil file");
return;
}
for (int i = 0; i < playList.getLength(); i++)
{
Node scheduledPlayList = playList.item(i);
if (scheduledPlayList.getNodeType() == Node.ELEMENT_NODE)
{
Element e = (Element) scheduledPlayList;
NodeList videos = e.getElementsByTagName("video");
if (videos.getLength() == 0){
log.info("ServerListenerStreamPublisher: No videos defined in stream");
return;
}
String streamName = e.getAttribute("playOnStream");
if (streamName.length()==0)
continue;
Playlist playlist = new Playlist(streamName);
playlist.setRepeat((e.getAttribute("repeat").equals("false"))?false:true);
playlistMap.put(e.getAttribute("name"), playlist);
for (int j = 0; j < videos.getLength(); j++)
{
Node video = videos.item(j);
if (video.getNodeType() == Node.ELEMENT_NODE)
{
Element e2 = (Element) video;
String src = e2.getAttribute("src");
Integer start = Integer.parseInt(e2.getAttribute("start"));
Integer length = Integer.parseInt(e2.getAttribute("length"));
playlist.addItem(src, start, length);
}
}
String scheduled = e.getAttribute("scheduled");
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date startTime = null;
try {
startTime = parser.parse(scheduled);
} catch (Exception z ) { log.info("Parsing schedule time failed."); return ; }
Stream stream = streamMap.get(streamName);
stream.setSendOnMetadata(passThruMetaData);
ScheduledItem item = new ScheduledItem(startTime, playlist, stream);
item.start();
IStreamActionNotify actionNotify = new StreamListener(app.getAppInstance("_definst_"));
stream.addListener(actionNotify);
log.info("ServerListenerStreamPublisher Scheduled: " + stream.getName() + " for: " + scheduled);
}
}
}
catch(Exception ex)
{
log.info("ServerListenerStreamPublisher: Error from playlist manager is '"+ex.getMessage()+"'");
}
}
private class ScheduledItem {
public Timer mTimer;
public TimerTask mTask;
public Date mStart;
public Playlist mPL;
public Stream mStream;
public ScheduledItem(Date d, Playlist pl, Stream s){
mStart = d;
mPL = pl;
mStream = s;
mTask = new TimerTask(){
public void run() {
//synchronized(mStream.getLock())
//{
mPL.open(mStream);
//}
log.info("ServerListenerStreamPublisher Scheduled stream is now live: " + mStream.getName());
}
};
mTimer = new Timer();
}
public void start(){
if (mTimer==null)
mTimer = new Timer();
mTimer.schedule(mTask, mStart);
log.info("scheduled playlist: "+mPL.getName()+
" on stream: "+mStream.getName()+
" for:"+mStart.toString());
}
public void stop(){
if (mTimer != null){
mTimer.cancel();
mTimer=null;
log.info("cancelled playlist: "+mPL.getName()+
" on stream: "+mStream.getName()+
" for:"+mStart.toString());
}
}
}
public void onServerCreate(IServer server)
{
}
public void onServerShutdownComplete(IServer server)
{
log.info("ServerListenerStreamPublisher: Shutdown server start");
for (Map.Entry<String, Stream> entry : streamMap.entrySet())
{
try
{
Stream stream = entry.getValue();
stream.close();
stream = null;
log.info("ServerListenerStreamPublisher Closed Stream: " + entry.getKey());
}
catch(Exception ex)
{
log.error(ex.getMessage());
}
}
for (Map.Entry<String, Playlist> entry : playlistMap.entrySet())
{
try
{
Playlist pl = entry.getValue();
pl = null;
}
catch(Exception ex)
{
log.error(ex.getMessage());
}
}
}
public void onServerShutdownStart(IServer server)
{
}
class StreamListener implements IStreamActionNotify
{
StreamListener(IApplicationInstance appInstance)
{
}
public void onPlaylistItemStop(Stream stream, PlaylistItem item)
{
if (item.getIndex() == (stream.getPlaylist().size() - 1))
{
if (! stream.getRepeat())
{
stream.close();
WMSLoggerFactory.getLogger(null).info("ServerListenerStreamPublisher: closing stream: " + stream.getName());
}
}
}
public void onPlaylistItemStart(Stream stream, PlaylistItem item)
{
try
{
String name = stream.getCurrentItem().getName();
stream.getPublisher().getAppInstance().broadcastMsg("PlaylistItemStart", name);
WMSLoggerFactory.getLogger(null).info("ServerListenerStreamPublisher PlayList Item Start: " + name);
}
catch(Exception ex)
{
WMSLoggerFactory.getLogger(null).info("ServerListenerStreamPublisher Get Item error: " + ex.getMessage());
}
}
}
}
Code:
<ServerListener>
<BaseClass>com.wowza.wms.plugin.collection.serverlistener.ServerListenerStreamPublisher</BaseClass>
</ServerListener>
Code:
<Property> <Name>PublishToApplication</Name> <Value>MyLiveApp</Value> </Property>
Code:
<Property> <Name>PublishToVHost</Name> <Value>CustomVHost</Value> </Property>
Code:
<Property>
<Name>PassthruMetaData</Name>
<Value>false</Value>
<Type>Boolean</Type>
</Property>
Create the smil file [install-dir]/content/streamschedule.smil
Create at least one stream, then create at least one playlist that plays on that stream. Set repeat to true or false for each playlist. Use start="-2" for live sources. Use length="-1" to play until media ends.
You can create an elaborate schedule with several streams and many playlists scheduled to play on a stream.
Code:
<smil>
<head>
</head>
<body>
<stream name="Stream1"></stream>
<stream name="Stream2"></stream>
<playlist name="pl1" playOnStream="Stream1" repeat="true" scheduled="2009-12-11 16:00:00">
<video src="mp4:sample.mp4" start="5" length="5"/>
<video src="mp4:sample.mp4" start="50" length="5"/>
<video src="mp4:sample.mp4" start="150" length="5"/>
</playlist>
<playlist name="pl2" playOnStream="Stream1" repeat="true" scheduled="2009-12-11 16:30:00">
<video src="mp4:sample.mp4" start="0" length="-1"/>
</playlist>
<playlist name="pl3" playOnStream="Stream2" repeat="true" scheduled="2009-12-11 16:00:00">
<video src="mp4:sample.mp4" start="30" length="5"/>
</playlist>
</body>
</smil>
To play open:
[install-dir]/examples/LiveVideoStreaming/FlashRTMPPlayer/player.html (Wowza Media Server 3.5 and later)
-or-
[install-dir]/examples/LiveVideoStreaming/client/live.html (prior version of Wowza Media Server)
Server: rtmp://[wowza-ip-address]/live
Stream: Stream1 (or Stream2. As set in smil file)
A compiled version of this server listener is included in the Wowza Modules Collection. Download and unzip the collection, then copy /lib/wms-plugin-collection.jar from the package to the Wowza /lib folder. Then restart Wowza.
This ServerListener broadcasts Playlist item changes to Flash clients. You can listen for these messages by adding a callback "PlaylistItemStart" on the NetConnection in your Flash application, something like this:
Code:
var clientObj:Object = new Object();
clientObj.PlaylistItemStart(itemName:String):void
{
trace("Item Name: " + itemName);
});
netconnection.client = clientObj;
netconnection.connect("rtmp://[wowza-ip-address]:1935/[app-name]");
http://www.wowzamedia.com/forums/content.php?187
- Click here, if you are having problems or would like to discuss this article.
- Leave a comment below, if there is some aspect of this article you would like to see changed or improved.


Article List
Categories
Wowza Media