Wowza Community

file name control

OK… So first off, OMG THANK YOU, Im relieved to see this is possible…

Im not sure its working tho :confused:

Heres what I did:

  • Applied the patch, deleted necessary files first, copied new files, restart wowza

  • Copied the /lib folder of the livestreamrecord zip you linked me into the installation lib folder, added module tag to my rtplive Application.xml file. according to the readme file in the zip.

  • Installed Wowza IDE2 on my windows machine (JDK+WowzaIDE2+Wowza2), created a new project put that package name you had in your example and class name you had in your example…

  • Pasted your example code into WowzaIDE2 and it said it built a JAR successfully on my Windows machine…

  • I copied that jar to my linux box into the /lib folder

  • I inserted, into my rtplive Application.xml file, a new Module tag like so:

 <Module> 
  <Name>ModuleStreamRecord</Name> 
  <Description>File Management</Description> 
  <Class>ModuleStreamRecord</Class> 
 </Module>

  • Restarted Wowza

Wowza gives me this error:

ERROR server comment - loadModFunctions: java.lang.ClassNotFoundException: ModuleStreamRecord
java.lang.ClassNotFoundException: ModuleStreamRecord
        at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:323)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:268)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:336)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:186)
        at com.wowza.wms.util.ModuleUtils.addModuleToApp(Unknown Source)
        at com.wowza.wms.util.ApplicationUtils.loadModules(Unknown Source)
        at com.wowza.wms.util.ApplicationUtils.loadConfigFile(Unknown Source)
        at com.wowza.wms.application.ApplicationInstance.loadConfig(Unknown Source)
        at com.wowza.wms.application.ApplicationInstance.<init>(Unknown Source)
        at com.wowza.wms.application.Application.getAppInstance(Unknown Source)
        at com.wowza.wms.vhost.VHost.startStartupStreams(Unknown Source)
        at com.wowza.wms.server.Server.startVHost(Unknown Source)
        at com.wowza.wms.server.Server.startVHosts(Unknown Source)
        at com.wowza.wms.server.Server.start(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at com.wowza.wms.bootstrap.Bootstrap.startServer(Unknown Source)
        at com.wowza.wms.bootstrap.Bootstrap.main(Unknown Source)

Im lost :frowning:

I tried that still doesnt work…

Am I missing a step or putting the jar in the wrong spot?

Do I need to tell it to load the jar or something in other than the Application.xml?

Error:

ERROR server comment - loadModFunctions: java.lang.ClassNotFoundException: streamrecord.hourly.monthlyrollover.ModuleStreamRecord
java.lang.ClassNotFoundException: streamrecord.hourly.monthlyrollover.ModuleStreamRecord
        at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:323)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:268)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:336)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:186)
        at com.wowza.wms.util.ModuleUtils.addModuleToApp(Unknown Source)
        at com.wowza.wms.util.ApplicationUtils.loadModules(Unknown Source)
        at com.wowza.wms.util.ApplicationUtils.loadConfigFile(Unknown Source)
        at com.wowza.wms.application.ApplicationInstance.loadConfig(Unknown Source)
        at com.wowza.wms.application.ApplicationInstance.<init>(Unknown Source)
        at com.wowza.wms.application.Application.getAppInstance(Unknown Source)
        at com.wowza.wms.vhost.VHost.startStartupStreams(Unknown Source)
        at com.wowza.wms.server.Server.startVHost(Unknown Source)
        at com.wowza.wms.server.Server.startVHosts(Unknown Source)
        at com.wowza.wms.server.Server.start(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at com.wowza.wms.bootstrap.Bootstrap.startServer(Unknown Source)
        at com.wowza.wms.bootstrap.Bootstrap.main(Unknown Source)
INFO server comment - ModuleStreamRecord.onAppStart

Application.xml:

 <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>ModuleLiveStreamRecord</Name>
        <Description>ModuleLiveStreamRecord</Description>
        <Class>com.wowza.wms.plugin.livestreamrecord.ModuleLiveStreamRecord</Class>
</Module>
 <Module>
  <Name>ModuleStreamRecord</Name>
  <Description>File Management</Description>
  <Class>streamrecord.hourly.monthlyrollover.ModuleStreamRecord</Class>
 </Module>

Code in IDE:

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<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;
					}
					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) {
		TimeZone tz = TimeZone.getTimeZone(timezone);
		Calendar cal = Calendar.getInstance(tz);
		hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
		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) {
//			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);
			}
		}
	}
}

