I’m using wowza media server2 prepetual 2.2.4 build27452
Flash Media Encoder 3.2 with (MP4:+streamname)
streamrecord hourly
I built the follwoing code using wowza IDE. it works good
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);
}
}
}
}
with the follwoing Application Conf
<Root>
<Application>
<!-- Uncomment to set application level timeout values
<ApplicationTimeout>60000</ApplicationTimeout>
<PingTimeout>12000</PingTimeout>
<ValidationFrequency>8000</ValidationFrequency>
<MaximumPendingWriteBytes>0</MaximumPendingWriteBytes>
<MaximumSetBufferTime>60000</MaximumSetBufferTime>
<MaximumStorageDirDepth>25</MaximumStorageDirDepth>
-->
<Connections>
<AutoAccept>true</AutoAccept>
<AllowDomains></AllowDomains>
</Connections>
<!--
StorageDir path variables
${com.wowza.wms.AppHome} - Application home directory
${com.wowza.wms.ConfigHome} - Configuration home directory
${com.wowza.wms.context.VHost} - Virtual host name
${com.wowza.wms.context.VHostConfigHome} - Virtual host config directory
${com.wowza.wms.context.Application} - Application name
${com.wowza.wms.context.ApplicationInstance} - Application instance name
-->
<Streams>
<StreamType>live-record</StreamType>
<StorageDir>${com.wowza.wms.context.VHostConfigHome}/content/qt</StorageDir>
<KeyDir>${com.wowza.wms.context.VHostConfigHome}/keys</KeyDir>
<!-- LiveStreamPacketizers (separate with commas): cupertinostreamingpacketizer, smoothstreamingpacketizer, sanjosestreamingpacketizer, cupertinostreamingrepeater, smoothstreamingrepeater, sanjosestreamingrepeater -->
<LiveStreamPacketizers>cupertinostreamingpacketizer,smoothstreamingpacketizer,sanjosestreamingpacketizer</LiveStreamPacketizers>
<!-- Properties defined here will override any properties defined in conf/Streams.xml for any streams types loaded by this application -->
<Properties>
</Properties>
</Streams>
<!-- HTTPStreamers (separate with commas): cupertinostreaming, smoothstreaming, sanjosestreaming -->
<HTTPStreamers>cupertinostreaming,smoothstreaming,sanjosestreaming</HTTPStreamers>
<SharedObjects>
<StorageDir></StorageDir>
</SharedObjects>
<Client>
<IdleFrequency>-1</IdleFrequency>
<Access>
<StreamReadAccess>*</StreamReadAccess>
<StreamWriteAccess>*</StreamWriteAccess>
<StreamAudioSampleAccess></StreamAudioSampleAccess>
<StreamVideoSampleAccess></StreamVideoSampleAccess>
<SharedObjectReadAccess>*</SharedObjectReadAccess>
<SharedObjectWriteAccess>*</SharedObjectWriteAccess>
</Access>
</Client>
<RTP>
<!-- RTP/Authentication/[type]Methods defined in Authentication.xml. Default setup includes; none, basic, digest -->
<Authentication>
<PublishMethod>digest</PublishMethod>
<PlayMethod>none</PlayMethod>
</Authentication>
<!-- RTP/AVSyncMethod. Valid values are: senderreport, systemclock, rtptimecode -->
<AVSyncMethod>senderreport</AVSyncMethod>
<MaxRTCPWaitTime>12000</MaxRTCPWaitTime>
<IdleFrequency>75</IdleFrequency>
<RTSPSessionTimeout>90000</RTSPSessionTimeout>
<RTSPMaximumPendingWriteBytes>0</RTSPMaximumPendingWriteBytes>
<RTSPBindIpAddress></RTSPBindIpAddress>
<RTSPConnectionIpAddress>0.0.0.0</RTSPConnectionIpAddress>
<RTSPOriginIpAddress>127.0.0.1</RTSPOriginIpAddress>
<IncomingDatagramPortRanges>*</IncomingDatagramPortRanges>
<!-- Properties defined here will override any properties defined in conf/RTP.xml for any depacketizers loaded by this application -->
<Properties>
</Properties>
</RTP>
<MediaCaster>
<!-- Properties defined here will override any properties defined in conf/MediaCasters.xml for any MediaCasters loaded by this applications -->
<Properties>
<Property>
<Name>forceInterleaved</Name>
<Value>true</Value>
<Type>Boolean</Type>
</Property>
</Properties>
</MediaCaster>
<MediaReader>
<!-- Properties defined here will override any properties defined in conf/MediaReaders.xml for any MediaReaders loaded by this applications -->
<Properties>
</Properties>
</MediaReader>
<MediaWriter>
<!-- Properties defined here will override any properties defined in conf/MediaWriter.xml for any MediaWriter loaded by this applications -->
<Properties>
</Properties>
</MediaWriter>
<LiveStreamPacketizer>
<!-- Properties defined here will override any properties defined in conf/LiveStreamPacketizers.xml for any LiveStreamPacketizers loaded by this applications -->
<Properties>
</Properties>
</LiveStreamPacketizer>
<HTTPStreamer>
<!-- Properties defined here will override any properties defined in conf/HTTPStreamers.xml for any HTTPStreamer loaded by this applications -->
<Properties>
</Properties>
</HTTPStreamer>
<Repeater>
<OriginURL></OriginURL>
<QueryString><![CDATA[]]></QueryString>
</Repeater>
<Modules>
<Module>
<Name>base</Name>
<Description>Base</Description>
<Class>com.wowza.wms.module.ModuleCore</Class>
</Module>
<Module>
<Name>properties</Name>
<Description>Properties</Description>
<Class>com.wowza.wms.module.ModuleProperties</Class>
</Module>
<Module>
<Name>logging</Name>
<Description>Client Logging</Description>
<Class>com.wowza.wms.module.ModuleClientLogging</Class>
</Module>
<Module>
<Name>flvplayback</Name>
<Description>FLVPlayback</Description>
<Class>com.wowza.wms.module.ModuleFLVPlayback</Class>
</Module>
<Module>
<Name>liverecord</Name>
<Description>StreamRecordModule</Description>
<Class>streamrecord.hourly.monthlyrollover.ModuleStreamRecord</Class>
</Module>
</Modules>
<!-- Properties defined here will be added to the IApplication.getProperties() and IApplicationInstance.getProperties() collections -->
<Properties>
<Property>
<Name>starHour</Name>
<Value>00</Value>
<Type>Integer</Type>
</Property>
<Property>
<Name>endHour</Name>
<Value>24</Value>
<Type>Integer</Type>
</Property>
<Property>
<Name>timezone</Name>
<Value>GMT+03:00</Value>
<Type>String</Type>
</Property>
</Properties>
</Application>
</Root>
i want to record exactly 1 hour then split to new folder but it never happened it records less or more and never exceeded 55 min
I also want to use Media mover module to move the every day stream to new folder
please advice
Thanks