https://www.wowza.com/docs/how-to-control-stream-class-streams-dynamically-modulestreamcontrol
You can use this with (or without) the server listener stream scheduler. If this application module is used in the same application that the scheduler populates with Streams and Playlists, you can use this application can be used to control those Streams and Playlists.
I can’t make this work. Server listener is working but Stream entity seems to be different between listener and controller.
I’m using StreamControl Flex app to add 1 playlist to same Stream mentionned in listener.
Then add some streams to the new playlist. When I hit open playlist on stream, it does not work. WMS logs only show this single line :
2010-06-12 20:02:25 CEST comment server ERROR 500 - - - - - 88.224 - - - - - - - - - - - - - - - – - - - - - - - -
I"ve added a try/catch block in openPlaylistOnStream function but actually exception getMessage() returns this empty string.
What am I missing ?
Thank you.
Module to control Stream class streams and playlists. Use this module to dynamically create, setup and start live Stream class streams from static and live sources, and add sources to running Streams.
You can use this module along with the Stream class scheduler. The Streams and Playlists that you set up in the schedule can be modified and controlled using this application.
package com.wowza.wms.plugin.collection.module; import com.wowza.wms.amf.*; import com.wowza.wms.client.*; import com.wowza.wms.module.*; import com.wowza.wms.request.*; import com.wowza.wms.stream.publish.*; public class ModuleStreamControl extends ModuleBase { public void openPlaylistOnStream(IClient client, RequestFunction function, AMFDataList params) { String streamName = getParamString(params, PARAM1); String playlistName = getParamString(params, PARAM2); Stream stream = (Stream)client.getAppInstance().getProperties().getProperty(streamName); Playlist playlist = (Playlist)client.getAppInstance().getProperties().getProperty(playlistName); playlist.open(stream); } public void addItemToPlaylist(IClient client, RequestFunction function, AMFDataList params) { AMFDataObj obj = getParamObj(params, PARAM1); Playlist playlist = (Playlist)client.getAppInstance().getProperties().getProperty(obj.getString("playListName")); playlist.addItem(obj.getString("itemName"), obj.getInt("itemStart"), obj.getInt("itemDuration")); } public void playNextPlaylistItem(IClient client, RequestFunction function, AMFDataList params) { String streamName = getParamString(params, PARAM1); Stream stream = (Stream)client.getAppInstance().getProperties().getProperty(streamName); stream.next(); } public void addNewStream(IClient client, RequestFunction function, AMFDataList params) { String streamName = getParamString(params, PARAM1); Stream stream = Stream.createInstance(client.getAppInstance(), streamName); client.getAppInstance().getProperties().put(streamName, stream); } public void addNewPlaylist(IClient client, RequestFunction function, AMFDataList params) { String playListName = getParamString(params, PARAM1); Playlist playlist = new Playlist(playListName); playlist.setRepeat(true); client.getAppInstance().getProperties().put(playListName, playlist); } public void stopStream(IClient client, RequestFunction function, AMFDataList params) { String streamName = getParamString(params, PARAM1); Stream stream = Stream.createInstance(client.getAppInstance(), streamName); stream.removeFromPlaylist(streamName); } }
Actionscript 3
public function addNewStream():void { nc.call("addNewStream",null,streamName.text); } public function addNewPlaylist():void { nc.call("addNewPlaylist",null,playlist.text); } public function stopStream():void { nc.call("stopStream",null,streamName.text); } public function openPlaylistOnStream():void { nc.call("openPlaylistOnStream",null,streamName.text,playlist.text); } public function nextItem():void { nc.call("playNextPlaylistItem",null,streamName.text); } public function addItemToPlaylist():void { var obj:Object = new Object(); obj.playListName = playlist.text; obj.itemName = itemName.text; obj.itemStart = itemStart.text; obj.itemDuration = itemDuration.text; nc.call("addItemToPlaylist",null,obj); }
You can use this with (or without) the server listener stream scheduler. If this application module is used in the same application that the scheduler populates with Streams and Playlists, you can use this application can be used to control those Streams and Playlists. The association between Playlists and Streams in the scheduler is irrelevant in the application module – you can start any Playlist on any Stream.
This is a modified version of Wowza Live Player with these features.
This client is functional but missing everything in the way of validation, feedback and state (of existing streams on the server). To operate, use the buttons and textinput below the the video display
Enter a Stream (I filled in “StreamNew”), then click “add stream”
Enter a Playlist (I filled in “playlistNew”), then click “add playlist”
Add at least one item to the playlist (I filled in “mp4:Extremists.m4v” with start of 0 and length -1), click “add Item to playlist”
Then click “open playlist on stream”
Then in above, enter the streamname, “StreamNew” and click “Play”
To add a new item, enter a new video name with start and length, and click “add item to playlist”, then click open “open playlist on stream again”. If the video is currently playing, the new item will start playing after the current one or when it cycles through to the end. You can also click “play next item”
The Stream Control application module and client provide no feedback or listing of active streams. If you are using with the scheduler you have to know what stream name and playlist name to use.
To add a live stream to a playlist enter “-2” for Video start. You can mix live streams and static content. You can use MediaCaster streams, but be sure to start them using /conf/StartupStreams.xml or in a application module or server listener using MediaCasterStreamManager.
I set the buffer to 0 for the playback in the provided client app for the moment. Buffer time in the client is a big factor in what the user sees when streams switch.
Does this needs to be compiled or is it already built into the release version of WMS 2.0
It needs to be compiled. But this module has been added to this collection:
http://community.wowza.com/t/-/237
Richard
Sorry, I need to update the README.html to include this module.
Add this Module to the Modules list in the /conf/live/Application.xml
<Module>
<Name>streamcontroller</Name>
<Description>ModuleStreamControl</Description>
<Class>com.wowza.wms.plugin.collection.module.ModuleStreamControl</Class>
</Module>
Richard
No, you have to add that Module. Add it below the ModuleMediaCasterStreamManager Module.
But make sure it is above
Richard
No, it is not right.
But make sure it is above
It has to be inside the Modules group. You added it below , so it is outside.
Richard
The Modules section looks right, but the Properties section is not. Everything has to be well-formed in XML. Any starting tag has to have a close , and in the right order.
Not correct:
<Properties>
</Property>
<Property>
<Name>mediaCasterStreamManagerPassword</Name>
<Value>!()$£&^%"!901</Value>
</Properties>
Richard
Great! Glad it’s working. Enjoy, it’s a fun system, tho missing much.
If you want to include a live stream, set the start value to “-2”.
If the live stream is MediaCaster stream, like IP cam, you have to start it first. Use the /conf/StartupStreams.xml
Richard
The playlist.open(stream) method doesn’t play a stream, it just sets it, so it wouldn’t be the place to see if the stream existed.
When a Stream class stream plays a Playlist, and it cycles into an Playlist item that does not exist, there is an error:
WARN server comment - Extremistsx.flv open: java.io.FileNotFoundException: C:\Program Files (x86)\Wowza Media Systems\Wowza Media Server 2\content\Extremistsx.flv (The system cannot find the file specified)
You should be able to handle that error in the IStreamActionNotify interface, onPlaylistItemStart method. Here is a the module modified to implement IStreamActionNotify interface for each Stream it creates. Explore the Stream and PlaylistItem parameters of the onPlaylistItemStart method:
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);
public void onServerInit(IServer server)
{
log.info("ServerINIT");
IVHost vhost = VHostSingleton.getInstance(server.getProperties().getPropertyStr("PublishToVHost", VHost.VHOST_DEFAULT));
IApplication app = vhost.getApplication(server.getProperties().getPropertyStr("PublishToApplication", "live"));
String storageDir = app.getAppInstance("_definst_").getStreamStorageDir();
try
{
String smilLoc = storageDir + "/streamschedule.smil";
File playlistxml = new File(smilLoc);
if (playlistxml.exists() == false){
log.info("Could not find playlist file: " + smilLoc);
return;
}
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(smilLoc);
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");
Stream stream = Stream.createInstance(vhost, app.getName(), streamName);
app.getAppInstance("_definst_").getProperties().put(streamName, stream);
}
}
NodeList playList = document.getElementsByTagName("playlist");
if (playList.getLength() == 0){
log.info("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("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")=="false")?false:true);
app.getAppInstance("_definst_").getProperties().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 = parser.parse(scheduled);
Stream stream = (Stream)app.getAppInstance("_definst_").getProperties().get(streamName);
ScheduledItem item = new ScheduledItem(startTime, playlist, stream);
item.start();
IStreamActionNotify actionNotify = new StreamListener(app.getAppInstance("_definst_"));
stream.addListener(actionNotify);
log.info("Scheduled: " + stream.getName() + " for: " + scheduled);
}
}
}
catch(Exception ex)
{
}
}
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() {
mPL.open(mStream);
log.info("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)
{
}
public void onServerShutdownStart(IServer server)
{
}
class StreamListener implements IStreamActionNotify
{
StreamListener(IApplicationInstance appInstance)
{
}
public void onPlaylistItemStop(Stream stream, PlaylistItem item)
{
}
public void onPlaylistItemStart(Stream stream, PlaylistItem item)
{
}
}
}
Richard
The smil file from the scheduler example is read once, not involved in anything after that.
openPlaylistOnStream() is a function in that controller example. Which is separate application, no smil file involved.
https://www.wowza.com/forums/showthread.php?p=32659
Server-side, openPlaylistOnStream is just doing this:
playlist.open(stream);
Richard
Also, to your 1st question: yes, you can remove a PlaylistItem like this:
Boolean success = stream.removeFromPlaylist("Extremists.flv");
Note that this will not work if it is playing.
Richard
Right, no smil in the controller, it’s acting on running objects. The source code is at the top of this thread. It’s short, simple, not much to it really.
Richard
It is getting repetitive, I think it’s all in that post already. Try it out, there’s a little Flash front end.
Richard
It’s a server-side switch that is not dependent on the client.
Richard
New info: The iphone/ipad is sensitive to encoding changes and will freeze. So server-side playlists may not work in those situations.
Richard
It’s a matter of knowing that there is a stream name, e.g., “Stream1” created by the scheduler. That controller will create a Stream from scratch too.
Richard
Not familiar with that player, but usually need Flash CS or Flex/Flash Builder to compile.
Richard
Yes it is.
Richard