Structure on left side of IDE:

ModuleStreamRecord
-> src
- -> streamrecord.hourly.monthlyrollover
- - -> ModuleStreamRecord

IDE Output:

Buildfile: C:\Documents and Settings\Administrator\Documents\workspace\ModuleStreamRecord\build.xml
jar:
         [jar] Building jar: C:\Program Files\Wowza Media Systems\Wowza Media Server 2.1.2\lib\ModuleStreamRecord.jar
BUILD SUCCESSFUL

I copied that .jar to the /usr/local/WowzaMediaServer/lib folder on my linux box (the one im running, and is giving this error)

What am I missing??

I seemed to get it LOADING when i recompiled under a diff name BUT

it gives me an error about timezone library:

ERROR server comment - invoke(onAppStart): java.lang.NullPointerException: java.util.TimeZone.parseCustomTimeZone(TimeZone.java:712)
java.lang.NullPointerException
        at java.util.TimeZone.parseCustomTimeZone(TimeZone.java:712)
        at java.util.TimeZone.getTimeZone(TimeZone.java:488)
        at java.util.TimeZone.getTimeZone(TimeZone.java:482)
        at com.wowza.wms.plugin.streamrecord.StreamRecordModule.onAppStart(StreamRecordModule.java:98)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at com.wowza.wms.module.ModuleFunction.invoke(Unknown Source)
        at com.wowza.wms.module.ModuleFunctions.invokeSpecial(Unknown Source)
        at com.wowza.wms.module.ModuleFunctions.onAppStart(Unknown Source)
        at com.wowza.wms.application.Application.getAppInstance(Unknown Source)
        at com.wowza.wms.vhost.VHost.startStartupStreams(Unknown Source)
        at com.wowza.wms.server.Server.startVHost(Unknown Source)
        at com.wowza.wms.server.Server.startVHosts(Unknown Source)
        at com.wowza.wms.server.Server.start(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at com.wowza.wms.bootstrap.Bootstrap.startServer(Unknown Source)
        at com.wowza.wms.bootstrap.Bootstrap.main(Unknown Source)

My application.xml


 <Module>
	<Name>StreamRecordModule</Name>
	<Description>StreamRecordModule</Description>
	<Class>com.wowza.wms.plugin.streamrecord.StreamRecordModule</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-08:00</Value>
			<Type>String</Type>
		</Property>

		</Properties>

As an experiment I tore out all the timezone related calls, and made it just pull from a new Date() object… (I dont care what timezone the files are named for)…

The new JAR executes without error when WMS is started … but the file is still recordered to camera.stream.flv, even when stopping/starting the server it stays as camera.stream.flv

package com.wowza.wms.plugin.streamrecord;

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 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;
import java.text.SimpleDateFormat;

public class StreamRecordModule extends ModuleBase implements IMediaStreamNotify {
	private IApplicationInstance appInstance;
	private StreamTimer streamTimer;
	private int date;
	private int startHour = 0;
	private int endHour = 23;
	private int hourOfDay = -1;
	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;
		}

		@SuppressWarnings("deprecation")
		public void run() {
			while (true) {
				try { 

					Date mydate = new Date();
					int prevHour = hourOfDay;
					hourOfDay = mydate.getHours();
					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();
				}
			}
		}
	}

	@SuppressWarnings("deprecation")
	public void onAppStart(IApplicationInstance appInstance) {
		Date mydate = new Date();
		hourOfDay = mydate.getHours();
		this.appInstance = appInstance;
		WMSProperties props = appInstance.getProperties();
		startHour = props.getPropertyInt("startHour", 0);
		endHour = props.getPropertyInt("endHour", 24);
		
		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()) {
			SimpleDateFormat dateformatYYYYMMDD = new SimpleDateFormat("yyyyMMdd");
			 Date dateNow = new Date ();
			 StringBuilder nowYYYYMMDD = new StringBuilder( dateformatYYYYMMDD.format( dateNow ) );

			String outputPath = appInstance.getStreamStorageDir()+"/"+nowYYYYMMDD; //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) {
//			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);
			}
		}
	}
}

