I’m using Wowza 2.2.3 + patch 6 , I receive stream from Adobe Flash Media Live Encoder 3.2 (connected to Roku).
With Wowza IDE compile the following module:
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 PublishNotifier publishNotifier;
private List 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;
}
boolean record = false;
if (hourOfDay >= start && hourOfDay < end) {
record = true;
}
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”);
TimeZone tz = TimeZone.getTimeZone(timezone);
Calendar cal = Calendar.getInstance(tz);
hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
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 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.startRecordin g: "+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) {
// appInstance.getVHost().getHandlerThreadPool().exec ute(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 mediaCasters = appInstance.getMediaCasterStreams().getMediaCasterNames();
if (mediaCasters.contains(streamName)) {
int lockCount = appInstance.getMediaCasterStreams().getMediaCaster (streamName).getLockCount();
if (lockCount > 0)
startRecording(stream);
}
}
}
}
My configuration file Application.xml :
true
live
${com.wowza.wms.context.VHostConfigHome}/content
${com.wowza.wms.context.VHostConfigHome}/keys
cupertinostreamingpacketizer, smoothstreamingpacketizer, sanjosestreamingpacketizer
cupertinostreaming, smoothstreaming, sanjosestreaming
-1
*
*
*
*
digest
none
senderreport
12000
75
90000
0
0.0.0.0
127.0.0.1
*
base
Base
com.wowza.wms.module.ModuleCore
properties
Properties
com.wowza.wms.module.ModuleProperties
logging
Client Logging
com.wowza.wms.module.ModuleClientLogging
flvplayback
FLVPlayback
com.wowza.wms.module.ModuleFLVPlayback
ModuleMediaWriterFileMover
ModuleMediaWriterFileMover
com.wowza.wms.module.ModuleMediaWriterFileMover
ModuleStreamRecord
File Management
streamrecord.hourly.monthlyrollover.ModuleStreamRecord
fileMoverDestinationPath
/Wowfiles
fileMoverDeleteOriginal
true
Boolean
starHour
00
Integer
endHour
24
Integer
timezone
GMT-08:00
String
As far as I understand this module is to record the stream into separate files every hour, but this does not happen.
All is correct? Thanks in advance.