You can do it but you are going to have to take the plunge and compile a module.
https://www.wowza.com/downloads/WowzaIDE_beta_2/WowzaIDE_UsersGuide.pdf
Here a module I use for this purpose. It uses the name of the *.stream file as the recorded stream name.
It also requires the LiveStreamRecord addon package.
https://www.wowza.com/docs/how-to-record-live-streams-httplivestreamrecord
There was also an issue with Wowza trying to call the record command twice so you need to make sure that you are using the latest patch version of wowza.
https://www.wowza.com/downloads/WowzaMediaServer-2-1-2/WowzaMediaServer2.1.2-patch5.zip
Carefully ready the IMPORTANT!!! notice in the README.txt file of the patch. Several core components have been upgraded:
IMPORTANT!!!
Several core components have been updraded to newer versions. Before
applying this patch, delete the following files from your Wowza Media Server
installation:
[install-dir]/lib/bcprov-ext-jdk15-143.jar
[install-dir]/lib/commons-lang-2.4.jar
[install-dir]/lib/log4j-1.2.15.jar
You can set a startHour, endHour and timezone as properties in your Application.xml. Add them to the last properties section in the file.
It will record the files in a separate folder each day 00 - 31 in 1 hour long files. At the end of the month it will start overwriting the old files.
package streamrecord.hourly.monthlyrollover;
import java.io.File;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import com.wowza.wms.application.*;
import com.wowza.wms.module.*;
import com.wowza.wms.plugin.integration.liverecord.*;
import com.wowza.wms.stream.IMediaStream;
import com.wowza.wms.stream.IMediaStreamActionNotify;
import com.wowza.wms.stream.IMediaStreamNotify;
public class ModuleStreamRecord extends ModuleBase implements IMediaStreamNotify {
private IApplicationInstance appInstance;
private String timezone;
private StreamTimer streamTimer;
private int date;
private int startHour = 0;
private int endHour = 23;
private int hourOfDay = -1;
private boolean record = false;
private PublishNotifier publishNotifier;
private List<String> streams;
public static final int FORMAT_UNKNOWN = 0;
public static final int FORMAT_FLV = 1;
public static final int FORMAT_MP4 = 2;
private Map<String, ILiveStreamRecord> recorders = null;
private class StreamTimer extends Thread {
private boolean doQuit = false;
public synchronized void quit() {
doQuit = true;
}
public void run() {
while (true) {
try {
TimeZone tz = TimeZone.getTimeZone(timezone);
Calendar cal = Calendar.getInstance(tz);
date = cal.get(Calendar.DATE);
int prevHour = hourOfDay;
hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
int start = startHour;
int end = endHour;
if (start > end && hourOfDay > start) {
end += 24;
}
if (end < start && hourOfDay < end) {
start -=24;
}
if (hourOfDay >= start && hourOfDay < end) {
record = true;
} else {
record = false;
}
streams = appInstance.getMediaCasterStreams().getMediaCasterNames();
if (prevHour != hourOfDay && record) {
for (String streamName : streams) {
IMediaStream stream = appInstance.getStreams().getStream(streamName);
appInstance.getVHost().getHandlerThreadPool().execute(new DoStartRecording(stream, streamName));
}
} else if(!record) {
for (String streamName : streams) {
ILiveStreamRecord recorder = recorders.remove(streamName);
if(recorder != null)
recorder.stopRecording();
}
}
Thread.currentThread();
Thread.sleep(60000);
synchronized (this) {
if (doQuit) {
break;
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public void onAppStart(IApplicationInstance appInstance) {
this.appInstance = appInstance;
WMSProperties props = appInstance.getProperties();
startHour = props.getPropertyInt("startHour", 0);
endHour = props.getPropertyInt("endHour", 24);
timezone = props.getPropertyStr("timezone", "America/Chicago");
appInstance.addMediaStreamListener(this);
recorders = Collections.synchronizedMap(new HashMap<String, ILiveStreamRecord>());
publishNotifier = new PublishNotifier();
streamTimer = new StreamTimer();
streamTimer.setName("RecordController-" + appInstance.getApplication().getName());
streamTimer.setDaemon(true);
streamTimer.start();
}
public void onAppStop(IApplicationInstance appInstance) {
streamTimer.quit();
// cleanup any recorders that are still running
synchronized (recorders) {
Iterator<String> iter = recorders.keySet().iterator();
while(iter.hasNext())
{
String streamName = iter.next();
ILiveStreamRecord recorder = recorders.get(streamName);
recorder.stopRecording();
getLogger().info(" stopRecording: "+streamName);
}
recorders.clear();
}
recorders = null;
appInstance.removeMediaStreamListener(this);
publishNotifier = null;
}
private synchronized void startRecording(IMediaStream stream) {
String streamAlias = stream.getName().substring(0, stream.getName().indexOf(".stream"));
if (!streamAlias.isEmpty()) {
String outputPath = appInstance.getStreamStorageDir()+"/"+String.format("%02d", date); //day;
boolean append = false;
File path = new File(outputPath);
if (path.exists()) {
File outputFile = new File(path.getPath()+File.separator+streamAlias+"_"+String.format("%02d", hourOfDay)+".mp4");
Date today = new Date();
if (!(outputFile.lastModified() < today.getTime() - 86400000)) {
append = true;
}
} else {
path.mkdirs();
}
recordStream(stream, FORMAT_MP4, append, outputPath+File.separator+streamAlias+"_"+String.format("%02d", hourOfDay)+".mp4", false, true, true);
}
}
private void recordStream(IMediaStream stream, int format, boolean append, String outputPath, boolean versionFile, boolean startOnKeyFrame, boolean recordData)
{
String streamName = stream.getName();
// if a format was not specified then check the stream prefix and choose accordingly
if (format == FORMAT_UNKNOWN)
{
format = FORMAT_FLV;
String extStr = stream.getExt();
if (extStr.equals("mp4"))
format = FORMAT_MP4;
}
String params = "stream:"+streamName;
params += " format:"+(format==FORMAT_MP4?"mp4":"flv");
params += " append:"+append;
if (outputPath != null)
params += " outputPath:"+outputPath;
else
{
File writeFile = stream.getStreamFileForWrite();
params += " outputPath:"+writeFile.getAbsolutePath();
}
params += " versionFile:"+versionFile;
params += " startOnKeyFrame:"+startOnKeyFrame;
params += " recordData:"+recordData;
getLogger().info("ModuleStreamRecord.startRecording: "+params);
// create a stream recorder and save it in a map of recorders
ILiveStreamRecord recorder = null;
// create the correct recorder based on format
if (format == FORMAT_MP4)
recorder = new LiveStreamRecorderMP4();
else
recorder = new LiveStreamRecorderFLV();
// add it to the recorders list
ILiveStreamRecord prevRecorder = recorders.get(streamName);
if (prevRecorder != null)
prevRecorder.stopRecording();
recorders.put(streamName, recorder);
// if you want to record data packets as well as video/audio
recorder.setRecordData(recordData);
// Set to true if you want to version the previous file rather than overwrite it
recorder.setVersionFile(versionFile);
// If recording only audio set this to false so the recording starts immediately
recorder.setStartOnKeyFrame(startOnKeyFrame);
// start recording
recorder.startRecording(stream, outputPath, append);
}
@Override
public void onMediaStreamCreate(IMediaStream stream) {
stream.addClientListener(publishNotifier);
}
@Override
public void onMediaStreamDestroy(IMediaStream stream) {
stream.removeClientListener(publishNotifier);
if(!stream.isPlay()) {
ILiveStreamRecord recorder = recorders.remove(stream.getName());
if (recorder != null)
recorder.stopRecording();
}
}
private class PublishNotifier implements IMediaStreamActionNotify {
@Override
public void onPause(IMediaStream stream, boolean isPause,
double location) {
// TODO Auto-generated method stub
}
@Override
public void onPlay(IMediaStream stream, String streamName,
double playStart, double playLen, int playReset) {
// TODO Auto-generated method stub
}
@Override
public void onPublish(IMediaStream stream, String streamName,
boolean isRecord, boolean isAppend) {
if(record)
appInstance.getVHost().getHandlerThreadPool().execute(new DoStartRecording(stream, streamName));
}
@Override
public void onSeek(IMediaStream stream, double location) {
// TODO Auto-generated method stub
}
@Override
public void onStop(IMediaStream stream) {
// TODO Auto-generated method stub
}
@Override
public void onUnPublish(IMediaStream stream, String streamName,
boolean isRecord, boolean isAppend) {
ILiveStreamRecord recorder = recorders.remove(streamName);
if (recorder != null)
{
// stop recording
recorder.stopRecording();
}
}
}
private class DoStartRecording implements Runnable {
private IMediaStream stream;
private String streamName;
private DoStartRecording(IMediaStream stream, String streamName) {
this.stream = stream;
this.streamName = streamName;
}
public void run() {
List<String> mediaCasters = appInstance.getMediaCasterStreams().getMediaCasterNames();
if (mediaCasters.contains(streamName)) {
int lockCount = appInstance.getMediaCasterStreams().getMediaCaster(streamName).getLockCount();
if (lockCount > 0)
startRecording(stream);
}
}
}
}
Look at the tutorials for info on how to compile and deploy a module.