My apologies… It appears to be working just fine! Guess I had to wait for the hour to roll around…

Thank you very very much!

uhhh… does camera.stream.flv (original stream file) just record everything still???

i dont want camera.stream.flv to be 50GB by the end of the month, on top of each hour .mp4 file … essentially requiring double storage

… can it flush that .flv ?

Why are some file like 7mb and some files 700mb ?? audio doesnt record sometimes, sometimes it does… is there additional data being stored? how do i access it?

Thank you for this code! Please help me I can’t get it to work, look below…

I downloaded the Wowza IDE, removed the specified jars from the reference list, compiled a successful build, uploaded it to my ec2 box, created a new application folder called live_int, copied live’s conf file Application.xml into live_int’s conf dir, added in the module so my Application.xml file for live_int looks like this:

<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</StorageDir>
			<KeyDir>${com.wowza.wms.context.VHostConfigHome}/keys</KeyDir>
			<!-- LiveStreamPacketizers (separate with commas): cupertinostreamingpacketizer, smoothstreamingpacketizer, cupertinostreamingrepeater, smoothstreamingrepeater -->
			<LiveStreamPacketizers>smoothstreamingpacketizer</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 -->
		<HTTPStreamers>cupertinostreaming,smoothstreaming</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>
			<!-- 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>
			</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>
		<LiveStreamPacketizer>
			<!-- Properties defined here will override any properties defined in conf/LiveStreamPacketizers.xml for any LiveStreamPacketizers loaded by this applications -->
			<Properties>cupertinostreamingpacketizer, smoothstreamingpacketizer
			</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>ModuleMediaWriterFileMover</Name>
				<Description>ModuleMediaWriterFileMover</Description>
				<Class>com.wowza.wms.module.ModuleMediaWriterFileMover</Class>
			</Module>
			<Module> 
  				<Name>ModuleStreamRecord</Name> 
  				<Description>File Management</Description> 
  				<Class>streamrecord.hourly.monthlyrollover.ModuleStreamRecord</Class> 
 			</Module>
			<Module>
				<Name>ModuleLiveStreamRecord</Name>
				<Description>ModuleLiveStreamRecord</Description>
				<Class>com.wowza.wms.plugin.livestreamrecord.ModuleLiveStreamRecord</Class>
			</Module>
		</Modules>
		<!-- Properties defined here will be added to the IApplication.getProperties() and IApplicationInstance.getProperties() collections -->
		<Properties>
			<Property>
				<Name>fileMoverDestinationPath</Name>
				<Value>/mnt/s3</Value>
			</Property>
			<Property>
				<Name>fileMoverDeleteOriginal</Name>
				<Value>true</Value>
				<Type>Boolean</Type>
			</Property>
			<Property>
				<Name>fileMoverVersionFile</Name>
				<Value>true</Value>
				<Type>Boolean</Type>
			</Property>
			<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-08:00</Value>
				<Type>String</Type>
			</Property>
		</Properties>
	</Application>
</Root>

I point my stream to live_int/myvideo, start recording, everything seems to work fine it records but then when I reach an hour nothing stops recording. It continues to record to the mp4 file just like my live application. What am I doing wrong?

streamrecord.hourly.monthlyrollover.ModuleStreamRecord is an extension of com.wowza.wms.plugin.livestreamrecord.ModuleLiveStreamRecord so you do not need both. Remove com.wowza.wms.plugin.livestreamrecord.ModuleLiveStreamRecord and see what happens.

It is currently set to only work with mediacaster streams that are started using *.stream files to resolve the name for recording.

Also, as Richard mentions, when you are doing manual recording as in this case, you should set the stream type to live and not live-record. live-record will automatically record the stream but won’t roll over.

I see, thank you for the clarifications, my streams are currently sdp files, I will test to see if this works.

It doesn’t, logger says it gets as far as onMediaStreamCreate, startRecording never runs and as such recordStream never gets called when stream starts.

Hello,

Thank you for this superb coding, its working on first attempt :slight_smile:

You guys simply rock !

I have the following questions:

  • Right now it only record .stream files. How can i record any live stream coming from flash encoder in RTMP format through stream name

  • Rightnow we are using a DOS batch process to move files in userwise directory matching the mp4 file name Is there any process similar that i can put in application.xml or i can compile in the code which move files accordingly to userwise directory.

-Mamoor

Hello Richard,

In RTMP it shows that Module wakeup but when stream connect but when i press start on FMLE it dont show its recording, neither the files are created

2011-05-20 16:50:12 GMT+04:00 comment server INFO 200 - ModuleStreamRecord.onAppStart - - - 21.454 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 16:50:12 GMT+04:00 app-start application INFO 200 definst 00E801010EF1/definst - - - 21.454 - - - - - - - - - - - - - - - - - - - - - - - - -

On the other hand when ts.stream is started then the recording starts, see log below:

2011-05-20 17:44:10 GMT+04:00 comment server INFO 200 - RTPMediaCaster.Reconnector[6503761:live/definst:ts.stream]: start: 1 - - - 19.078 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 17:44:11 GMT+04:00 comment server INFO 200 - RTPSessionDescriptionDataProviderBasic.getStreamInfo[live/definst]: URI: udp://0.0.0.0:10000 - - - 19.172 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 17:44:11 GMT+04:00 create stream INFO 200 - - defaultVHost live definst 0.015 - 80 null 127.0.0.1 rtsp - known 1253710198 0 0 1 0 0 0 ts.stream - - - - - null null - null -

2011-05-20 17:44:11 GMT+04:00 publish stream INFO 200 ts.stream - defaultVHost live definst 0.031 - 80 null 127.0.0.1 rtsp - known 1253710198 0 0 1 0 0 0 ts.stream - - - - - null null - null -

2011-05-20 17:44:11 GMT+04:00 comment server INFO 200 - ModuleStreamRecord.startRecording: stream:ts.stream format:mp4 append:true outputPath:C:\Program Files\Wowza Media Systems\Wowza Media Server 2.2.3/content/20\ts_08.mp4 versionFile:false startOnKeyFrame:true recordData:true - - - 19.235 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 17:44:11 GMT+04:00 comment server INFO 200 - RTPUDPTransport.bind[live/definst]: /0.0.0.0:10000 - - - 19.266 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 17:44:11 GMT+04:00 comment server INFO 200 - RTPMediaCaster.Reconnector[6503761:live/definst:ts.stream]: done: 1 - - - 19.281 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 17:44:11 GMT+04:00 comment server INFO 200 - UDPTransport.firstPacket: /0.0.0.0:10000 - - - 19.313 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 17:44:11 GMT+04:00 comment server INFO 200 - HTTPStreamManager.onHTTPRequest: Publish stream successfully started [live/definst]: flv:ts.stream - - - 19.797 - - - - - - - - - - - - - - - - - - - - - - - - -

Looks like ModuleStreamRecord.startRecording is not working on stream other then with .stream files.

If its invoking for RTMP than it should record also. I wonder what is the problem.

Please help !

Also as Richard suggested i create a test.stream file with RTMP link:

rtmp://97.74.80.208:1935/live/test.sdp

Now on Flash encoder:

FMS URL: rtmp://rtmp://97.74.80.208:1935/live

Stream: test.sdp

Than on Stream Manager i started a stream name test.stream.

Still the video is not recorded.

I am wondering now my RTMP stream can be recorded with this module.

Please help !

Richard,

I changed the link to test.sdp only in test.stream file and push again with flash encoder. And started stream with StreamManager.

The stream started successfully, below are the logs but still no recording started:

2011-05-20 20:27:21 GMT+04:00 comment server INFO 200 - RTPMediaCaster.Reconnector[20313166:test/definst:test.stream]: start: 1 - - - 96.375 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 20:27:21 GMT+04:00 comment server INFO 200 - RTPSessionDescriptionDataProviderBasic.getStreamInfo[test/definst]: C:/Program Files/Wowza Media Systems/Wowza Media Server 2.2.3/content/test.sdp - - - 96.485 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 20:27:21 GMT+04:00 comment server WARN 200 - RTPSessionDescriptionDataProviderBasic.getStreamInfo: SDP file missing: C:/Program Files/Wowza Media Systems/Wowza Media Server 2.2.3/content/test.sdp - - - 96.485 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 20:27:22 GMT+04:00 comment server INFO 200 - HTTPStreamManager.onHTTPRequest: Publish stream successfully started [test/definst]: flv:test.stream - - - 97.313 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 20:27:25 GMT+04:00 app-stop application INFO 200 definst live/definst - - - 101.032 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 20:27:25 GMT+04:00 comment server INFO 200 - ModuleStreamRecord.onAppStop - - - 101.032 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 20:27:33 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSmoothStreaming.addFragment[test/definst/test.sdp]: Add chunk: type:video id:3 count:150 duration:12495 - - - 108.328 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 20:27:33 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSanJose.endChunkTS[test/definst/test.sdp]: Add chunk: id:6 a/v/k:270/151/1 duration:12496 - - - 108.469 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 20:27:33 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerCupertino.endChunkTS[test/definst/test.sdp]: Add chunk: id:6 a/v/k:90/150/1 duration:12496 - - - 108.719 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 20:27:43 GMT+04:00 comment server INFO 200 - ModuleStreamRecord.onAppStart - - - 119.094 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 20:27:43 GMT+04:00 app-start application INFO 200 definst live/definst - - - 119.11 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 20:27:43 GMT+04:00 connect-pending session INFO 100 182.177.107.143 - defaultVHost live definst 0.797 [any] 1935 rtmp://97.74.80.208/live 182.177.107.143 rtmp http://www.hamariweb.com/pakistan-tv-channels/player.swf WIN 10,3,181,14 923086104 3451 3073 - - - - - - - - - - - - - rtmp://97.74.80.208/live -

2011-05-20 20:27:43 GMT+04:00 connect session INFO 200 182.177.107.143 - defaultVHost live definst 0.797 [any] 1935 rtmp://97.74.80.208/live 182.177.107.143 rtmp http://www.hamariweb.com/pakistan-tv-channels/player.swf WIN 10,3,181,14 923086104 3451 3073 - - - - - - - - - - - - - rtmp://97.74.80.208/live -

Still looking for a resolution on this matter.

-Mamoor

Richard,

I changed the mystream.stream to mystream.play

Inside file is mystream

On flash encoder:

Stream name: mystream

Started the stream from encoder and started the stream from StreamManager > live/mystream.play

But still no luck on recording :frowning:

Below are the logs:


2011-05-20 22:01:53 GMT+04:00 comment server INFO 200 - ModuleStreamRecord.onAppStart - - - 4.75 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:01:53 GMT+04:00 app-start application INFO 200 definst live/definst - - - 4.797 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:01:53 GMT+04:00 connect-pending session INFO 100 2.51.134.125 - defaultVHost live definst 0.875 [any] 1935 rtmp://97.74.80.208/live 2.51.134.125 rtmp http://www.hamariweb.com/pakistan-tv-channels/player.swf WIN 10,3,180,65 1734474552 3451 3073 - - - - - - - - - - - - - rtmp://97.74.80.208/live -

2011-05-20 22:01:53 GMT+04:00 connect session INFO 200 2.51.134.125 - defaultVHost live definst 0.875 [any] 1935 rtmp://97.74.80.208/live 2.51.134.125 rtmp http://www.hamariweb.com/pakistan-tv-channels/player.swf WIN 10,3,180,65 1734474552 3451 3073 - - - - - - - - - - - - - rtmp://97.74.80.208/live -

2011-05-20 22:01:54 GMT+04:00 create stream INFO 200 - - defaultVHost live definst 0.016 [any] 1935 rtmp://97.74.80.208/live 2.51.134.125 rtmp http://www.hamariweb.com/pakistan-tv-channels/player.swf WIN 10,3,180,65 1734474552 3522 3411 1 0 0 0 - - - - - - rtmp://97.74.80.208/live rtmp://97.74.80.208/live - rtmp://97.74.80.208/live -

2011-05-20 22:01:54 GMT+04:00 destroy stream INFO 200 ary_low - defaultVHost live definst 0.641 [any] 1935 rtmp://97.74.80.208/live 2.51.134.125 rtmp http://www.hamariweb.com/pakistan-tv-channels/player.swf WIN 10,3,180,65 1734474552 3572 3637 1 - 0 0 ary_low - - - - - rtmp://97.74.80.208/live/ary_low rtmp://97.74.80.208/live/ary_low - rtmp://97.74.80.208/live -

2011-05-20 22:01:54 GMT+04:00 disconnect session INFO 200 1734474552 - defaultVHost live definst 1.875 [any] 1935 rtmp://97.74.80.208/live 2.51.134.125 rtmp http://www.hamariweb.com/pakistan-tv-channels/player.swf WIN 10,3,180,65 1734474552 3572 3637 - - - - - - - - - - - - - rtmp://97.74.80.208/live -

2011-05-20 22:01:59 GMT+04:00 connect-pending session INFO 100 58.181.108.19 - defaultVHost live definst 0.813 [any] 1935 rtmp://97.74.80.208/live 58.181.108.19 rtmp rtmp://97.74.80.208/live FMLE/3.0 (compatible; FMSc/1.0) 766775432 3253 3073 - - - - - - - - - - - - - rtmp://97.74.80.208/live -

2011-05-20 22:01:59 GMT+04:00 connect session INFO 200 58.181.108.19 - defaultVHost live definst 0.813 [any] 1935 rtmp://97.74.80.208/live 58.181.108.19 rtmp rtmp://97.74.80.208/live FMLE/3.0 (compatible; FMSc/1.0) 766775432 3253 3073 - - - - - - - - - - - - - rtmp://97.74.80.208/live -

2011-05-20 22:02:04 GMT+04:00 create stream INFO 200 - - defaultVHost live definst 0.0 [any] 1935 rtmp://97.74.80.208/live 58.181.108.19 rtmp rtmp://97.74.80.208/live FMLE/3.0 (compatible; FMSc/1.0) 766775432 3400 3563 1 0 0 0 - - - - - - rtmp://97.74.80.208/live rtmp://97.74.80.208/live - rtmp://97.74.80.208/live -

2011-05-20 22:02:04 GMT+04:00 publish stream INFO 200 mystream - defaultVHost live definst 0.39 [any] 1935 rtmp://97.74.80.208/live 58.181.108.19 rtmp rtmp://97.74.80.208/live FMLE/3.0 (compatible; FMSc/1.0) 766775432 3493 3604 1 0 0 0 mystream videoKeyframeFrequency=5&totalDatarate=132 - - - - rtmp://97.74.80.208/live/mystream?videoKeyframeFrequency=5&totalDatarate=132 rtmp://97.74.80.208/live/mystream videoKeyframeFrequency=5&totalDatarate=132 rtmp://97.74.80.208/live -

2011-05-20 22:02:06 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSanJose.init[live/definst/mystream]: chunkDurationTarget: 10000 - - - 17.953 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:06 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSanJose.init[live/definst/mystream]: chunkDurationTolerance: 500 - - - 17.953 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:06 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSanJose.init[live/definst/mystream]: playlistChunkCount:4 - - - 17.953 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:06 GMT+04:00 comment server INFO 200 - MediaStreamMap.getLiveStreamPacketizer: Create live stream packetizer: sanjosestreamingpacketizer:mystream - - - 17.953 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:06 GMT+04:00 comment server INFO 200 - SanJosePacketHandler.startStream[live/definst/mystream] - - - 17.953 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:06 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSanJose.handlePacket: Audio codec: AAC - - - 17.953 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:06 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerCupertino.init[live/definst/mystream]: chunkDurationTarget: 10000 - - - 17.984 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:06 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerCupertino.init[live/definst/mystream]: chunkDurationTolerance: 500 - - - 17.984 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:06 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerCupertino.init[live/definst/mystream]: audioGroupCount: 3 - - - 17.984 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:06 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerCupertino.init[live/definst/mystream]: playlistChunkCount:3 - - - 17.984 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:06 GMT+04:00 comment server INFO 200 - MediaStreamMap.getLiveStreamPacketizer: Create live stream packetizer: cupertinostreamingpacketizer:mystream - - - 17.984 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:06 GMT+04:00 comment server INFO 200 - CupertinoPacketHandler.startStream[live/definst/mystream] - - - 18.0 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:07 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerCupertino.handlePacket[live/definst/mystream]: Audio codec:AAC isCompatible:true - - - 18.0 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:07 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerCupertino.handlePacket[live/definst/mystream][mp4a.40.2]: AAC Audio info: {AACFrame: size: 0, rate: 22050, channels: 1, samples: 1024, errorBitsAbsent: true, profileObjectType: “LC”} - - - 18.0 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:07 GMT+04:00 comment server INFO 200 - MediaStreamMap.getLiveStreamPacketizer: Create live stream packetizer: smoothstreamingpacketizer:mystream - - - 18.015 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:07 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSmoothStreaming.startStream[live/definst/mystream] - - - 18.015 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:07 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSanJose.handlePacket: Video codec: H264 - - - 18.406 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:07 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerCupertino.handlePacket[live/definst/mystream]: Video codec:H264 isCompatible:true - - - 18.406 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:07 GMT+04:00 comment server WARN 200 - LiveStreamPacketizerCupertino.handlePacket[live/definst/mystream][avc1.77.30]: H.264 Video encoding settings are beyond iPhone/iPod touch recommendations (Baseline/3.0): {H264CodecConfigInfo: profile: “Main”, level: 3.0, frameSize: 640x480, displaySize: 640x480, PAR: 1:1} - - - 18.422 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:12 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSmoothStreaming.flushPendingAudio: Bitrate[live/definst/mystream]: 32016 - - - 23.547 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:12 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSmoothStreaming.addFragment[live/definst/mystream]: Add chunk: type:audio id:0 count:44 duration:2035 - - - 23.562 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:14 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSmoothStreaming.addFragment[live/definst/mystream]: Add chunk: type:audio id:1 count:44 duration:2044 - - - 25.531 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:16 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSmoothStreaming.addFragment[live/definst/mystream]: Add chunk: type:audio id:2 count:44 duration:2045 - - - 27.515 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:18 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSmoothStreaming.addFragment[live/definst/mystream]: Add chunk: type:audio id:3 count:44 duration:2042 - - - 29.703 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:20 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSanJose.endChunkTS[live/definst/mystream]: Add chunk: id:1 a/v/k:295/151/1 duration:13636 - - - 31.281 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:20 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerCupertino.endChunkTS[live/definst/mystream]: Add chunk: id:1 a/v/k:98/150/1 duration:13636 - - - 31.281 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:20 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSmoothStreaming.addFragment[live/definst/mystream]: Add chunk: type:audio id:4 count:44 duration:2043 - - - 31.906 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:22 GMT+04:00 comment server INFO 200 - RTPMediaCaster.create[14113863] - - - 33.562 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:22 GMT+04:00 comment server INFO 200 - RTPMediaCaster.init[14113863] - - - 33.562 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:22 GMT+04:00 comment server INFO 200 - RTPMediaCaster.Reconnector[14113863:live/definst:mystream]: start: 1 - - - 33.562 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:22 GMT+04:00 comment server INFO 200 - RTPSessionDescriptionDataProviderBasic.getStreamInfo[live/definst]: C:/Program Files/Wowza Media Systems/Wowza Media Server 2.2.3/content/mystream - - - 33.672 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:22 GMT+04:00 comment server WARN 200 - RTPSessionDescriptionDataProviderBasic.getStreamInfo: SDP file missing: C:/Program Files/Wowza Media Systems/Wowza Media Server 2.2.3/content/mystream - - - 33.672 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:22 GMT+04:00 comment server INFO 200 - LiveStreamPacketizerSmoothStreaming.addFragment[live/definst/mystream]: Add chunk: type:audio id:5 count:44 duration:2044 - - - 33.922 - - - - - - - - - - - - - - - - - - - - - - - - -

2011-05-20 22:02:23 GMT+04:00 comment server INFO 200 - HTTPStreamManager.onHTTPRequest: Publish stream successfully started [live/definst]: flv:mystream - - - 34.015 - - - - - - - - - - - - - - - - - - - - - - - - -


Please help how can i record an rtmp stream.

-Mamoor

Hello,

I’m not able to get this custom module working. My environment seems simple which is running Wowza developer edition locally and using FMLE to live stream. It looks like the Application.xml is setup right and I can breakpoint the code in Wowza IDE. The issue is this:

streams = appInstance.getMediaCasterStreams().getMediaCasterNames();
[B]and also this:[/B] 
		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);
			}
		}

In my simple environment, there are no “Media Caster Streams” and therefore the above List is empty and the code to record never gets executed. Can you please tell me what are “Media Caster Streams” and how do I set up FMLE to be recognized as a “Media Caster Stream” so I can take advantage of this custom module.

Thanks much,

Greg

The answer to this is that for non MediaCasterStreams such as FMLE and others, we need to obtain the streams differently than the code above like this:

Liststreams = appInstance.getStreams().getPublishStreamNames();

If you modify the above code to use this, everything else seems to work well. Haven’t full tested but it does record the file to the /content folder with a unique filename. I’ll post back if it stops/starts on the hour as advertised.

Greg

Is there a way to get this module without compiling? I have never done that before and don’t want to break my production box.

My 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

/mnt/s3

fileMoverDeleteOriginal

true

Boolean

starHour

00

Integer

endHour

24

Integer

timezone

GMT-08:00

String

///////////////////////////////////////////////////////////////////////////////////////////

My Wowza IDE:

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.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) {

// 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 mediaCasters = appInstance.getMediaCasterStreams().getMediaCasterNames();

if (mediaCasters.contains(streamName)) {

int lockCount = appInstance.getMediaCasterStreams().getMediaCaster(streamName).getLockCount();

if (lockCount > 0)

startRecording(stream);

}

}

}

}

Please Help

Thank you for your help!!!wonderful tech support!!!